我有一个函数FIsPrime
,它接受int64
并返回一个布尔值True或False,具体取决于给定的int64
是否为素数。
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;
然而,虽然这有效,但它很慢。检查从10 6 到5×10 6 的数字需要4秒。
我正在测试大约10 12 和10 15 的数字 - 整个代码选择一个随机数××10呢 12 得到一个大的随机数。
这个随机数通过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;
这可能需要一段时间 - 对于10 12 约为6秒,对于10 15 为7。
虽然14秒不多,但很烦人。有没有办法通过更有效的算法来缩短这段时间?
我对Pascal很新,但多年来一直在编程 - 所以任何更有效的算法都会有用。
我看了AKS Test但是有很多行话可以克服 - “多项式同余关系”,“多项式的推广”和“二项式系数”来选择一些。
有关如何在Delphi中实现某些内容的任何基本提示将不胜感激。
答案 0 :(得分:4)
将RSqrtP更改为Int64很可能会提高性能。我没有对它进行测试,但我希望将浮点值与int64值进行比较并不是最快的。
将TOP 100 PERCENT
带出循环。
此外,如果您不介意为您的应用程序设置更长的初始化时间,您可以加载2到X之间的所有素数列表,并在进入I + 2例程之前从它们开始。你不需要尝试除以非素数,因为这已经由素数来处理(即任何可以除以4的东西将除以2,任何可以除以49的东西也会是7,等等)
我发布an article关于使用素数的优化作为一个例子。也许您会在那里看到更多可以帮助您的信息。
答案 1 :(得分:1)
我怀疑提高速度的最有效的简单方法是创建一个已知素数表。
可能有太多的素数存储所有适合64位整数的素数。但是你可以存储少于sqrt(high(int64))
的所有素数。然后当你循环抛出可能的除数时,你只能检查质数。这应该提供非常显着的好处。
因此,该算法概括为:
sqrt(high(int64))
的所有素数填充数组。这应该是预先计算的。 sqrt(N)
。 sqrt(N)
的素数。