Python 试图解决项目Euler 10的问题,但代码需要*大量*时间来显示输出

Python 试图解决项目Euler 10的问题,但代码需要*大量*时间来显示输出,python,python-2.7,pypy,Python,Python 2.7,Pypy,: 低于10的素数之和为2+3+5+7=17 求200万以下所有素数之和 我认为我的代码中没有任何错误。但给出答案确实需要很多时间。我尝试过使用PyPy,因为我听说它比CPython解释器快,但仍然不行 代码如下: #Implementation of Sieve of Eratosthenes def prime_sieve(limit): primes = range(2, limit) for i in primes: for j in range(2, p

:

低于10的素数之和为2+3+5+7=17

求200万以下所有素数之和

我认为我的代码中没有任何错误。但给出答案确实需要很多时间。我尝试过使用PyPy,因为我听说它比CPython解释器快,但仍然不行

代码如下:

#Implementation of Sieve of Eratosthenes
def prime_sieve(limit):
    primes = range(2, limit)
    for i in primes:
        for j in range(2, primes[-1]):
            try:
                primes.remove(i*j)
            except ValueError:
                pass

    return primes;


answer = 0

for x in prime_sieve(2000000):
    answer += x

print "Answer: %d." % answer
raw_input()
问题是:

primes.remove(i*j)
.remove在大型列表上调用时效率非常低,因为它首先必须遍历整个列表以确定值存在的位置(如果有的话),然后必须再次遍历列表的一部分以将删除元素后的所有元素向下移动一个位置

这里还有其他方法可以使用数据结构—使用列表的其他方法,以及更高效的其他数据结构


最后:您的代码在迭代的同时修改素数,这就是素数中的i所做的。这通常被认为是一件坏事,因为在迭代过程中修改某些内容可能是未定义的行为。

一个更有效的想法是:

您可以从列表开始:

[0,1,2,3,4,5,6,7,8,9,10]
您希望将每个非素数元素都设置为0,并保留素数

将0和1设置为零,因为它们不是素数。从现在起,你需要做这两件事 步骤:

1找到你还没有考虑过的最小素数,我们称之为n

2将每n个元素设置为0,但不设置为n,因为它们是n的倍数

例如:将0和1设置为0s后:

[0,0,2,3,4,5,6,7,8,9,10]
您没有考虑的最小素数是2,因此您将每秒钟的元素设置为0,但不是2:

[0,0,2,3,0,5,0,7,0,9,0]
你没有考虑过的最小素数是3,所以你把每三个元素都设为0,但不是3,以此类推

[0,0,2,3,0,5,0,7,0,0,0]
另外请注意,您不必对每个素数都这样做,一旦素数达到sqrtlimit,您就可以停止,因为您知道所有非素数都已设置为零

例如,在这个例子中,10limit的平方根是3.162,这意味着当我们到达5时,我们不需要做任何事情,我们在这一点上完成了。但为什么呢?我们使用每个素数将其倍数设置为零,因为这些倍数不是素数;但是,由于5大于10的平方根,因此5的任何倍数都必须是小于5的数字的倍数,因此已经设置为0


假设我们的初始范围是从20到20。20的平方根小于5,所以我们不需要检查5,因为5:5*2=10,5*3=15,5*2*2=20的所有倍数都是较小素数的倍数,我们已经将它们设置为0。

素数筛的正确数据结构是一个按整数值索引的位集。Python没有这些内置的,但是由于您的限制很小,只有200万个,一个常规的整数列表应该适合内存,即使它的浪费是30倍或更多,它将需要大约9 MB,而C中的等效位集需要250 KB

提高速度的重要一点是,除非通过直接索引,否则永远不要访问数组,这样就不会删除/删除。此外,将筛的外循环限制为sqrtlimit,并将循环前进到下一个素数,而不是下一个值

所以类似这样的东西应该很快,在我的旧机器上用香草Python 2.7大约需要2秒钟

import math, sys

def prime_sieve(limit):
    # Mark everything prime to start
    primes = [1 for x in xrange(limit)]
    primes[0] = 0
    primes[1] = 0

    # Only need to sieve up to sqrt(limit)
    imax = int(math.sqrt(limit) + 1)

    i = 2
    while (i < imax):
        j = i + i
        while j < limit:
            primes[j] = 0
            j += i

        # Move i to next prime
        while True:
           i += 1
           if primes[i] == 1:
               break

    return primes

s = prime_sieve(2000000)
print(sum(i for i in xrange(len(s)) if s[i] == 1))

下面是一个简单版本的埃拉托什尼筛,用于计算总和,而不是形成一个小于n的素数列表:


有更好的方法进行筛选,但上述功能足以解决该项目的欧拉问题;它应该在大约一秒钟内返回总数。如果您对使用素数编程感兴趣,我在我的博客上适度推荐。

这不是非常有效的筛选实现,remove会尝试查找列表中的第一个元素。尝试用1和0的列表来实现它。另外,你们可以重新考虑你们的内部循环的范围和步骤。我想在其他人的评论中添加一个小提示:使用xrange而不是range。Xrange会更快,这不是主要问题,但会有所帮助。作为参考,此算法在1.46秒内运行prime_sieve5000,PyPy 2.1,在28.1秒内运行CPython 2.7。由于在两个嵌套循环中删除,它位于^3上,因此每增加~25%,它的运行时间就会加倍;e、 g.运行prime_sieve4000的速度是prime_sieve10000的两倍,运行prime_sieve10000的速度慢8倍。即使PyPy的速度提高了20倍也不能帮助你计算出这个素数200万:宇宙的生命只占这个时间的一小部分,可以忽略不计。嗯,你能解释一下最后一部分吗,也就是说,注意,你不必对每一个素数都这样做,一旦素数达到sqrtlimit,你就可以停下来,因为你知道所有的非素数都被设置为零。@Stormboy,我补充了一个解释,希望能有所帮助。如果我真的需要改进,请告诉我
    def isPrime(n):
        if n < 2: return "Neither prime, nor composite"
        for i in range(2, int(n**0.5) + 1):
            if n % i == 0:
                return False
        return True



 def sumPrime():
        sumT = 0
        for i in range(2,2000000):
            if(isPrime(i)):
                sumT = sumT + i
        return sumT
速度,检查:
    def isPrime(n):
        if n < 2: return "Neither prime, nor composite"
        for i in range(2, int(n**0.5) + 1):
            if n % i == 0:
                return False
        return True



 def sumPrime():
        sumT = 0
        for i in range(2,2000000):
            if(isPrime(i)):
                sumT = sumT + i
        return sumT