算法中有一类题,题目中涉及到大量素数的判定,只要范围确定,素数的个数和素数就已经是固定不变的,那么我们可以考虑先预处理,把范围内所有素数筛选出来,那么筛素数的方法有哪些,下面就两种算法的思想和标程进行说明。
一、埃氏(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; //关键代码
}
}
}