欧拉筛
在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#

浙公网安备 33010602011771号