保研机试里看起来素数用的还是很频繁的,这里总结学习一下最简单常用的三种筛法
1.判断一个数是否为素数 复杂度 O ( n ) O(\sqrt{n}) O(n)
bool is_prime(int n){
for(int i=1;i*i<=n;i++){
if(n%i==0)return false;
}
return true;
}
那么,如果要找出所有素数,使用逐个判断的方法,复杂度会达到 O ( n n ) O(n\sqrt{n}) O(nn)
2.埃筛(埃拉托斯特尼筛)
思路:首先将2到n范围内的整数写下来,其中2是最小的素数。将表中所有的2的倍数划去,表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数。再将表中所有的3的倍数划去……以此类推,如果表中剩余的最小的数是m,那么m就是素数。然后将表中所有m的倍数划去,像这样反复操作,就能依次枚举n以内的素数
void aishai(int n){//埃筛,num存1-n的素数个数
for(int i=2;i<=n;i++){
if(!visit[i])num++;
for(int j=2;j*i<=n;j++){
visit[j*i]=true;
}
}
}
复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
3.欧拉筛
目标:在埃筛的基础上,让每个合数只被它的最小质因子筛选一次。
void eulershai(int n){
for(int i=2;i<=n;i++){
if(!visit[i]){
prime[++num]=i;
}
for(int j=1;j<=num;j++){
if(i*prime[j]>n)break;
visit[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
}
复杂度
O
(
n
)
O(n)
O(n)
思路:每次循环时,将已有的所有素数的
i
i
i倍的数筛选掉。
难点:理解
if(i%prime[j]==0)break;
这一步可以有效避免重复筛选。
推导如下:
当 i i i是 p r i m e [ j ] prime[j] prime[j]的整数倍时, i i i可表示为 i = k × p r i m e [ j ] i=k\times prime[j] i=k×prime[j],则 i × p r i m e [ j + 1 ] = ( k × p r i m e [ j ] ) × p r i m e [ j + 1 ] = ( k × p r i m e [ j + 1 ] ) × p r i m e [ j ] = k ′ × p r i m e [ j ] i\times prime[j+1]=(k\times prime[j])\times prime[j+1]=(k\times prime[j+1])\times prime[j]=k'\times prime[j] i×prime[j+1]=(k×prime[j])×prime[j+1]=(k×prime[j+1])×prime[j]=k′×prime[j]
即,我们本来下一步要筛掉 i × p r i m e [ j + 1 ] i\times prime[j+1] i×prime[j+1],此时我们发现,这个数可以在之后的某轮循环( k ′ k' k′轮)时被 p r i m e [ j ] prime[j] prime[j]筛掉,因此我们为了不重复筛选(即让每个合数只被它的最小质因子筛选一次),就在当前break,停止筛选。
参考博客:
https://blog.csdn.net/dy416524/article/details/86431057
https://blog.csdn.net/qq_41117236/article/details/81152055