顾名思义筛的意思是从一堆东西里面选择出自己想要或者说是符合条件的东西,类似的(新手小白自己理解的)有桶排序的思想,在很多算法算竞赛题中也算是一个很实用的思想。
那么废话不说就直接进入开始学习素数筛吧。
为了后期理解方便,给出一个目标,我们的目标就是找出2~n以内的所有素数
方法一:
暴力筛选
const int N=1e5;
bool x[N];
bool check(int x)
{
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
return false;
}
return true;
}
void read()
{
for(int i=2;i<N;i++)
{
if(check(i))
{
x[i]=true;
}
}
}
很直接的做法,从2~N判断是否为素数,如果是那么就进行一个标记。
对于时间复杂度并没有优化
方法二:
埃氏法
本方法筛素数相反,主要思考素数比较难找而非素数比较好找,那么我们就可以思考把非素数给筛出去的思想,代码如下
bool x[N];
void read()
{
for(int i=2;i<=N;i++)
{
if(x[i])
{
for(int j=2;j*i<=N;++j)//注意不要将本来为素数的i筛掉
{
x[j*i]=true;
}
}
}
}
方法三:
欧拉筛
本方法是对埃氏筛法的升级,避免如2*3和3*2的重复运算
void read()
{
for(int i=2;i<=N;i++)
{
if(!is[N])
{
x[++c]=i;
}
for(int j=1;j<=c&&i*x[j]<=N;++j)//限定范围
{
is[i*x[j]]=true;//标注非素数
if(i%x[j]==0)//防止重复操作
break;
}
}
}
这种方法的主要思想还是围绕素数的倍数不再是素数的问题,但是却比埃氏快,让我们来分析一波。
我们假设i为一个任意值n,那么x[]中必然记录了小于n的所有素数,那么n%x[j]=0时,说明要么n=x[j]要么x[j]为n的一个因子,i=2和3都只存在第一种情况,i=4时,假设对于x[j]=2不停止,那么会标记一个12,之后的6也还会被x[j]=2标记一个12,推广来看我们会发现所有的偶数都会被2标记一遍,这是因为我们标记的顺序是依次从最小的质数开始标记,且2为最小质因子。回到目标是为了减少重复搜素的次数,因此我们就是要让我们想要标记的数字只被他最小质因子标记,n%x[j]==0,时n存在了一个因子x[j]那么之后再用x[j+1(或者其他)]标记的时候所标记的数字最小质因子就不会是x[j+1]而是x[j]因此就会出现重复标记的现象,所以我们可以用i%x[j]来减少重复标记的次数。
以上就是我对质数筛目前为止的全部理解啦!这是我的第一篇博客,也希望有大佬能来多多指正啦!