素数

来源:互联网 发布:嵌入式linux开发流程 编辑:程序博客网 时间:2024/06/04 19:13

单个素数判断

对于一个数判断是不是素数可以直接从2-sqrt(n)判断能不能被n整除,若能整除则不是素数。

bool if_prime(int n){    if(n==1)         return false;    for(int i=2;i<=(int)sqrt(n);i++)    {        if(n%i==0)           return false;    }    return true;}

判断一个数是不是素数时这样还可以,但需要判断多个数时就会出现重复判断。所以有了筛法求素数。

一般筛法求素数

bool  flag[];int prime_num=0,primes[];void make_prime(int n){    memset(flag, 1, sizeof(flag));    flag[0]=false;    flag[1]=false;    for (int i=2;  i<n;  i++)        if (flag[i])        {            primes[prime_num++]=i;            for (int k=i*i; k<n; k+=i)               flag[k]=false;        }    return;}

这个方法比较好理解,初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数(注意上面的 i*i , 比 i*2 要快点 ),把这些合数都筛掉,即算法名字的由来。
但仔细分析能发现,这种方法会造成重复筛除合数,影响效率。比如30,在i=2的时候,k=2*15筛了一次;在i=5,k=5*6 的时候又筛了一次。所以,也就有了快速线性筛法。

线性筛法求素数

bool flag[];int primes[],prime_num=0;void make_prime(int n){    memset(flag,true,sizeof(flag));    flag[0]=false;    flag[1]=false;    for(int i=2; i<n; i++)    {        if(flag[i])        {            primes[prime_num++]=i;        }        for(int j=0; j<prime_num&&primes[j]*i<n; j++)        {            flag[primes[j]*i]=false;            if(i%primes[j]==0)//关键点                break;        }    }}

复杂度为O(n)。
难点在于为什么i%primes[j]==0时要break;

先可以得出一个结论,此时的prime[j]为(i*prime[j])的最小质因数。现设x=p1*a为合数,且p1为其最小的质因子,a为质数或合数,若为质数,则设a=1*p(a=1),否则设a=a*p,p为质数(因为任意一个合数都可以表示成一个质数和另一个数的乘积)。
则x=(p1*a
)*p=p1*(a*p),且可以知道p1*a<=p*a,即合数(p1*a)小于合数(p*a),且p1<=p,故得出结论,即比一个合数数大的质数和该合数的乘积可用一个更大的合数和比其小的质数相乘得到。
数x=p1a*prime[k],在之后i=(a*prime[k])时,还会再和p1`相乘重新进行isNotPrime[i prime[j]] = 1; 赋值,这样就造成了重复赋值,降低
了效率,如果break了,则不会出现这样的情况。
考虑完上面的问题,还有个问题需要考虑,即省去的i*prime[k]在后来的过程中一定会再出现从而使得将isNotPrime[i * prime[k]]赋值为1么?
答案是肯定的,因为上面提到了任意一个合数都可以表示成一个质数和另一个数的乘积,所以任意一个数总能表示成其最小质因子与另一个数相称的形式,因为我们枚举了所有可能的i,故“另一个数”的所有可能性我们都有考虑,所以,只要我们找到其最小质因子则就不会漏掉任意一个数。

0 0
原创粉丝点击