今天我解决了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)
答案 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
此代码的两个优化:
i
的初始值可以设置为value*value
== value**2
修改强>
虽然我希望我的答案有助于解释未来访客的筛网操作,但如果您正在寻找非常快速的筛选实施,请参阅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 Eratosthenes和this video。
答案 3 :(得分:0)
这个答案使用Sieve of Erastothenes方法标记非素数(这是marked
列表的用途),然后经过并只添加尚未标记的值。有关详细信息,请参阅维基百科文章。