一,前言
目前求质数的方法多种多样,下面就介绍两种方法求质数。
二,线性筛法生成质数
筛法生成小于或等于n的质数的原理就是,将0到n的所有自然数都存放进一个数组里面。然后从2开始循环直到n,循环到i时,就把i的所有倍数(小于或等于n)都从这个数组里面移除,最后剩下的就是小于或者等于n的质数。
但是这种方法有一定的缺陷,就是假设 i 和 j 都是max的因数,那么无论是循环到i还是j,程序都会将max从数组中抹去,这样就重复计算了。为了消除这种不必要的计算,我们可以选用线性筛法来提高速度。
线性筛法生成质数的原理如下(结合下面的代码来理解可以更顺畅):
整个事件简单来说就是,从2到n按顺序依次抽出一个数(设为i),然后找出所有的i*m,把i*m从数组里面筛掉(因为它是合数),其中,m的取值是不小于2且不大于“i的最小质因数”的全体质数。那么数组中剩下的就全是质数了。这就是实现每个合数都只被划去一次(被它们的最小质因数筛掉)的过程。详细代码解说如下:
若要生成小于或者等于n的所有质数,则设置两个数组is_prime和prime,它们的大小均为n+1。将数组is_prime中的所有元素均初始化为0。is_prime[i]等于0时,代表i是质数;当is_prime[i]等于1时,代表i是合数。prime[i]从2到n的第i+1个质数。再设置一个下标now,它代表当程序运行到当前状态时,prime[now-1]是prime数组中的最后一个质数。
然后循环变量i,从2到n。当循环到h时,如果h是质数,那么就将h加入prime数组中,并将下标now的值加一,然后向下继续执行。如果h是合数,那就直接向下执行。
这时候,循环j,从0到now-1,而prime[now-1]*h必定是一个合数,仅当它小于或等于n时,循环j继续。如果说,prime[j]是h的因数,那么将prime[j]*h从数组里面删掉,然后退出循环。这是因为如果prime[j]是h的因数的话,那么prime[j]*h的最小质因数只能是prime[j],我们的目标是,用每一个合数的最小质因数来删除这个合数,这样就能保证,每个合数都只被删除一次。
具体代码如下:
#include<iostream>
#include<cstring>
#include<time.h>
using namespace std;
void filter_prime(int max)//线性筛生成小于或等于max的所有 质数
{
int* is_prime ;//is_prime[i]若是等于0,就说明i是质数;is_prime[i]若是等于1,就说明i是合数
is_prime = new int[max+1];
//is_prime[2] = true;
int* prime;//prime[i]代表从2开始到max之间的第i+1个质数 (包括2和max)
prime = new int[max+1];
int i,j,now=0;//now代表当前状态下prime数组最后一个质数的下标
for(i=1;i<max+1;i++)
is_prime[i] = 0;//初始化is_prime数组
//memset(is_prime,0,sizeof(is_prime));
for(i=2;i<max+1;i++)
{
if(is_prime[i] == 0)//如果i是质数
prime[now++] = i;
//以上两条语句是肯定正确的,不会出现当i是合数但is_prime[i]未修改为1的情况。理由就是:当i是质数时,该语句成立,
//所以只需讨论i是合数的情况。设i是合数, 那么程序运行到此处时,is_prime[i]一定被修改成了1。
//证明:令i = n*m,其中n是i的最小质因数。当m也是质数时,那么在程序运行到i==m时,程序会将所有的is_prime[p*m]都修改为1。
//其中p的值为从2取到m的质数(包含了n)。 当m是由一堆质数相乘得到的时(就是说它是一个合数),由于n是i的最小质因数,
//所以m的所有质因数都大于n,这也说明了当程序运行至i==m时,会将所有的is_prime[p*m]都修改为1,p的取值是从2到n的所有质数。
for(j=0; j<now && i*prime[j]<=max; j++)
{
is_prime[ i*prime[j] ] = 1;
if(i % prime[j] == 0)break;
}
}
//统计并输出所有小于或者等于max 的所有质数:
cout<<"小于或等于"<< max <<"的质数共有"<<now<<"个,具体如下:"<<endl;
/*for(i=0;i<now;i++)
cout<<prime[i]<<",";
cout<<endl;*/
//您可以去掉上面这条注释,这样的话,程序就可以输出筛选出来的所有质数了。
}
int main()
{
int max=0;
cin>>max;
clock_t begin = clock();
filter_prime(max);
clock_t end = clock();
//cout<<"3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,83,89,93,97"<<endl;
cout<<"线性筛总共耗时"<<end-begin<<"毫秒"<<endl;
return 0;
}
下面就是数据规模为1亿的时候程序运行的结果:
数据规模为1千万时的运行结果:
三,附录
在网上还看到一个与质数有关的东西,特地把它贴出来,希望能对大家的编程有所帮助。
设i为正整数,那么6*i,6*i+1,6*i+2,6*i+3,6*i+4,6*i+5,6*i+6之中,只有6*i+1和6*i+5有可能是质数。也就是说,当i为正整数时,只有6*i+1和6*i-1可能是质数。也就是说,质数只分布在6的倍数两侧。这个可以用来较为简单高效地判断一个数有没有可能是质数。