筛素数的方法有很多,先说一下Eratosthenes筛法,这种筛法的思想不难理解,就是对不超过n的每个正整数
p
,依次删除
代码也是很好写的,如下:
#include<iostream>
#include<cstdio>
#define N 100000
using namespace std;
int i,j;
bool pd[N];
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
,这样,循环的总次数小于
所以这个筛法已经足够应付大多数题了.
但是如果
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到
2.只要当前的
i% prime[j]=0
就会退出循环,
prime[j]
也是从小到大循环的,所以合数
n
只会在
总结:
还是要多做一些数论题,尤其这种基础算法一定要知道原理及证明,否则有变式的时候就不知所措了.