项目欧拉第10个问题: 找出200万以下所有素数的总和。 我想出了两个解决方案:
使用发电机功能
def gen_primes():
n = 2
primes = []
while True:
for p in primes:
if n % p == 0:
break
else:
primes.append(n)
yield n
n += 1
flag = True
sum=0
p=gen_primes()
while flag:
prime=p.__next__()
if prime > 2000000:
flag = False
else:
sum+=prime
print(sum)
W / O GENERATOR
def prime(number):
if number ==1:
return -1
else:
for a in (range(1,int(number**0.5)+1))[::2]:
if number%a==0 and a!=1:
return False
else:
return True
count=2
for i in (range(1,2000000))[::2]:
if prime(i)==True and i!=1:
count+=i
else:
continue
print(count)
令人惊讶的是,后者(w / o g) 7.4秒而前者(使用g)需要 10分钟 !!!
为什么会这样?认为由于步骤较少,发电机的性能会更好。
答案 0 :(得分:1)
您的生成器正在执行许多不必要的工作。当它只需要检查直到n
的平方根的素数时,它将检查直到n
的所有素数。这是生成器函数的修改版本,其中删除了不必要的工作:
from time import time
t = time()
def gen_primes():
primes = []
yield 2
n = 3
while True:
is_prime = True
upper_limit = n ** .5
for p in primes:
if n % p == 0:
is_prime = False
break
elif p > upper_limit:
break
if is_prime:
primes.append(n)
yield n
n += 2 # only need to check divisibility by odd numbers
sum = 0
for prime in gen_primes():
if prime > 2_000_000:
break
else:
sum += prime
print("time:", time() - t)
print(sum)
这在我的计算机上需要2.3秒。没有生成器的版本在我的计算机上需要4.8秒。
如果您对查找质数的更有效算法感兴趣,请查看Eratosthenes筛。 https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
答案 1 :(得分:0)
这是您的生成器代码的重做,几乎是非生成器代码的两倍。与@DanielGiger的解决方案(+1)类似,但尝试使其更简单,并且可能使头发更快。它使用多个平方而不是一个平方根(可能会赢也可能不会赢),并且不需要布尔标志:
def gen_primes():
yield 2
primes = [2]
number = 3
while True:
for prime in primes:
if prime * prime > number:
yield number
primes.append(number)
break
if number % prime == 0:
break
number += 2
total = 0
generator = gen_primes()
while True:
prime = next(generator)
if prime > 2_000_000:
break
total += prime
print(total)
这是对非生成器代码的等效重做,该代码还试图变得更简单,并且速度更快。我的经验法则是处理2个及以下的特殊情况,然后写一个干净的奇数除数校验:
def is_prime(number):
if number < 2:
return False
if number % 2 == 0:
return number == 2
for divisor in range(3, int(number**0.5) + 1, 2):
if number % divisor == 0:
return False
return True
total = 2
for i in range(3, 2_000_000, 2):
if is_prime(i):
total += i
print(total)
此外,您的代码生成整个范围的数字,然后丢弃偶数-我们可以使用range()
的第三个参数来仅生成奇数。
最后,使用生成器与两个解决方案的总体性能没有任何关系。您可以将第二种解决方案重写为生成器:
def gen_primes():
yield 2
number = 3
while True:
for divisor in range(3, int(number**0.5) + 1, 2):
if number % divisor == 0:
break
else: # no break
yield number
number += 2
两种解决方案之间的区别是,一种仅使用质数作为试验除数(以存储成本为代价),另一种使用奇数(伪素数)作为试验除数(无存储成本)。在时间上,少做试验的人会获胜。