线性筛质数——高效的质数判断与求解法

求质数比较高效的算法——线性筛质数(埃拉托色尼筛选法的优化)

  • 基本原理

    每次用该合数的最小质因子消除这个合数,所以就不会重复消除,也能保证将合数全部消除掉。同时质数本身没有最小质因子,所以质数不会被消除掉。

    总共设置两个数组

    • int primes[N]:从下标0开始,记录2到n的质数。

    • bool st[N]:标志这个下标(从0开始)对应的数字是否是数字,为0代表是质数,为1代表是合数。这个数组要初始化全为0。

    设置一个cnt变量,初值为0,记录执行到目前质数的个数。

  • 外循环i=2到n(n表示要求的范围),首先判断st数组,对应的i这个数是否是质数,是的话则将其加入到primes数组。

  • 而后就是利用 i 和目前已求解到的所有质数来消除部分合数,从最小质数开始遍历,保证 j<cnt,且 primes[j] x i < max,那么如何保证这个质数是该合数的最小质因子呢?首先从2开始,2毋庸置疑是拥有2这个质因子的合数的最小质因子,如果往后执行,到3呢?其实我们只要知道 i 和当前质因子 primes[j] 的关系,如果 i % primes[j] =0, 他们的商为 m 。说明什么?说明你下一次消除的合数 i x primes[j+1] 完全可以等同于 k x primes[j] ,k为primes[j+1] x m ,说明下一次消除合数所用的质因子并非最小,所以需要break,跳出判断合数这个循环。

  • 代码

    #include <iostream>
    #include <algorithm>
    #include<cstdio>
    using namespace std;
    const int N = 1000010;
    int primes[N], cnt; //题目本意是求素数个数,当然也能求每个数的最小质因子 
    int st[N];
     
    void get_primes(int n)
    {
        for(int i = 2; i <= n; i++)
        {
            if(!st[i]) primes[cnt++] = i;
            // j<cnt,防止越界到没有质数的位置, primes[j]*i <= n 防止消除的合数超过了最大值。
            for(int j = 0; j<cnt && primes[j]*i <= n; j++)
            {
                // 从已有的质数中,筛除合数,用合数的最小质因子筛除这个合数
                st[primes[j]*i] = 1;
                // 防止被重复消除,若此时i的值是某一个质数的倍数,相乘消除一个合数之后,就要break了!
                // 为什么呢?因为i是primes[j]的几倍,那么就不能再用i*primes[j+1]消除这个合数了。
                // 因为primes[j+1]不是最小的质因数,而primes[j]才是最小的,因为i本身都是这个合数的因数,那么primes[j]也是这个合数的因素,明显primes[j]更小。
                if(i % primes[j] == 0) break;
            }
        }
    }
     
    int main()
    {
        int n;
        cin >> n;
        get_primes(n);
        cout << cnt << endl;
        return 0;
    }
    
  • 求解某一范围内的质数,以及判断该质数是否为质数,都可以用这个算法。可以用预处理某一个大范围内,求解出质数的个数,或者用标记数组标记每个质数的位置。
  • 同时判断不是质数有一个快的方法,就是你要判断的这个数字n,(n+1)%6!=0 && (n-1)%6!=0,那么这个数字一定不是质数,大于3的质数,都是分布在6的倍数这个数字的左边和右边,比如5,7、分布在6的左右边。
  • 但是反之不一定,分布在6的倍数这个数字的左右边也不一定是质数,比如35,分布在36的左边,但并不是质数。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值