温故篇之素数

Posted 寻找星空的孩子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了温故篇之素数相关的知识,希望对你有一定的参考价值。

太久没有写c++代码了,原来工作以后会是这个样子,但是我依然喜欢c++,这次打算花一个月是时间,温故下代码。

今天就从简单的开始写吧。。。哈哈~水了。


温故篇之素数


素数大家都不陌生,定义:只能被本身和1整除的数,也叫质数。


判定一个数是否为素数很简单,方式也很多,单单判定一个正整数N(1<N<100000000)是否为素数

可以写个function ,eg:

bool JudgePrime(int x)

    if(x<2) return false;
    if(x==2) return true;
    else if((x)%2==0) return false;
    for(int i=3;i*i<=(x);i+=2)
    
        if((x)%i==0) return false;
    
    return true;
当然,上面的函数还可以进一步优化;


但是当我们要重复使用某个N,并判定N是否是素数,且N(1<N<100000)时,我们可以考虑【打表】也就是说可以做一张质数表,打表时候可以根据题意的需求,做适当的调整。下面是常见的素数打表

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
#define MAXN 10001

int prime[MAXN];
int mark[MAXN];
int Prime()

    int index = 0;
    memset(mark,1,sizeof(mark));//全标记为质数
    for(int i=2;i<MAXN;i++)
    
        if(mark[i]) prime[index++] = i;
        for(int j=0;j<index && prime[j]*i<MAXN;j++)
        
            mark[i*prime[j]] = 0;//合数
            if (i%prime[j]==0) break;//最小质因数
        
    
    return index;

这里简单说明,首先mark[]是一个标记数组可以用bool型代替,prime[]是存储的质数的;

对[2-MAXN)之间的素数打表,关键在于降低时间复杂度,找到质数后,标记,并在素数表里与其他已得到素数相乘,

标记后面的合数;当i为合数时候,仅标记到它与它最小质因数乘积得到的合数,防止重复标记;

eg: i=2时候,会标记4是合数;

i=3时候,会标记6,9是合数;

当  i=4时候,因为是合数,所以只标记到8,而不会标记到12

当  i=5时候,会标记10,15,25,是合数;

i=6时候,因为是合数,所以只标记到12,而不会标记18,30;;;因为合数18和30斗会有其他已标记合数来标记的。。。这样就不会重复标记了。


---------------------------------------------------------------------------------------------------------------------------------------------------------


代码还是要多写,多实践!

那么来练习一下吧...




链接:1262 寻找素数对


寻找素数对

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 13847    Accepted Submission(s): 6997


Problem Description 哥德巴赫猜想大家都知道一点吧.我们现在不是想证明这个结论,而是想在程序语言内部能够表示的数集中,任意取出一个偶数,来寻找两个素数,使得其和等于该偶数.
做好了这件实事,就能说明这个猜想是成立的.
由于可以有不同的素数对来表示同一个偶数,所以专门要求所寻找的素数对是两个值最相近的.
 
Input 输入中是一些偶整数M(5<M<=10000).
 
Output 对于每个偶数,输出两个彼此最接近的素数,其和等于该偶数.
 
Sample Input
  
   20 30 40
  
 
Sample Output
  
   7 13
13 17
17 23
  
 
Source 浙江工业大学第四届大学生程序设计竞赛


code:

/**
http://acm.hdu.edu.cn/showproblem.php?pid=1262
寻找素数对
对于每个偶数,输出两个彼此最接近的素数,其和等于该偶数.

**/

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
#define MAXN 10001

int prime[MAXN];
int mark[MAXN];
int Prime()

    int index = 0;
    memset(mark,1,sizeof(mark));//全标记为质数
    for(int i=2;i<MAXN;i++)
    
        if(mark[i]) prime[index++] = i;
        for(int j=0;j<index && prime[j]*i<MAXN;j++)
        
            mark[i*prime[j]] = 0;//合数
            if (i%prime[j]==0) break;//最小质因数
        
    
    return index;

int main()

    int index = Prime();
    int even;//(5<even<=10000).
    while(scanf("%d",&even)!=EOF)
    
        int pos=0;
        if(even%2) continue;
        for(int i=1;prime[i]*2<=even;i++)//这里应该是可以由两个相同的素数的和 eg:6 = 3+3
        
            if(mark[even-prime[i]]) pos=i;
        
        if (pos) printf("%d %d\\n",prime[pos],even-prime[pos]);
    
    return 0;


链接: 2012 素数判定


素数判定

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 139444    Accepted Submission(s): 49248


Problem Description 对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括x,y)(-39<=x<y<=50),判定该表达式的值是否都为素数。  
Input 输入数据有多组,每组占一行,由两个整数x,y组成,当x=0,y=0时,表示输入结束,该行不做处理。  
Output 对于每个给定范围内的取值,如果表达式的值都为素数,则输出"OK",否则请输出“Sorry”,每组输出占一行。
 
