【问题标题】:Is this an optimal prime generator?这是一个最优的素数生成器吗?
【发布时间】:2013-05-27 20:08:58
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

    嗯,很有趣。您的代码实际上是诚实的 Eratosthenes 的真正筛子恕我直言,通过将它为遇到的每个素数设置的每个计数器递减 1 来沿递增的自然数计数在每一步。

    而且效率很低。 Tested on Ideone 它运行在相同的 empirical order of growth ~ n^2.2 (测试范围内产生几千个素数)和著名的低效 Turner's trial division sieve (在 Haskell 中)。

    为什么?几个原因。 首先,在您的测试中没有提前救助:当您检测到它是一个组合时,您会继续处理计数器数组sieve。由于第二个原因:您必须计算差异将每个计数器减1 step,0代表你当前的位置。这是原始筛恕我直言最忠实的表达,而且效率很低:今天我们的 CPU 知道如何在 O(1) 时间内添加数字(如果这些数字属于某个范围,0 .. 2^32,或者0 .. 2^64,当然)。

    此外,我们的计算机现在也有直接存取存储器,并且在计算出遥远的数字后,我们可以将它标记在随机存取数组中。这是现代计算机上埃拉托色尼筛效率的基础——直接计算和倍数的直接标记。

    第三个​​,也许是效率低下的最直接原因,是对倍数的过早处理:当您遇到 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.6n = 1000 .. 6000 素数降至略高于~ n^1.5(速度明显提高)。当计数进一步被直接加法取代时,所得的测量经验增长顺序为~ n^1.2 .. 1.3,产生多达一百万个素数(尽管它很可能会在~ n^1.5 上获得更大的范围)。

    【讨论】:

    • 感谢您的评价,帮助很大。
    猜你喜欢
    • 2010-09-18
    • 2012-03-06
    • 2015-02-20
    • 1970-01-01
    • 2014-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多