埃氏筛法和线性筛法求素数

      算法中有一类题,题目中涉及到大量素数的判定,只要范围确定,素数的个数和素数就已经是固定不变的,那么我们可以考虑先预处理,把范围内所有素数筛选出来,那么筛素数的方法有哪些,下面就两种算法的思想和标程进行说明。

一、埃氏(Eratosthenes)筛素数。

      原理:基于任意整数x的倍数2x,3x,4x,...都不是质数。时间复杂度为O(Nlog logN),该算法原理比较好理解,算法竞赛中常用此算法筛质数。

      我们可以从2开始,从小到大扫描每个数x,把x的倍数x*2,x*3,...x*(N/x)标记为非质数【此处其实可以优化,不用从2倍开始,因为对于每个数x,x的2倍,3倍都会被2,3,..<x的数筛过,因此只需从x倍开始】。当扫描到一个数x时,若未被标记为非质数,那么该数便是质数,因为它能在比它小的数的过筛过程中逃过,就说明它前面没有一个数是它的倍数,然一个数的因子只可能比它小,因此我们可以断定,该数x便是一个素数。

       标程如下:

    bool isprime[10000005];
    void aishiPrime(int n){
	memset(isprime,true,sizeof(isprime));
	isprime[1]=false;
	for (int i=2; i*i<=n; i++){
		if (isprime[i])	{    
		  	for(int j=i*i;j<=n; j+=i) 
            	            isprime[j]=false; 
    	        }
	}	    
    }

二、线性筛法

      在埃式筛法中,存在有些数存在重复筛的情况,如:385这个数,385= 5 * 7* 11,会被5的倍数时标记一次,7的倍数时标记一次,11的倍数时标记一次,造成效率达不到最优。而线性筛法基于改进这个不足的基础上,在线性时间内,也就是O(n),用筛选的方法把素数找出来。

     核心原理:对于每个合数,都只由它最小的质因子筛掉。

     比如:(假定:ans[]数组中存放着已经确定的素数)
             合数 i = p(最小素因子)* a;

           若 i%ans[j] ==0; 则 i * ans[j+1] =  p * a * ans[j+1] 可以被后面的 a * ans[j+1] 再乘以素数 p 筛选出来,(显而p<ans[j+1]) 所以i%ans[j] == 0 时要停止。

举例如下【读者自行模拟23以后的数的筛选过程】:

更正:上图中i=20时,筛掉的素数是40.

标程如下:

const int MAXN = 20000000;
bool isprime[MAXN+1];
int ans[MAXN/10] ,cnt; 
void _Prime(int n){
    memset(isprime,true,sizeof(isprime));
    isprime[0] = isprime[1] = false;
    for (int i=2; i<=n; i++){
        if (isprime[i]){      
            ans[++cnt] = i;
        }
        for(int j=1;j <= cnt && i*ans[j]<=n ; j++){
            isprime[i*ans[j]] = false;
            if(!(i%ans[j])) break;    //关键代码
        }
    }       
}


猜你喜欢

转载自blog.csdn.net/xuechen_gemgirl/article/details/79555123