素数筛法程序实现

来源:互联网 发布:全民淘宝客工具 编辑:程序博客网 时间:2024/05/28 04:55

    质数是数论中的关键所在,因此很多ACM的题目都需要求出或者判断质数。判断一个数是否为质数很简单。

bool isPrime(int n){    for(int i=2;i*i<=n;++i)//i*i可能超范围,也可以使用sqrt        if( 0 == n % i )            return false;    return true;}


    一般而言,相关题目的测试数据都不止一个case,因此比较好的做法是用筛法求出一定范围内所有的质数。
bool isComp[SIZE] = {false};//isComp[i]指示i是否为合数int P[SIZE] = {0}; //依次保存质数的数组int PCnt = 0; //质数的个数void sieve(){    for(int i=2;i<SIZE;++i){        if ( isComp[i] ) continue;        P[PCnt++] = i;        for(int j=i*i;j<SIZE;j+=i)//i*i同样可能超范围            isComp[j] = true;    }}


    还可以有一个略微快一点的方法,因为偶数显然是合数。
bool isComp[SIZE] = {false};//isComp[i]指示i是否为合数int P[SIZE] = {2}; //依次保存质数的数组int PCnt = 1; //质数的个数void sieve(){    for(int i=4;i<SIZE;i+=2) isComp[i] = true;    for(int i=3;i<SIZE;++i){        if ( isComp[i] ) continue;        P[PCnt++] = i;        for(int j=i*i;j<SIZE;j+=i<<1)//i*i同样可能超范围            isComp[j] = true;    }}


    因为没有重复判断偶数,所以上述算法会比原始筛法快一点点。根据“不重复判断”这个原则,有线性时间筛法。如下:
bool isComp[SIZE] = {false};int P[SIZE] = {0};int PCnt = 0;void sieve(){  for(int i=2;i<SIZE;++i){    if ( !isComp[i] ) P[PCnt++] = i;      for(int j=0;j<PCnt&&i*P[j]<SIZE;++j){       isComp[i*P[j]] = true;       if ( 0 == i % P[j] ) break;    }  }}


    筛法的根本原则是找到一个质数之后,将它的倍数全部标识为合数。而线性筛法则是将所有已知的质数的i倍标识为合数。同时当P[j]是i的最小质因子时,不需要再去标记更大质数的倍数。因为对于更大的质数P[j'],它的i倍可以写作P[j']*i = P[j]*k*P[j'],记住P[j]是i的质因子。所以这个数一定可以在比i更大的i'=P[j']*k的时候找到。因此,比P[j]大的质数在i的时候均不需要考虑。
    例如当已经找到质数2、3、5、7、11、13时,当i为15时,将30、45标识为合数,而75及其以上则不必进行标识。因为当i迭代至25时,75自然会被标识为合数。
    用这种方法确保每一个标志位只被设置一次,因此是线性时间的。同时这种方法可以求出每个整数的最小质因子。
    某些题目中,由于取值范围较大,不可能将范围内的所有质数都筛出来,一般就求得SIZE=sqrt(MAXSIZE)以内的所有质数,保证所有合数的质因子至少已经求出一个。此时判断质数的函数可以如下:
bool isPrime(int x){    if ( x < SIZE ) return !isComp[x];        for(int i=0;P[i]*P[i]<=x&&i<PCnt;++i)        if ( 0 == x % P[i] )             return false;   return true;}


0 0
原创粉丝点击