素数线性筛(O(N)!!!)

来源:互联网 发布:宝贝关键词怎么优化 编辑:程序博客网 时间:2024/06/13 11:39

我们先来看一下最经典的埃拉特斯特尼筛法。时间复杂度为O(n loglog n)

    int ans[MAXN];      void Prime(int n)      {          int cnt=0;          memset(prime,1,sizeof(prime));          prime[0]=prime[1]=0;          for(int i=2;i<n;i++)          {              if(vis[i])              {                 ans[++cnt]=i;//保存素数                  for(int j=i*i;j<n;j+=i)//i*i开始进行了稍微的优化                 prime[j]=0;//不是素数               }          }          return;      }  

通过观察我们可以发现一个问题,这种方法会重复筛除合数,影响了效率。
比如,当30,在2*15筛了一次,在5*6又筛了一次。
所以我们有了一个快速线性筛法,不会重复筛选同一个数,所以几乎是线性的。
首先我们要知道一个条件
任何一个合数都可以表示成一连串质数的乘积
每个合数有一个最小的质因子,用这个质因子筛去这个合数,这样时间就是线性的了。
至于实现,先来看下代码

#include <cstdio>#include <iostream>using namespace std;int prime[20000];//记录质数int vis[20000];//标记是否是质数int cnt;void Prime(int n){    vis[1]=1;    for(int i=2;i<=n;i++)     {        if(!vis[i])         prime[++cnt]=i;        for(int j=1;j<=cnt&&i*prime[j]<=n;j++)         {            vis[i*prime[j]]=1;            if(!(i%prime[j]))             break;         }        }}int main(){    int n;    scanf("%d",&n);    Prime(n);    for(int i=1;i<=n;i++)      if(!vis[i])      printf("%d ",i);    return 0;}

刚刚说的让合数被它最小的质因数筛去在代码中的体现就是这句:
if(!(i%prime[j]))
break;
由于枚举是从小到大的,所以prime数组中的质数是递增的
如果i%prime[j]为零(即i为prime[j]的倍数)
那么i*prime[j+1]这个合数一定已经被prime[j] 乘以某个数筛掉了
因为i中含有prime[j], prime[j] 比 prime[j+1] 小
在满足i%prme[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是prime[j]*i的最小因子。
这样我们便实现的线性筛

原创粉丝点击