素数筛

保研机试里看起来素数用的还是很频繁的,这里总结学习一下最简单常用的三种筛法

洛谷P3912 求1-n个数中素数个数

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

你可能感兴趣的:(算法学习)