欧拉筛质数(线性筛)

  • 适用场景:求2~N范围内的素数
  • 优点:线性筛,复杂度为O(n)。与埃氏筛相比,不会对已经被标记过的合数再进行重复标记,故效率更高。欧拉筛将合数分解为 (最小质因数 * 一个自然数) 的形式,通过最小质因数来判断当前合数是否已经被标记过。
  • 筛数原理:我们知道当一个数为素数的时候,它的倍数肯定不是素数。所以我们可以从2开始通过乘积筛掉所有的合数。
  • 算数基本定理:一个合数可以唯一分解为多个素数的乘积。
  • 例如:i循环到77,这是要筛掉77的倍数,2*77,3*77,4*77…这些都为合数,那么筛到什么时候为止呢?
    • 77=7*11
    • 筛掉2*77=2*7*11
    • 筛掉3*77=3*7*11
    • 筛掉5*77=5*7*11
    • 筛掉7*77=7*7*11
    • 停止筛数
    • 此时如果继续筛下去,就要筛掉11*77=11*7*11=847,那么这个数也会被121筛,因为7*121=7*11*11=847,产生重复筛出
  • 避免重复筛除原理:把一个合数分解为,一个质因子乘以另一个自然数X,欧拉筛就是保证所有合数,只被这个X筛。这样就保证了这个数不会被重复筛出。
    • 847=7*11*11,保证847只被11*11,即121在它的回合内筛除(筛2*121,3*121,5*121…)就行了
    • 实现方法就是,每个数i只筛到最小质因子乘以自身就停止。
  • 代码
class Solution {
public:
    int countPrimes(int n) {
        vector<int>prime(n);    //记录已发现的素数
        int num=0;  //已发现素数数量
        //记录0~n-1是否是质数
        vector<bool>digit(n,true);  //下标即对应数字
        for(int i=2;i<n;i++){
            //遇到素数存起来
            if(digit[i]){
                prime[num++]=i;
            }
            //筛除合数
            for(int j=0;j<num;j++){
                if(i*prime[j]>=n)
                    break;
                digit[i*prime[j]]=false;
                //第一个能整除i的,即i的最小质因数(因为prime中的质数是从小到达排列的)
                if(i%prime[j]==0)
                    break;
            }
        }
        return num;
    }
};
  • 算法思想:保证构建合数的路径唯一
    • 方法:用一个基数向后筛数的时候,将基数分解为从小到大的质因数相乘
      • 如果是素数,则为X1
      • 如果是合数,则质因数分解为X1 * X2 * …* Xn
    • 规定一种构建合数的方法:即在上面的序列左侧再乘上一个数
    • 使用基数向后筛数的过程,即找基数的倍数的过程,即在质因数相乘的序列左侧再乘以一个数的过程,等同在构建合数
    • 那么每次构建合数的时候,只允许在左侧乘以一个小于等于X1的素数,那么就保证了构建合数的路径唯一,也就意味着一个合数只会被一个特定的基数筛出,也就不会出现重复筛数的情况。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值