Sample Input
  
   0 1
0 0
  
 
Sample Output
  
   OK
  
 
Author lcy  
Source C语言程序设计练习(二)


Code:


/**
http://acm.hdu.edu.cn/showproblem.php?pid=2012
素数判定
对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括x,y)(-39<=x<y<=50),判定该表达式的值是否都为素数。

**/
//maxNum = 50^2+50+41 = 2591 ;minNum = 41
/***
[-39,50] 打表得
1523 ,1447 ,1373 ,1301 ,1231 ,1163 ,1097 ,1033 ,971 ,911 ,853 ,797 ,743 ,691 ,641 ,593 ,547 ,503
,461 ,421 ,383 ,347 ,313 ,281 ,251 ,223 ,197 ,173 ,151 ,131 ,113 ,97 ,83 ,71 ,61 ,53 ,47 ,43 ,41
-------------------------------------------------------------------------------------------------所以有效[-39,-1]=[0,38],[39,50]
,41 ,43 ,47 ,53 ,61 ,71 ,83 ,97 ,113 ,131 ,151 ,173 ,197 ,223 ,251 ,281 ,313 ,347 ,383 ,421 ,461
,503 ,547 ,593 ,641 ,691 ,743 ,797 ,853 ,911 ,971 ,1033 ,1097 ,1163 ,1231 ,1301 ,1373 ,1447 ,1523
,1601 ,1681 ,1763 ,1847 ,1933 ,2021 ,2111 ,2203 ,2297 ,2393 ,2491 ,2591 ,
----------------------------------------------------------------------------
发现:仅40(1681),41(1763),44(2021),49(2491) 非素数
*/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
#define N 51
bool prime[N];
bool JudgePrime(int x)

    if(x<2) return false;
    if(x==2) return true;
    else if(x%2==0) return false;
    for(int i=3;i*i<=(x);i+=2)
    
        if((x)%i==0) return false;
    
    return true;

void Init()

    //memset(prime,fasle,sizeof(prime));
    for(int i=0;i<N;i++)
    
        int ans = i*i + i + 41;
        prime[i] = JudgePrime(ans);
    

int main()

    //Init();
    int NotPrime[4]=40,41,44,49;
    int x,y;
    while(scanf("%d%d",&x,&y)!=EOF)
    
        int L=-1,R=-1;
        if(x==0 and y==0) return 0;
        if(x>=y) continue;
        if(y<=39) printf("OK\\n");
        else
        

            bool bo = true;
            for(int i=0;i<4;i++)
            
                if(NotPrime[i]>=x && NotPrime[i]<=y)bo = false;break;
            
            if(bo) printf("OK\\n");
            else printf("Sorry\\n");

        

    
    return 0;

本题通过打表发现仅40(1681),41(1763),44(2021),49(2491)是非素数,那么题目就简单了。以上代码是解题思路,当然本体数据不大,每个集合内算一遍也应该可以过。(仅提供参考)



链接: 4548 美素数


美素数

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 6823    Accepted Submission(s): 2409


Problem Description   小明对数的研究比较热爱,一谈到数,脑子里就涌现出好多数的问题,今天,小明想考考你对素数的认识。
  问题是这样的:一个十进制数,如果是素数,而且它的各位数字和也是素数,则称之为“美素数”,如29,本身是素数,而且2+9 = 11也是素数,所以它是美素数。
  给定一个区间,你能计算出这个区间内有多少个美素数吗?  
Input 第一行输入一个正整数T,表示总共有T组数据(T <= 10000)。
接下来共T行,每行输入两个整数L,R(1<= L <= R <= 1000000),表示区间的左值和右值。  
Output 对于每组数据,先输出Case数,然后输出区间内美素数的个数(包括端点值L,R)。
每组数据占一行,具体输出格式参见样例。  
Sample Input
  
   3
1 100
2 2
3 19
  
 
Sample Output
  
   Case #1: 14
Case #2: 1
Case #3: 4
  
 
Source 2013金山西山居创意游戏程序挑战赛——初赛(2)


本题稍微拓展下。

Code:

/**
http://acm.hdu.edu.cn/showproblem.php?pid=4548
美素数
问题是这样的:一个十进制数,如果是素数,而且它的各位数字和也是素数,则称之为“美素数”,
如29,本身是素数,而且2+9 = 11也是素数,所以它是美素数。
给定一个区间,你能计算出这个区间内有多少个美素数吗?

**/
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define MAXN 1000001

