【问题标题】:How can I speed up a primality checker?如何加快素数检查器的速度?
【发布时间】:2015-12-10 20:29:02
【问题描述】:

我有一个函数 FIsPrime,它接受 int64 并根据给定的 int64 是否为素数返回布尔值 True 或 False。

function FIsPrime(P:int64):boolean;

  var
  I:int64;
  RSqrtP:Extended;

  begin
    I:= 3;
    RSqrtP := sqrt(P) + 1;

    while ((P mod I) <> 0) AND (I <= RSqrtP) AND ((P mod 2) <> 0) do
      I := I + 2;

    if I < RSqrtP then
      FIsPrime := False
    else
      FIsPrime := True;
  end;

但是,虽然这可行,但速度很慢。检查从 106 到 5×106 的数字需要 4 秒。

我正在按 1012 和 1015 的顺序测试数字 - 整个代码选择一个随机数并 × 1012 sup> 得到一个大的随机数。

这个随机数通过FIsPrime 函数运行并递增1直到它是素数:

function FFirstPrimeAbove(P:int64):int64;

  var
    BIsPrime: boolean;

  begin
    if P mod 2 = 0 then
      inc(P);

    repeat
      BIsPrime := FIsPrime(P);
      P := P + 2;
    until (BIsPrime);

    FFirstPrimeAbove := P;
  end;

这可能需要一段时间 - 1012 大约需要 6 秒,1015 大约需要 7 秒。

虽然 14 秒不算多,但很烦人。有没有办法通过更有效的算法来减少这个时间?

我对 Pascal 还很陌生,但已经编程多年 - 所以任何更有效的算法都会很有用。


我查看了AKS Test,但有很多行话要克服——“多项式同余关系”、“多项式的泛化”和“二项式系数”等等。

任何关于我如何在 Delphi 中实现某些东西的基本提示都将不胜感激。

【问题讨论】:

  • 这似乎与 Pascal 和 Delphi 无关。
  • @AndreasRejbrand 我正在写它是 Delphi - 所以答案将用 Delphi 编写。或者你的意思是快速算法的概念是无关的?
  • 那么在这种情况下,我建议您进行一些研究。搜索主要的测试算法肯定会发现一些东西。
  • @蒂姆。这取决于您运行此功能的频率。如果你需要它经常存储中间素数,直到你的数字的平方根可能是它。对于 10**14,您需要将质数存储到 10*7,其中大约有 5,761,455 (people.mpim-bonn.mpg.de/zagier/files/doi/10.1007/BF03039306/…)
  • 你为什么把你的函数称为FIsPrime? F是干什么用的?像这样分配布尔值:somebool := somecondition.

标签: performance delphi primes pascal


【解决方案1】:

将 RSqrtP 更改为 Int64 很可能会提高性能。我没有测试它,但我希望将浮点值与 int64 值进行比较不会是最快的。

((P mod 2) &lt;&gt; 0) 排除在外。

另外,如果您不介意应用程序的初始化时间更长,您可以加载 2 和 X 之间的所有素数的列表,并在进入 I+2 例程之前从它们开始。你不需要尝试除以非素数,因为这已经被素数处理了(即任何可以被 4 整除的东西都将被 2 整除,任何可以被 49 整除的东西也将被7等)

我不久前发布了an article 以使用素数为例进行优化。也许您会在那里看到一些可以帮助您的更多信息。

【讨论】:

  • 肯定将“它是偶数”从循环中取出会减慢它 - 还是您的意思是在循环之前检查,只有在奇数时才进入循环?我想不需要每次都检查。
  • 我猜你刚刚在这里回答了你的问题。现在,如果一个数字是偶数或非偶数,您正在测试一些数字几千次。
  • 这将它从 14 秒减少到 2 秒 - 哇!不同之处在于循环 - 只需将 P mod 2 去掉就可以了。谢谢!
【解决方案2】:

我怀疑提高速度最有效的简单方法是创建一个已知素数表。

可能有太多的素数来存储所有适合 64 位整数。但是您可以存储少于sqrt(high(int64)) 的所有素数。然后,当您循环抛出可能的除数时,您只能检查素数。这应该会带来非常显着的好处。

所以,算法大致如下:

  • 用所有小于sqrt(high(int64)) 的素数填充一个数组。这应该是预先计算好的。
  • 要测试值 N 是否为素数,首先要找到它的根,sqrt(N)
  • 然后尝试将素数除以待测值,直到达到大于sqrt(N) 的素数。
  • 如果你走到那一步,N 是素数。否则,如果你找到一个素数除数,它就不是素数。

【讨论】:

  • 根据磁盘访问速度和阿特金/埃拉托色尼筛子实现的效率,有时从头开始重新计算素数列表而不是从磁盘加载它会更快。
  • 绝对是值得进行基准测试的东西,它还取决于您的调用模式(一次调用与多次调用)。我同意@rossum 的观点,一个好的 SoE 将具有竞争力并且可能比读取文件更快,加上没有文件可以拖动。还要研究一个简单的调制轮。例如。 C 代码测试从 10^12 到 10^12+10000 的 3614 个素数的时间:17.8 秒的几率; 12.0s 模组 6; 8.0s mod 30; 4.3s 过筛素数; 3.0s 静态编译数组。后者在更大的示例中变得非常笨拙。顺便说一句,使用 BPSW 大约是 0.007 秒——比试除法快得多。
猜你喜欢
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 2012-10-26
  • 2015-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多