ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C++质数判断算法的时间测试

2022-07-03 23:03:33  阅读:131  来源: 互联网

标签:埃氏 筛法 质数 C++ IntT 算法 000 优化


测试标准

这里使用两类、五种常用质数判断算法进行测试:枚举因子法(暴力、开方优化、6n再优化)、质数筛(埃氏筛法、欧拉筛法)。(Miller-Rabin呢?不会,没搞懂)

同时,使用两类情况进行测试:

  1. 寻找 2-100,000 内的质数个数
  2. 寻找 10,000,001-10,009,999 内的质数个数

质数判断算法

枚举因子法

1. 暴力遍历

很显然,判断n是不是质数,最简单的只要暴力从2到n过一遍就可以了

template <class IntT> bool isPrime(IntT n) {
    if (n <= 1)
        return false;
    for (IntT i=2; i<n; i++) {
        if (n % i == 0)
            return false;
    }
    return true;
}

分析:最坏情况(是质数)每个都遍历一遍,时间复杂度\(O(n)\),平均情况由于质数分布也是\(O(n)\)

2. 开方优化

容易推出,若一个数n不是质数,则它必然有一个因数\(F\le\sqrt{n}\)。因此,只需要判断 [2-\(\sqrt n\)]之间的数即可。

template <class IntT> bool isPrime(IntT n) {
    if (n <= 1)
        return false;
    for (IntT i=2; i*i<=n; i++) {
        if (n % i == 0)
            return false;
    }
    return true;
}

分析:时间复杂度降为\(O(\sqrt n)\)

3. 6n优化

再想一想就会发现,我们对于2、3的倍数做了太多次重复运算:一个数模2不等于0,那么它模4、6、8也都不会等于0;3也一样。这些重复在6次的周期内会重复4次。因此,我们可以考虑优化它们,即:
除特殊情况外,只需考虑因数为\(6n-1\)和\(6n+1\)的情况(n为正整数)

template<class IntT> bool isPrime(IntT n) {
    if (n <= 1)
        return false;
    if (n <= 5 && n != 4)
        return true;
    if (n % 2 == 0 || n % 3 == 0)
        return false;
    for (IntT i=5; i*i<=n; i+=6) {
        if (n % i == 0 || n % (i + 2) == 0)
            return false;
    }
    return true;
}

分析:虽然时间复杂度没降,但速度显著提升了

筛法

这里就不详细讲解了,主要是运用已知的质数遍历,减少求范围内质数时重复的遍历次数(筛法都会更耗空间)。

1. 埃氏筛法

(注:这里加了个小小的优化,在第二层循环里,j初始化为i*i

template<class IntT> IntT get_primes_num(IntT n) {
    if (n <= 1)
        return 0;
    bool *isPrimes = new bool[n + 1];
    IntT *primes = new IntT[n - 1],
         pn = 0;
    memset(isPrimes, 1, n - 1);
    for (IntT i=2; i<=n; i++) {
        if (isPrimes[i]) {
            primes[pn++] = i;
            for (IntT j=i*i; j<=n; j+=i)
                isPrimes[j] = false;
        }
    }
    delete isPrimes;
    delete primes;
    return pn;
}

时间复杂度为\(O(n\ln\ln n)\)(这一堆原理自己搜去,我自己也讲不清楚)

2. 欧拉筛法

对于埃氏筛法的进一步优化

template<class IntT> IntT get_primes_num(IntT n) {
    if (n <= 1)
        return 0;
    bool *isPrimes = new bool[n + 1];
    IntT *primes = new IntT[n - 1],
        pn = 0;
    memset(isPrimes, 1, n - 1);
    for (IntT i=2; i<=n; i++) {
        if (isPrimes[i])
            primes[pn++] = i;
        for (IntT j=0; i*primes[j]<=n; j++) {
            isPrimes[i * primes[j]] = false;
            if (i % primes[j] == 0)
                break;
        }
    }
    delete isPrimes;
    delete primes;
    return pn;
}

时间复杂度为\(O(n)\)。注意:在n不大的时候,欧拉筛法与埃氏筛法比并不具有绝对优势,还有可能被反超(毕竟只是优化了个\(\ln\ln n\)),几乎相当于拼常数

测试

好了,接下来就是我们的核心——测试环节了。我这里在本机使用了一个基于std::chrono的封装计时库(程序主体部分运行至少6s)。记录以微秒(μs,百万分之一秒)为单位。
(windows x64 AMDRyzen5-5600H)

回顾一下两种情况:

  1. 寻找 2-100,000 内的质数个数
  2. 寻找 10,000,001-10,009,999 内的质数个数(筛法:你故意找茬是吧)
平均用时(μs) 情况1 情况2
暴力遍历 647,400 8,800,000
平方优化 3,985 3,496
6n优化 1,363 1,163
埃氏筛 154.5 35,000
欧拉筛 265.5 50,000

总结

平时做算法题的时候,如果说是从1或者一个较小的数开始计数的,那么建议使用埃氏筛(欧拉筛实现起来更烦,还不一定拼得过加了优化的埃式筛法(ps:优化加了容易数据容易溢出,建议用unsigned long long),要么去掉优化老老实实用j=2i);
反之,如果是都在一个较高位的,那么建议使用6n优化,容易写,比平方优化效率也高很多。这时候再使用两次筛法的差值,时间就容易爆掉(就算一次也容易爆时间)。

好了,以上就是本人对C++质数判断算法的时间测试的所有内容了,初次创作,如有错误欢迎指出ヾ(^▽^*)))

标签:埃氏,筛法,质数,C++,IntT,算法,000,优化
来源: https://www.cnblogs.com/cup11/p/16441262.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有