您的位置:首页 > 其它

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

2017-05-26 17:57 183 查看
我们先来看一下最经典的埃拉特斯特尼筛法。时间复杂度为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的最小因子。

这样我们便实现的线性筛
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: