素数筛法

今天并不是十分愉快

在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 某个不知名的精分患者

posted @ 2020-09-16 15:02  Luo_Feng_Han  阅读(179)  评论(0编辑  收藏  举报