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

筛素数的方法有很多,先说一下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)

所以这个筛法已经足够应付大多数题了.
但是如果 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) ,都可以唯一分解成 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 <script type="math/tex" id="MathJax-Element-267">n</script>都会被筛除且只筛一遍.

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

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值