草庐IT

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

cup11 2023-03-28 原文

测试标准

这里使用两类、五种常用质数判断算法进行测试:枚举因子法(暴力、开方优化、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++质数判断算法的时间测试的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  3. ruby - Ruby 的 Hash 在比较键时使用哪种相等性测试? - 2

    我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。

  4. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  5. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  6. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

    我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

  7. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  8. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  9. ruby-on-rails - 如何使辅助方法在 Rails 集成测试中可用? - 2

    我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel

  10. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

随机推荐