素数筛

对于一些题,我们需要去枚举n以下的素数,
但是有时可能不止一个数需要这样做,所以下面介绍一些筛出n以下的素数的方法

一.暴力求解

直接枚举每一个\(i\in[2,n]\),判断其是否是素数,然后加入数组
判断素数的话枚举\(j\in[2,\sqrt i]\),判断是否i能整除j,若都不能,则i为素数
代码比较简单,时间复杂度最高,为\(O(n\sqrt n)\)

for(int i=2;i<=n;i++){
	bool f=true;;
	for(int j=2;j<=sqrt(i);j++)if(i%j==0)f=false;
	if(f)cout<<i<<endl;
}

输出的即为n以内的所有素数

二.优化一下

当然,是素数的放在q数组中,
那其实有一些数可以被它的因数筛掉,也就是加个标记
之后看到直接跳过判断
所以判断也不需要\(\sqrt n\)的时间了
若到它时它仍未被访问过,则它就是个素数
这样的时间复杂度可以被减为比\(O(n)\)大一点的了
代码的话如下:

for(int i=2;i<=n;i++){
	if(!vis[i])q[++cnt]=i;
	for(int j=1;i*j<=n;j++)vis[i*j]=1;
}

进一步优化

我们会发现优化的代码中\(j\)是从\(1→n/i\)
那么难免会碰到一些无用的步骤
如枚举\(k\)\(j\)时会循环出现\(1→k-1\),会有一定的重复标记
为了避免,我们可以不枚举筛\(i\times j\),可以去筛\(i\times p[j]\)
\(p[j]==i\)的因子时就可以跳出循环
因为\(j\)之后的某个\(k\)而产生的\(p[k]\times i\)可以被\(p[j]整除\)
则之后这个\(p[k]\times i\)可以被\(p[j]\)筛掉,则此时无需多筛
时间复杂度接近\(O(n)\),代码也不怎么长

for(int i=2;i<=n;i++){
	if(!vis[i])q[++cnt]=i;
	for(int j=1;p[j]*i<=n;j++){
		vis[i*p[j]]=1;
		if(i%p[j]==0)break;
	}
}

总结

其实最后优化出的就是经典的欧拉筛
其实有些题预处理时暴力也可以……
但是这样也没多多少,代码也快了不少
所以提倡记熟欧拉筛
偷偷说一句,打表好像更快
如果有所遗漏,望大佬们在评论中加以补充
好了就这样了,下期再见!

posted @ 2020-08-04 17:52  Realityang  阅读(105)  评论(0编辑  收藏  举报