以下代码用于生成质数pythonic吗?
def get_primes(n):
primes=[False,False]+[True]*(n-1)
next_p=(i for i,j in enumerate(primes) if j)
while True:
p=next(next_p)
yield p
primes[p*p::p]=[False]*((n-p*p)//p+1)
注意next(next_p)最终会抛出一个StopIteration错误,它会以某种方式结束函数get_primes。那不好吗?
另请注意,next_p是一个迭代素数的生成器,但是在迭代期间素数会发生变化。这是不好的风格吗?
添加以下if语句使其在第一百万个素数下的时间不到0.25秒:
if p*p<=n:
primes[p*p::p]=[False]*((n-p*p)//p+1)
答案 0 :(得分:3)
next(next_p)
抛出StopIteration
错误并不错 - 这就是当生成器耗尽时生成器始终会执行的操作!
迭代时更改列表的长度是一个坏主意。但是简单地改变内容并没有错。总的来说,我认为这是一个相当优雅,如果基本,seive。
一个小的观察结果:当你“划掉”素数的倍数时,你会发现,如果你稍微考虑一下,你就不必从p * 2
开始。您可以跳到p ** 2
。
答案 1 :(得分:2)
StopIteration
没有任何问题,实际上这是生成器的预期行为。
我想说这个实现更加pythonic(不一定更有效):
def get_primes(n):
"""Generates prime numbers < n"""
return (x for x in xrange(2,n) if all(x % i for i in xrange(2,x)))
对我来说,Pythonic 意味着清晰,简洁,易读,并且充分利用了语言的优势。虽然我可以看到你的实现是某种筛选,但我只知道从那些算法的先前经验。上面的实现我可以直接读作可分性的直接测试。
注意: 接口存在细微差别,您的实现会产生素数&lt; = n而我的实现会产生素数&lt; ñ。显然这可以轻松而轻松地改变(只是在函数体中将n更改为n + 1),但我觉得生成素数更为pythonic,但不包括n更符合方式,比如说,range()
内置作品。
编辑:只是为了乐趣
这是至少 pythonic实现,也可能效率很低:)
def get_primes(n):
import re
return (x for x in xrange(2,n) if re.match(r'^1?$|^(11+?)\1+$', '1' * x) is None)
我认为这是最不可能的pythonic,因为如果你之前没有看过这个技巧,你会在几天前摸不着头脑,弄清楚它是如何工作的!
答案 2 :(得分:0)
这是@wim推动的另一个有点pythonic的解决方案,但你可以看到它比第一种方法稍慢。
def get_primes2(n):
primes=[]
for i in range(2,n+1):
small_primes=(p for p in primes if p*p<=i)
if all(i%p for p in small_primes):
yield i
primes.append(i)
import timeit
print(timeit.timeit("list(get_primes(10**5))",number=5,setup="from __main__ import get_primes")/5.0)
"0.0350940692182945"
print(timeit.timeit("list(get_primes2(10**5))",number=5,setup="from __main__ import get_primes2")/5.0)
"8.226938898658908"