素数筛法
今天并不是十分愉快
在gg面前吐槽素数个数那道题很简单,然后发现我早就忘了欧拉筛怎么写
这就十分令人愉(尴)快(尬)了。
于是,我下定决心
整理一下素数的筛法(其实只有两种,rua)
首先是埃斯托拉尼特筛法
埃氏筛:从2开始,将每一个质数的倍数都标记成合数,从而筛选出质数(未被标记的)。
visit[0]=visit[1]=1;//0和1显然不是素数
for(int i=2;i<=MAXN;i++){
if(!visit[i]){ //如果i是素数
for(int j=i*i;j<=MAXN;j+=i){
visit[j]=i;
}
}
}
因为不同的人寻找素数筛法的目的是不相同的,因此这里并不会贴上完整的代码。
如果有什么需要的话可以自己进行不全。
另外补充:
关于为什么j=i*i
。
我并不能说这是显然,但是大家可以仔细思考一下。
举个栗子:
对于i=7而言,
如果我们取到了j小于7,例如5,
显然之前在对5进行判断的时候已经筛过了5*7,
那么自然就会出现重复的情况。
最后是欧拉筛法(怪怪的)
欧拉筛
其实按照我个人的知识范围而言,所知道的筛法只有欧拉筛和埃氏筛(也许吧)。
所以我们自然而然的就要比较一下两种筛法的区别。
经过了长时间的探索,翻了无数个题解,无数篇博客。
终于在zdx大神的指导下,我悟了。
其实欧拉筛和埃氏筛最大的差别就在于一行代码。
如果没有那一行代码,那么其实欧拉筛本身是要比埃氏筛复杂的。
(但是同样不要想把埃氏筛加上一行代码使他更加简洁,两者的循环本质不同,无法实现)
这一行代码是:
if(i%is_prime[j]==0)break;
zdx说很少有人能理解这段代码,然而我悟了?
那就还是说一下本人的拙见:
在欧拉筛整体代码(详见本文末尾)的大环境之下,这段代码还是比较好理解的。
他的根本作用就是为了防止筛合数的重复。
因为对于欧拉筛的操作而言,在没有这行代码的情况下,
我们所进行的操作就是把所有的质数从1到n乘一遍得到合数(并不一定能够乘到n),
但是这样的操作就意味着一个问题,
还是一个栗子:
比如说4*3=12,
我们对12进行了一次标记。
但是当i=6的时候,便利j=2,6*2=12,我们又进行了一次标记,
相当于进行了二次的操作。
而对于这行代码,
显然我们省略了这段重复判断的过程。
妙啊~
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
int is_prime[10000000];
int n,tot=0;
bool a[1000000001];
int main(){
scanf("%d",&n);
a[1]=true;
for(int i=2;i<=n;i++){
if(!a[i]){
is_prime[tot++]=i;
}
for(int j=0;j<tot&&is_prime[j]*i<=n;j++){
a[is_prime[j]*i]=true;
if(i%is_prime[j]==0)break;
}
}
printf("%d",tot);
return 0;
}
上面是洛谷的P3912 素数个数AC代码,注意数组范围,我曾经因此RE,TLE,WA
祝大家OI愉快。
——by 某个不知名的精分患者