进阶必备:素数筛法(欧拉,埃氏筛法)

筛选素数其实有两种比较高效的算法可以提供选用,分别是:Eratosthenes筛选法与欧拉筛选法。但是欧拉算法的普适性比较高,所以这里就只介绍欧拉函数的算法。
筛选的范围较小的话,欧拉算法,数据较大,埃氏筛法,所以埃氏筛法一定要掌握,以后可以统一用埃氏筛法
【埃氏筛法】

const int maxn=10000005;
int prime[maxn];
int vis[maxn];
int cnt = 0;
void sieve()//返回n以内的素数
{
    cnt=0;
    for(int i=0;i<=maxn;i++)
        vis[i]=1;
    vis[0]=vis[1]=0;
    for(int i=2;i<=maxn;i++)
        if(vis[i])
        {
            prime[cnt++]=i;
            for(int j=2*i;j<=maxn;j+=i)
                vis[j]=0;
        }
}

筛选10000000以内的素数,埃氏筛法的平均时间是0.756秒,而欧拉算法达到了2.2秒多,所以算法的复杂度埃氏筛法远远优于欧拉。
还有一种筛选法的速度比埃氏筛法还要快一些,它的10000000以内的时间为0.721秒。代码贴上来作为参考:

const int maxn=10000005;
int prime[maxn];
int vis[maxn];
int check[maxn];
int cnt = 0;
void init()
{    
    int cnt=0;
    check[0]=check[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!check[i])
        {
            prime[cnt++]=i;
            vis[i]=1;
        }
        for(int j=0;j<cnt;j++)
        {
            if(prime[j]*i>=maxn) break;
            check[i*prime[j]]=1;
            if(i%prime[j]==0) break;

        }
    }
}

以上两种算法,执行完毕之后,vis数组里面的如果为1,表示为素数,为0则为合数。prime数组里面保存的则是素数集,cnt表示素数的个数。还有重要的一点就是对于卡时间的题,注意不要使用cin,cout这类的输入输出了,不然时间会爆掉,比如下面牛客网的这题,如果不用scanf还有printf的话,就是过不了的,所以养成好习惯的话,以后应当减少cin,cout的使用,实在无法避免使用的话,可以试试加上ios::sync_with_stdio(false); (因为即使加上这句话,cin/cout体系还是没有scanf/printf快)
【题源】牛客网

欧拉筛法因为不具有普适性,时间优化不够好,所以在这里不贴上来了,有兴趣的读者可以自行百度。

2019/4/23 二改

原文地址:https://www.cnblogs.com/myxdashuaige/p/8890712.html