优化素数功能速度

时间:2012-11-26 02:09:47

标签: performance optimization primes

我正在编写一个函数来列出超过某个起始值'N'的'M'个素数。在这一点上,我只想使功能尽可能高效(即:快!)。我真的没有想法,所以任何帮助将不胜感激。代码(matlab)如下:

function PrimeNumbersList = primes_after(N,M)
tic;
x = N;
s = 1;
PrimeNumbersList = 0;
if mod(N,2) == 0
while numel(PrimeNumbersList) < M

if isprime(x) == 1
    PrimeNumbersList(s) = x;
    x=x+2;
    s=s+1;
else
    x=x+2;
end
end
else
while numel(PrimeNumbersList) < M

if isprime(x) == 1
    PrimeNumbersList(s) = x;
    x=x+1;
    s=s+1;
else
    x=x+1;
end 
end
end
tElapsed=toc
end

3 个答案:

答案 0 :(得分:1)

你能做的一件事就是只考虑奇数(增加2而不是1)。这会将循环迭代次数减半。

isprime可能会有所收获,具体取决于它的实施方式。这一切都取决于你需要它的准确程度(即允许使用Carmichael数字?)。

修改

您的修改并没有真正解决任何问题。试试这个:

function PrimeNumbersList = primes_after(N,M)
    tic;
    x = N;
    s = 1;
    PrimeNumbersList = 0;

    if mod(x,2) == 0
        if x == 2
            PrimeNumbersList(s) = x;
            s=s+1;
        end
        x=x+1;
    end

    while numel(PrimeNumbersList) < M
        if isprime(x) == 1
            PrimeNumbersList(s) = x;
            s=s+1;
        end
        x=x+2;
    end

    tElapsed=toc
end

此外,您可以将numel(PrimeNumberList) < M更改为s < m并避免函数调用。小的优化,但是,嘿,我们已经分裂了。

修改

如果您无法接受错误(例如Carmichael数字),那么您会遇到isprime的缓慢执行(假设它是正确的)。这是因为检查数字是否为素数是困难的。 Fermat s Little Theorem is a clever shortcut, but isprime`可能会使用它进行额外验证以消除错误。

你真的没有其他事可做。如果你愿意用另一种语言重写它,那么我会推荐Haskell;它对生成数字有很大的支持,并且会将你的代码转换成大约3行函数(或者左右)。

我不太了解matlab以消除一些额外的周期,但这里还有一些建议:

  • 如果matlab可以附加到PrimeNumbersList,请执行此操作而不是设置索引。这可能更快(它在Javascript中)
    • 这将删除s变量,从而消除了添加
  • 使用s代替numel(尝试此操作代替上述尝试)

答案 1 :(得分:1)

这里有一些潜在的速度提升。

function PrimeNumbersList = primes_after(N,M)
tic;

x = 0;
if (N mod 2) == 0 && N != 2
    x = N + 1;
else
    x = N;
end

s = 1;
PrimeNumbersList = 0;
tempInt = x - 1;
isPrime = 1;

while numel(PrimeNumbersList) < M

    while tempInt > 1 && isPrime
        if (x mod tempInt) == 0
            isPrime = 0;
        end
        tempInt=tempInt-1;
    end

    if isPrime
        PrimeNumbersList(s) = x;
        x=x+1;
        s=s+1;
    else
        x=x+1;
    end

end
tElapsed=toc
end

好的,现在解释一下:

首先,我检查N是否为偶数。如果是这样,我增加1只是为了确保它是奇数(虽然不一定是素数)。我确实考虑了整数2,因为它是素数,但可以被2整除。

由于我不知道isPrime()的速度,我只是自己编写(基于素数的简单证明)。请随意使用你的tic / toc进行检查。

除此之外,我看到的速度并没有增加太多。我的2美分。

答案 2 :(得分:0)

迭代一组数字,测试每个数字的素数。这将是不可能的缓慢。您正在寻找的算法称为Eratosthenes的分段筛。

虽然Eratosthenes的筛子非常快,但它需要O(n)空间。通过在连续的段中进行筛分,可以将筛分质子的O(sqrt(n))减少到比特阵列的O(1)。在第一段,计算该段内每个筛分素数的最小倍数,然后以正常方式将筛分素数的倍数标记为复合物;当所有筛选质数都被使用时,该段中剩余的未标记数字是素数。然后,对于下一个段,每个筛分素数的最小倍数是结束前一个段中筛分的倍数,因此筛分一直持续到完成。

考虑从20到200的段中筛选100到200的例子; 5个筛分质数分别为3,5,7,11和13.在100到120的第一个段中,比特阵有10个槽,槽0对应101,槽k对应100 + 2k - 1,槽9对应于119.段中3的最小倍数为105,对应于插槽2;时隙2 + 3 = 5和5 + 3 = 8也是3的倍数.5的最小倍数在时隙2是105,而时隙2 + 5 = 7也是5的倍数.7的最小倍数是105在插槽2处,插槽2 + 7 = 9也是7的倍数。依此类推。

函数素数采用参数lo,hi和delta; lo和hi必须是偶数,lo&lt;嗨,和lo必须大于天花板(sqrt(hi))。分段大小是两倍增量。长度为m的阵列ps包含小于sqrt(hi)的筛分素数,其中2被移除,因为偶数被忽略,并且数组qs包含到相应筛分素数的当前片段中的最小倍数的筛子比特阵列的偏移。在每个段之后,lo前进两次delta,因此对应于sier bitarray的索引j的数字是lo + 2j + 1.

function primes(lo, hi, delta)
  sieve := makeArray(0..delta-1) # bitarray
  # calculate m and ps as described in text
  qs := makeArray(0..m-1) # least multiples
  for i from 0 to m-1
    qs[i] := (-1/2 * (lo + ps[i] + 1)) % ps[i]
  while lo < hi
    for i from 0 to delta-1
      sieve[i] := True
    for i from 0 to m-1
      for j from qs[i] to delta step ps[i]
        sieve[j] := False
      qs[i] := (qs[i] - delta) % ps[i]
    for i from 0 to delta-1
      t := lo + 2*j + 1
      if sieve[i] and t < hi
        output t
    lo := lo + 2*delta

对于上面给出的样本,这被称为素数(100,200,10)。在上面给出的例子中,qs最初是[2,2,2,10,8],对应于最小的倍数105,105,105,121和117,并且被重置为第二段到[1,2,6, 0,11],对应于最小的倍数123,125,133,121和143.该算法非常快;你应该能够在不到一秒的时间内产生数百万个素数。

如果您想了解有关素数编程的更多信息,我谦虚地在我的博客上推荐this essay