这是最佳的主要发电机吗?

时间:2013-05-31 22:19:39

标签: python algorithm primes sieve-of-eratosthenes sieve

这在任何方面都是寻找素数的最佳解决方案吗?我不是想在阳光下添加所有优化,但主要是好处吗?

def primesUpto(self, x):
    primes = [2]
    sieve = [2]
    i = 3
    while i <= x:
        composite = False
        j = 0
        while j < len(sieve):
            sieve[j] = sieve[j] - 1
            if sieve[j] == 0:
                composite = True
                sieve[j] = primes[j]
            j += 1
        if not composite:
            primes.append(i)
            sieve.append(i*i-i)
        i += 1
    return primes

1 个答案:

答案 0 :(得分:4)

嗯,非常有趣。你的代码实际上诚实善良 Eratosthenes的真正筛子 恕我直言,通过递减为每个遇到的素数设置的每个计数器,按照递增的自然数计数在每一步。

这是非常低效的。 Tested on Ideone它运行在相同的empirical order of growth ~ n^2.2(在所测量的几千个素数的测试范围内)作为着名的低效Turner's trial division sieve(在Haskell中)。

为什么呢?几个原因。测试中 第一次 ,没有早期救助:当您检测到它是一个复合时,您继续处理计数器数组sieve 。您必须,因为 第二个原因 :您计算差异通过递减每个计数器1步骤,0表示您当前的位置。这是原始筛选IMHO最忠实的表达,效率非常低:今天我们的CPU知道如何在O(1)时间内添加数字(如果这些数字属于某个范围,0 .. 2 ^ 32,或者0 .. 2 ^ 64,当然)。

此外,我们的计算机现在也有直接访问内存,并且计算了我们可以在随机访问数组中标记它的远端数。这是Eratosthenes在现代计算机上筛分效率的基础 - 直接计算和多重直接标记。

第三 ,也许是效率低下的最直接原因,是 过早 处理倍数:当你遇到5作为素数时,你将它的第一个倍数(尚未遇到),即25,马上添加到计数器数组sieve(即当前点和倍数,i*i-i)。那太早了。添加25必须 推迟 ,直到......好吧,直到我们在升序自然数中遇到25。开始过早地处理每个素数的倍数(在p而不是p*p)导致有太多的计数器需要维护 - O(n)它们(其中n是产生的素数),而不仅仅是O(π(sqrt(n log n))) = O(sqrt(n / log n))

推迟优化when applied on a similar "counting" sieve in Haskell带来了经验性的增长订单,从~ n^2.3 .. 2.6 n = 1000 .. 6000素数增长到~ n^1.5以上(显然巨大)速度提高)。当计数进一步被直接加法取代时,由此产生的经验增长的经验序列为~ n^1.2 .. 1.3,产生高达一百万个素数(尽管很可能在较大范围内获得~ n^1.5。) / p>