线性筛
前言
线性筛是筛素数一种比较常用的方法(实际上,它的用途含有很多,如筛\(\mu,\phi\)等玄学的函数)。它的时间复杂度近似于\(O(n)\)。
代码(代码后面有解析)
#include<bits/stdc++.h>
using namespace std;
int n;
map<int,int> Is_Prime;//保存每一个数是否为质数
vector<int> Prime;//保存全部质数
int main()
{
scanf("%d",&n);//筛选2~n内的质数
for(int i=2;i<=n;i++) Is_Prime[i]=1;//先默认全部都是质数
for(int i=2;i<=n;i++)
{
if(Is_Prime[i]) Prime.push_back(i);//如果这个数是质数,就将其保存下来
for(int j=0;j<Prime.size();j++)//将这个数与已有的质数进行操作
{
Is_Prime[i*Prime[j]]=0;//将这个数与该质数的积标记为非质数
if(!(i%Prime[j])) break;//如果当前数是该质数的倍数,就退出循环(这个在后面会进行解释)
}
}
printf("%d\n",Prime.size());
for(int i=0;i<Prime.size();i++) printf("%d ",Prime[i]);
return 0;
}
解释
对于\(2\sim n\)之间的一个整数\(i\),有两种可能性:
一:它是质数。则此时\(Prime[j]\)必为\(i\)(一个质数只能被\(1\)和本身整除,而\(Prime[j]\)不可能为\(1\),故\(Prime[j]\)为\(i\)),而\(i\)是刚保存下来的,必然在最末尾,即i后面没有其他可操作的数了,于是退出循环
二:它是合数。则可得\(i=Prime[j]*x\)(\(x\)为\(2\sim i\)之间的一个整数),此时又有两种可能性:
1:\(x\)是质数。则\(x\ge Prime[j]\)(若\(x<Prime[j]\),则在对\(Prime[j]\)进行操作前就会对\(x\)进行操作并退出循环),那么\(i\)与\(Prime[j]\)之后的任意一个数\(t\)的积\(i*t\)都可以表示为\(Prime[j]*x*t\),而\(x*t\)是一个合数,所以在轮到对\(x*t\)和\(Prime[j]\)进行操作时时就可以得到\(i*t\),故不需要继续操作了,可以退出循环
2:\(x\)是合数。则\(x\)可以分解为\(a*b\)的形式(其中\(a\)是所能取值的数中的最小质数),那么\(i=a*b*Prime[j]\)。对于\(a\)和\(Prime[j]\)的大小关系,又有两种可能性:
①:\(a<Prime[j]\)。显然不可能(否则在对\(Prime[j]\)进行操作前就会对a进行操作并退出循环)
②:\(a\ge Prime[j]\)。\(∵a\ge Prime[j],∴b*Prime[j]\le x\),那么\(i\)应在对\(x\)进行操作就已经在对\(b*Prime[j]\)进行操作的时候的时候被判定为非质数,自然无需再判一遍。