您的位置:首页 > 其它

欧拉线性筛法求素数 学习报告

2016-11-10 09:06 246 查看
筛素数的方法有很多,先说一下Eratosthenes筛法,这种筛法的思想不难理解,就是对不超过n的每个正整数p,依次删除p,2∗p,3∗p……(k−1)∗p,k∗p(k∗p<=n),最后没被筛除的就是素数了

代码也是很好写的,如下:

#include<iostream>
#include<cstdio>
#define N 100000
using namespace std;
int i,j;
bool pd
;
int main()
{
pd[1]=1;
for(i=2;i<=N;i++)
if(!pd[i])
for(j=i*i;j<=N;j+=i)//此处写成i*i,一个小优化
pd[j]=1;
for(i=1;i<=N;i++)
if(!pd[i])
printf("%d ",i);
return 0;
}


这个筛法效率还是很高的,内层的循环次数是x=⌊ni⌋−1<ni,这样,循环的总次数小于

n2+n3+n4+……+nn=O(nlogn)

所以这个筛法已经足够应付大多数题了.

但是如果n是107呢,在Eratosthenes筛法里,我们一个合数标记过很多遍,导致了时间的浪费,所以便有了一种时间复杂度近似O(n)的算法,就是欧拉线性筛法。这种筛法出去了很多冗余的标记和计算,先看下代码

#include<iostream>
#include<cstdio>
# define N 10000000
using namespace std;
int num=0,i,j,prime[1000005];
bool pd[10000005];
int main()
{
for(i=2;i<=N;i++)
{
if(!pd[i])
prime[++num]=i;//①如果是素数,选取加入prime[]
for(j=1;j<=num&&prime[j]*i<=N;j++)
{
pd[prime[j]*i]=1;
if(!i%prime[j]) //②这是为了防止出现一个合数被判断两次的情况发生
break;
}
}
for(i=1;i<=num;i++)
printf("%d ",prime[i]);
}


根据唯一分解定理,我们可以得知任意一个合数N(N>2),都可以唯一分解成p1∗p2∗p3∗p4∗…∗pn, 其中p1≤p2≤p3≤p4≤…≤pn 且都是素数.

我们要确定的就是每个合数都要被筛除且只筛一遍

设合数n=p1∗p2∗p3∗…∗pn(p1≤p2≤p3≤…≤pn)

1.因为i是从1到N循环的,所以肯定先访问n′=p2∗p3∗p4∗…∗pn, 又因为p1≤p2,所以在访问n′的时候p1∗n′是一定执行的,由此我们可以得出所有的合数都会被标记过.

2.只要当前的i% prime[j]=0 就会退出循环,prime[j] 也是从小到大循环的,所以合数n只会在i=n′=p2∗p3∗p4∗…∗pn 的时候被筛掉,在i>n′ 时,有可能满足条件的i′=p1∗p2∗…∗pnpk(2≤k≤n),此时prime[k]∗i′ 可以把n筛除,而根据②,在prime[1] 就break跳出循环了,故一个合数n都会被筛除且只筛一遍.

总结:

还是要多做一些数论题,尤其这种基础算法一定要知道原理及证明,否则有变式的时候就不知所措了.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数论