【问题标题】:Python Eratosthenes Sieve Algorithm OptimizationPython Eratosthenes Sieve 算法优化
【发布时间】:2012-02-21 01:29:02
【问题描述】:

我正在尝试实施埃拉托色尼筛法。输出似乎是正确的(减去需要添加的“2”),但如果函数的输入大于 100k 左右,它似乎需要过多的时间。我可以通过哪些方式优化此功能?

def sieveErato(n):
     numberList = range(3,n,2)

     for item in range(int(math.sqrt(len(numberList)))):
            divisor = numberList[item]
            for thing in numberList:
                    if(thing % divisor == 0) and thing != divisor:
                            numberList.remove(thing)
    return numberList

【问题讨论】:

标签: python optimization primes sieve-of-eratosthenes


【解决方案1】:

你可以尝试埃拉托色尼的方法。取一个包含所有数字的数组,以检查升序,转到数字 2 并标记它。现在每隔一个数字刮一次,直到数组结束。然后转到3并标记它。之后每三号刮一次。然后转到 4 - 它已经被划伤了,所以跳过它。对每个尚未划伤的 n+1 重复此操作。

最后,标记的数字是质数。此算法速度更快,但有时需要大量内存。您可以通过删除所有偶数(因为它们不是素数)并手动将 2 添加到列表中来对其进行一些优化。这会稍微扭曲逻辑,但会占用一半的内存。

这是我在说什么的插图:http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

【讨论】:

  • 如果您遵循该流程,您将多次检查每个号码。有可能只需要检查一次。看我的回答。
【解决方案2】:

警告:在迭代时从迭代器中删除元素可能是危险的......

你可以做

    if(thing % divisor == 0) and thing != divisor:

通过在到达“除数”索引然后测试时中断的循环中拆分它来测试更轻:

for thing in numberList_fromDivisorOn:
    if(thing % divisor == 0):
        numberList.remove(thing)

【讨论】:

    【解决方案3】:

    我按照@MAK 的建议点击了这个链接:Sieve of Eratosthenes - Finding Primes Python,我发现可以通过我在您的代码中找到的想法来改进接受的答案:

    def primes_sieve2(limit):
        a = [True] * limit               # Initialize the primality list
        a[0] = a[1] = False
        sqrt = int(math.sqrt(limit))+1
        for i in xrange(sqrt):
            isprime = a[i]
            if isprime:
                yield i
                for n in xrange(i*i, limit, i):     # Mark factors non-prime
                    a[n] = False
        for (i, isprime) in enumerate(a[sqrt:]):
            if isprime:
                yield i+sqrt
    

    【讨论】:

    • 返回 [2, 3, 5, 7, 1, 3, 7, 9, 13, 19, 21, 27, 31, 33, 37, 43, 49, 51, 57, 61 , 63, 69, 73, 79, 87] 限制 = 100。不仅存在奇怪的重复,而且其中许多项都不是质数。
    【解决方案4】:

    如果给定无限的内存和时间,以下代码将打印所有质数。它会在不使用试用部门的情况下完成。它基于论文中的haskell代码:The Genuine Sieve of Eratosthenes by Melissa E. O'Neill

    from heapq import heappush, heappop, heapreplace
    def sieve():
        w = [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]
        for p in [2,3,5,7]: print p
        n,o = 11,0
        t = []
        l = len(w)
        p = n
        heappush(t, (p*p, n,o,p))
        print p
        while True:
            n,o = n+w[o],(o+1)%l
            p = n
            if not t[0][0] <= p:
                heappush(t, (p*p, n,o,p))
                print p
                continue
            while t[0][0] <= p:
                _, b,c,d = t[0]
                b,c = b+w[c],(c+1)%l
                heapreplace(t, (b*d, b,c,d))
    sieve()
    

    【讨论】:

    • 代码不仅不打印11,而且似乎打印了许多11的素数[121、143、187等]
    • 糟糕,我没看到,但修复很简单。
    • 如果只打印出最多 100 的所有素数怎么办?你有没有使用你的堆?它的起始条目是 121。在您检查 121 之前不需要它,也不需要以下所有低于 100 的素数条目,这些条目会在您达到 100 时填充您的堆 - 但还不需要。这是文章中代码的一个主要缺陷,您在此处忠实地重新创建了该代码。仅当看到候选人以使其等于该素数的平方时,才应将素数的条目添加到堆中。因此,这段代码的内存需求是 O(n) 来打印 n 个素数,而不是 O(sqrt(n/log(n)))。
    • (contd.) 因为它必须操作比实际需要的大得多的堆,所以它也会变慢。要产生 1,000,000 个素数,您只需检查不超过 550 个素数;但是您的代码将在堆中存储 999,999 个条目。 Melissa O'Neill 在她的code distribution ZIP 文件中解决了这个问题。 Haskellwiki primes page 对此也有自己的看法。
    【解决方案5】:

    您的算法不是埃拉托色尼筛法。您执行试除法(模数运算符)而不是像埃拉托色尼在两千多年前所做的那样交叉倍数。 Here 是对真正筛分算法的解释,下面是我简单直接的实现,它返回一个不超过 n 的素数列表:

    def sieve(n):
        m = (n-1) // 2
        b = [True]*m
        i,p,ps = 0,3,[2]
        while p*p < n:
            if b[i]:
                ps.append(p)
                j = 2*i*i + 6*i + 3
                while j < m:
                    b[j] = False
                    j = j + 2*i + 3
            i+=1; p+=2
        while i < m:
            if b[i]:
                ps.append(p)
            i+=1; p+=2
        return ps
    

    我们只筛选奇数,在 n 的平方根处停止。 j 上看起来很奇怪的计算映射在被筛选的整数 3、5、7、9、... 和 b 中的索引 0、1、2、3、... 之间 位数组。

    您可以在 http://ideone.com/YTaMB 看到这个函数的运行情况,它可以在不到一秒的时间内计算出一百万个素数。

    【讨论】:

    • simple 实现应该类似于this
    【解决方案6】:

    此代码需要 2 秒才能生成小于 10M 的素数 (不是我的,我在 google 上找到的)

    def erat_sieve(bound):
        if bound < 2:
            return []
        max_ndx = (bound - 1) // 2
        sieve = [True] * (max_ndx + 1)
        #loop up to square root
        for ndx in range(int(bound ** 0.5) // 2):
            # check for prime
            if sieve[ndx]:
                # unmark all odd multiples of the prime
                num = ndx * 2 + 3
                sieve[ndx+num:max_ndx:num] = [False] * ((max_ndx-ndx-num-1)//num + 1)
        # translate into numbers
        return [2] + [ndx * 2 + 3 for ndx in range(max_ndx) if sieve[ndx]]
    

    【讨论】:

      猜你喜欢
      • 2018-08-10
      • 1970-01-01
      • 2016-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-25
      • 2019-07-03
      • 1970-01-01
      相关资源
      最近更新 更多