欧拉筛

在OI中,很多题目会用到素数的判断/筛选,所以掌握一个高效的素数筛法十分重要。我了解到的素数筛法主要有三类,下面我会进行介绍:

一、逐个判断法(简单易懂可惜非常慢……)

作为一个蒟蒻,谈到筛素(素数判断),本能地就会敲出下面的代码:

#include <cmath>
bool isprime(int n)  //判断n是否是素数
{
    if(n<2) return false; //特判
    int m=floor(sqrt(n));
    for(int i=2;i<=m;i++)
        if(n%i==0) return false;
    return true;
}

原理就是枚举,判断n有没有处1和n(其本身)的因数。时间复杂度\(O(n \sqrt{n})\)

二、Eratosthenes筛法(埃氏筛)

稍微快速高级一点的筛法,原理也不难理解,实现如下:

bool notprime[2000]; //素数表,不是素数值为true
void getprime()  //生成素数表
{
    notprime[0]=notprime[1]=true;  //特判
    for(int i=2;i<=n;i++)  //枚举因数
    {
        for(int j=2*i;j<=n;j+=i)  //枚举它的倍数
	    notprime[j]=true;  //为合数
    }
}

主要原理是把合数筛出,时间复杂度\(O(n \log \log n)\)

三、欧拉筛(线性筛数法)

这是我所了解的效率最高的筛法,但较难理解,(只是对于本蒟蒻而言)实现如下:

bool notprime[10000];  //素数表,辅助判断
int prime[5000]/*储存素数,尽量开大一点*/,num_prime/*素数的数量*/;
void prime(int n) //n为枚举范围(0~n)
{
    notprime[0]=notprime[1]=true;  //特判
    for(int i=2;i<=n;i++)  //枚举所有数(除0和1)
    {
        if(!notprime[i]) prime[num_prime++]=i;  //是素数->添加
        for(int j=0;i*prime[j]<=n&&j<num_prime;j++)  //
        {
            isnp[i*prime[j]]=true;  //素数*任意数=合数
	    if(i%prime[j]==0) break;  //关键!!!下面解释
        }
    }
}

这种筛法的时间复杂度为\(O(n)\),由此得名线性筛素法。为什么欧拉筛的速度比其他筛法都要快呢?
原因就是代码中关键的部分。这条语句保证了\(prime[j]\)一定小于或等于\(i\)的最小素因数,即\(prime[j]\)一定为\(i\times prime[j]\)的最小素因数。也就是保证了每个合数只筛一次,所以才是线性复杂度。

欧拉筛部分参考大佬博客->https://www.luogu.com.cn/blog/cicos/notprime#

posted @ 2020-02-06 15:58  stdout  阅读(304)  评论(0)    收藏  举报