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

筛素数的方法有很多,先说一下Eratosthenes筛法,这种筛法的思想不难理解,就是对不超过n的每个正整数p,依次删除p,2p,3p(k1)p,kp(kp<=n),最后没被筛除的就是素数了
代码也是很好写的,如下:

#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=ni1<ni,这样,循环的总次数小于

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

所以这个筛法已经足够应付大多数题了.
但是如果n107呢,在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),都可以唯一分解成p1p2p3p4pn, 其中p1p2p3p4pn 且都是素数.
我们要确定的就是每个合数都要被筛除且只筛一遍
设合数n=p1p2p3pn(p1p2p3pn)
1.因为i是从1到N循环的,所以肯定先访问n=p2p3p4pn, 又因为p1p2,所以在访问n的时候p1n是一定执行的,由此我们可以得出所有的合数都会被标记过.
2.只要当前的i% prime[j]=0 就会退出循环,prime[j] 也是从小到大循环的,所以合数n只会在i=n=p2p3p4pn 的时候被筛掉,在i>n 时,有可能满足条件的i=p1p2pnpk(2kn),此时prime[k]i 可以把n筛除,而根据②,在prime[1] 就break跳出循环了,故一个合数n都会被筛除且只筛一遍.

总结:
还是要多做一些数论题,尤其这种基础算法一定要知道原理及证明,否则有变式的时候就不知所措了.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值