n个数下面的素数之和需要说明

时间:2013-06-25 04:06:02

标签: python algorithm primes

今天我解决了Project Euler中给出的问题,它是problem number 10并且我的python程序花了 7小时来显示结果。 但是在该论坛中,一个名为 lassevk 的人为此发布了解决方案,并且只用了 4秒。 我不可能在该论坛上发布这个问题,因为它不是一个讨论论坛。 所以,如果你想把这个问题标记为非建设性,请考虑一下。

marked = [0] * 2000000
value = 3
s = 2
while value < 2000000:
    if marked[value] == 0:
        s += value
        i = value
        while i < 2000000:
            marked[i] = 1
            i += value
    value += 2
print s

如果有人理解此代码,请尽可能简单地解释。

这是我用了7小时计算的代码(我想我也使用了以下答案中提到的Eratosthenes Sieve技术的相同逻辑):

import time
start = time.clock()

total = 0
limit = 2000000
KnownPrime = set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 
                  53, 59, 61, 67, 71])
KnownPrime.update(set([73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 
                       131, 137, 139, 149, 151, 157, 163, 167, 173]))
suspected = set(range(2, limit+1)) # list of suspected prime numbers
for p in KnownPrime:
    if p <= limit:
        total += p
        suspected.difference_update(set(range(p, limit+1, p)))

for i in suspected:
    k = i/2
    if k % 2 == 0: k += 1
    PrimeCheck = set(range(k, 2, -2))
    PrimeCheck.difference_update(KnownPrime)
    for j in PrimeCheck:
        if i % j == 0:
            break
    if i % j:
        total += i

print time.clock() - start
print total

所以,任何人都可以告诉我为什么花了那么多时间。

最后我在这里做了我的重构代码。现在它可以在2秒内显示结果。

import math
import __builtin__

sum = __builtin__.sum

def is_prime(num):
    if num < 2: return False
    if num == 2: return True
    if num % 2 == 0: return False
    for i in range(3, int(math.sqrt(num)) + 1, 2):
        if num % i == 0: return False
    return True

def sum_prime(num):
    if num < 2: return 0
    sum_p = 2
    core_primes = []
    suspected = set(range(3, num + 1, 2))
    for i in range(3, int(math.sqrt(num)) + 1, 2):
        if is_prime(i): core_primes.append(i)
    for p in core_primes:
        sum_p += p
        suspected.difference_update(set(range(p, num + 1, p)))
    return sum(suspected) + sum_p

print sum_prime(2000000)

这是visualization

4 个答案:

答案 0 :(得分:4)

问题:

  

找出200万以下所有素数的总和。

这是一个简单的sieve。您应该阅读它,但一般的想法是迭代每个数字 - 如果索引号的值是0,它是素数并且您标记该数字的倍数(因为所有那些倍数必须不< / em>是素数)。忽略它是1(复合)。我将提供一些注释来解释这段代码特别在做什么,

marked = [0] * 2000000     # <- just set up the list
value = 3                  # <- starting at 3 then checking only odds
s = 2                      # <- BUT include 2 since its the only even prime
while value < 2000000:
    if marked[value] == 0: # <- if number at index value is 0 it's prime
        s += value         #    so add value to s (the sum)
        i = value          # <- now mark off future numbers that are multiples of
        while i < 2000000: #    value up until 2mil
            marked[i] = 1  # <- number @ index i is a multiple of value so mark
            i += value     # <- increment value each time (looking for multiples)
    value += 2             # <- only check every odd number
print s

此代码的两个优化:

  1. i的初始值可以设置为value*value == value**2
  2. 可以轻松更改此项以使用长度为100万的列表,因为我们已经知道没有evens是素数
  3. 修改

    虽然我希望我的答案有助于解释未来访客的筛网操作,但如果您正在寻找非常快速的筛选实施,请参阅this question。通过unutbu和Robert William Hanks发布的一些优秀算法进行出色的性能分析!

答案 1 :(得分:1)

代码基本上是使用Sieve of Eratosthenes来查找素数,一旦你拿出跟踪总和的代码,这可能会更清楚:

marked = [0] * 2000000
value = 3
while value < 2000000:
    if marked[value] == 0:
        i = value
        while i < 2000000:
            marked[i] = 1
            i += value
    value += 2

value上升2(因为你知道2以上的所有偶数都不是素数,你可以跳过它们)和任何尚未被标记的value到达它的时间是最好的,因为你已经标记了它下面的所有值的倍数。

答案 2 :(得分:1)

此代码基本上总结了所有素数&lt;使用Eratosthenes Sieve概念的2000000:

标记是一个充满零的巨大数组。

每次该值小于2000000时,检查标记的数组中的值是否已标记。 标记可以被视为将数组位置标记为1.例如,如果要标记值,则将标记数组中该值的位置标记为1(其余均为零)。

接下来,将i设置为该值(i是您用于while循环的值)。 当我小于2000000时,标记该特定值的标记数组。然后用该值递增i。这样做是为了: 如果标记2的所有倍数,则无需在下一次迭代中重复所有这些倍数。 例如。如果你标记所有2的倍数,下一步你可以从3 ^ 2 = 9开始3,因为那时所有较小的倍数都已被标记。

有关详细信息,请参阅Sieve of Eratosthenesthis video

答案 3 :(得分:0)

这个答案使用Sieve of Erastothenes方法标记非素数(这是marked列表的用途),然后经过并只添加尚未标记的值。有关详细信息,请参阅维基百科文章。