int prime[MAXN];
int mark[MAXN];
int beautifulPrime[MAXN];
int beautifulMark[MAXN];
void Prime()

    int index = 0;
    memset(mark,1,sizeof(mark));
    memset(beautifulMark,0,sizeof(beautifulMark));
    beautifulPrime[1] = 0;
    mark[1] = 0;
    for(int i=2;i<MAXN;i++)
    
        if(mark[i])
        
            prime[index++] = i;
            //美素数
            int ans = i;
            int sum = 0;
            while(ans)
            
                sum += ans % 10;
                ans /= 10;
            
            if(mark[sum]) beautifulPrime[i] = beautifulPrime[i-1]+1; beautifulMark[i] = 1;
            else beautifulPrime[i] = beautifulPrime[i-1];
        
        else
        
            beautifulPrime[i] = beautifulPrime[i-1];
        
        for(int j=0;j<index && prime[j]*i<MAXN;j++)
        
            mark[prime[j]*i] = 0;
            if(i%prime[j]==0) break;
        
    

int main()

    int index = 1;
    Prime();
    int T;
    scanf("%d",&T);
    while(index<=T)
    
        int L,R;
        scanf("%d%d",&L,&R);
        if(R<L) printf("Case #%d: 0\\n",index++);continue;
        int sum = beautifulPrime[R] - beautifulPrime[L];
        if (beautifulMark[L]) sum++;//左区间是美素数,需要包含; [13,19]=0
        printf("Case #%d: %d\\n",index++,sum);
    
    return 0;


链接: 1431 素数回文


素数回文

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 19576    Accepted Submission(s): 4635


Problem Description xiaoou33对既是素数又是回文的数特别感兴趣。比如说151既是素数又是个回文。现在xiaoou333想要你帮助他找出某个范围内的素数回文数,请你写个程序找出 a 跟b 之间满足条件的数。(5 <= a < b <= 100,000,000); 
 
Input 这里有许多组数据,每组包括两组数据a跟b。  
Output 对每一组数据,按从小到大输出a,b之间所有满足条件的素数回文数(包括a跟b)每组数据之后空一行。  
Sample Input
  
   5 500
  
 
Sample Output
  
   5
7
11
101
131
151
181
191
313
353
373
383
  
 

本题范围太大了,不适合打表,那么就要从回文入手了


Code:


/**
http://acm.hdu.edu.cn/showproblem.php?pid=1431
素数回文

**/
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define N 5

bool JudgePrime(int x)

    if(x<2) return false;
    if(x==2) return true;
    else if(x%2==0) return false;
    for(int i=3;i*i<=x;i+=2)
    
        if(x%i==0) return false;
    
    return true;

//其中 能被11整数的数,奇数位的和与偶数位的和的差为0或者11的倍数,所以四位及以上的偶数位数不考虑
void solve(int &L,int &R)

    int prime[]=2,3,5,7,11;
    if(L<=11)
    
        for(int i=0;i<N;i++)
        
            if(prime[i]<L) continue;
            if(prime[i]>R) return;
            printf("%d\\n",prime[i]);
        
    
    if(R>100)//三位数
    
        for(int i=1;i<=9;i+=2)//个,百
        
            for(int j=0;j<=9;j++)//十
            
                int ans = i*101+j*10;
                if(ans<L) continue;
                if(ans>R) return;
                if(JudgePrime(ans)) printf("%d\\n",ans);
            
        
    
    if(R>10000)//五位数
    
        for(int i=1;i<=9;i+=2)//个,万
        
            for(int j=0;j<=9;j++)//十,千
            
                for(int k=0;k<=9;k++)//百
                
                    int ans = i*10001+j*1010+k*100;
                    if(ans<L) continue;
                    if(ans>R) return;
                    if(JudgePrime(ans)) printf("%d\\n",ans);
                
            
        
    
    if(R>1000000)//七位数
    
        for(int i=1;i<=9;i+=2)//个,百万
        
            for(int j=0;j<=9;j++)//十,十万
            
                for(int k=0;k<=9;k++)//百,万
                
                    for(int m=0;m<=9;m++)//千
                    
                        int ans = i*1000001+j*100010+k*10100+m*1000;
                        if(ans<L) continue;
                        if(ans>R) return;
                        if(JudgePrime(ans)) printf("%d\\n",ans);
                    
                
            
        
    
    if(R>100000000)//九位数
    
        for(int i=1;i<=9;i+=2)//个,亿
        
            for(int j=0;j<=9;j++)//十,千万
            
                for(int k=0;k<=9;k++)//百,百万
                
                    for(int m=0;m<=9;m++)//千,十万
                    
                        for(int n=0;n<=9;n++)//万
                        
                            int ans = i*100000001+j*10000010+k*1000100+m*101000+n*10000;
                            if(ans<L) continue;
                            if(ans>R) return;
                            if(JudgePrime(ans)) printf("%d\\n",ans);
                        
                    
                
            
        
    

int main()

    int L,R;
    while(scanf("%d%d",&L,&R)!=EOF)
    
        if(L>R) continue;
        solve(L,R);
        printf("\\n");
    
    return 0;


关键是仔细,还有就是11倍数的特征;



链接: 2098 拆分素数和


分拆素数和

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 39561    Accepted Submission(s): 17323


Problem Description 把一个偶数拆成两个不同素数的和,有几种拆法呢?  
Input 输入包含一些正的偶数,其值不会超过10000,个数不会超过500,若遇0,则结束。  
Output 对应每个偶数,输出其拆成不同素数的个数,每个结果占一行。  
Sample Input
  
   30
26
0
  
 
Sample Output
  
   3
2
  
 
Source 2007省赛集训队练习赛(2)


Code:

/**
http://acm.hdu.edu.cn/showproblem.php?pid=2098
分拆素数和

把一个偶数拆成两个不同素数的和,有几种拆法呢?

**/
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
#define MAXN 10001

int prime[MAXN];
int mark[MAXN];
int Prime()

    int index = 0;
    memset(mark,1,sizeof(mark));//全标记为质数
    for(int i=2;i<MAXN;i++)
    
        if(mark[i]) prime[index++] = i;
        for(int j=0;j<index && prime[j]*i<MAXN;j++)
        
            mark[i*prime[j]] = 0;//合数
            if (i%prime[j]==0) break;//最小质因数
        
    
    return index;

int main()

    int index = Prime();
    int n;
    while(scanf("%d",&n) && n)
    
        if (n%2) continue;
        int cnt = 0;
        for(int i=0;prime[i]<n/2;i++)
        
            if(mark[n-prime[i]]) cnt++;
        
        printf("%d\\n",cnt);
    
    return 0;


链接: 2521 反素数


反素数

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6694    Accepted Submission(s): 4008


Problem Description 反素数就是满足对于任意i(0<i<x),都有g(i)<g(x),(g(x)是x的因子个数),则x为一个反素数。现在给你一个整数区间[a,b],请你求出该区间的x使g(x)最大。
 
Input 第一行输入n,接下来n行测试数据
输入包括a,b, 1<=a<=b<=5000,表示闭区间[a,b].
 
Output 输出为一个整数,为该区间因子最多的数.如果满足条件有多个,则输出其中最小的数.
 
Sample Input
  
   3
2 3
1 10
47 359
  
 
Sample Output
  
   2
6
240

   
    
     Hint
    
2的因子为:1 2
10的因子为:1 2 5 10

   
    
  
 
Source HDU 2008-10 Programming Contest  

正如题目意思说的,其实就是求因子个数,特别说明不是质因子。

但是这个方式是可以得到区间[]内的素数的;


Code:

/***
http://acm.hdu.edu.cn/showproblem.php?pid=2521
反素数

Problem Description
反素数就是满足对于任意i(0<i<x),都有g(i)<g(x),(g(x)是x的因子个数),则x为一个反素数。
现在给你一个整数区间[a,b],请你求出该区间的x使g(x)最大。

//解析一下就是求区间内最小的x满足其因子数最多(注意不是质因子)
*/

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
#define MAXN 5001

int prime[MAXN];
int mark[MAXN];
int PrimeFactorCnt[MAXN];
void Prime()

    int index = 0;
    memset(mark,1,sizeof(mark));//全标记为质数
    memset(PrimeFactorCnt,0,sizeof(PrimeFactorCnt));//1 错了?

    for(int i=2;i<MAXN;i++)
    
        PrimeFactorCnt[i]++;//因子: 1
        if(mark[i])
        
            prime[index++] = i;
        
        for(int k=i;k<MAXN;k += i)
        
            PrimeFactorCnt[k]++;
            mark[k] = 0;//合数
        
        //printf("...PrimeFactorCnt[%d]=%d\\n",i,PrimeFactorCnt[i]);
    

int main()

    Prime();
    int T,L,R;
    scanf("%d",&T);
    while(T--)
    
        scanf("%d%d",&L,&R);
        if(L>R) continue;
        int pos = 1;
        for(int i=L;i<=R;i++)
        
            if(PrimeFactorCnt[pos]<PrimeFactorCnt[i]) pos = i;
        
        //printf("PrimeFactorCnt[%d]=%d\\n",pos,PrimeFactorCnt[pos]);
        printf("%d\\n",pos);
    
    return 0;


太久没在oj上写题了,最近项目不忙,打算来温故一番,不喜勿喷,哈哈。。。




[温故而知新吧,现在发现当初刚参加acm时候代码写得好chuo~,真是不假思索啊]

以上是关于温故篇之素数的主要内容,如果未能解决你的问题,请参考以下文章

温故篇之STL_map,set的一些应用

温故篇之STL_map,set的一些应用

素数筛(筛选出一定范围内的所有素数)

WPF效果第一百九十七篇之Path范围内拖拽

c语言 指定范围内的回文素数,题目如下,要求1S内完成的

计算给定范围内的半素数 [a..b]