【问题标题】:is this primes generator pythonic这是素数生成器 pythonic
【发布时间】:2012-03-06 10:42:26
【问题描述】:

下面的代码是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)

【问题讨论】:

  • 如果你想使用primes=[False,False]+[True]*(n-1),你可以节省一行,同时增加复杂性,你可以优化使用半个筛子,跳过偶数。见stackoverflow.com/a/3035188/464543
  • 在我的机器上测试你的代码是否为 0,1,2,3 而没有 if p*p&lt;=n:... 在我的机器上不需要该行
  • 谢谢,我忘了 [False]*(non positive) = []
  • 查看this question,了解python中用于快速生成素数的许多示例
  • 感谢 Kimvais,我用那里的代码测试了我的代码,我的速度慢了大约 30 倍,但我希望强调“pythonic”而不是速度。

标签: generator python primes


【解决方案1】:

next(next_p) 抛出 StopIteration 错误还不错——这就是生成器在项目用完时总是做的事情!

在迭代列表时更改列表的长度是个坏主意。但是简单地改变内容并没有错。总的来说,我认为这是一个相当优雅的,如果基本的,seive。

一个小观察:当你“划掉”素数的倍数时,如果你想一想,你会发现你不必以p * 2开头。你可以跳到p ** 2

【讨论】:

  • 啊当然跳到 p**2 因为所有较小的素数都已经找到了。我会改变它谢谢
【解决方案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 对我来说意味着清晰、简洁、易读和利用语言的优势。虽然我可以看到您的实现是某种筛子,但我仅从以前使用此类算法的经验中知道这一点。上面的实现我可以直接阅读为可分性的直接测试。


注意: 接口有细微差别,您的实现将产生素数 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)

我称这是最不符合 Python 的,因为如果你以前没有见过这个技巧,你可能会摸不着头脑来弄清楚它是如何工作的!!

【讨论】:

  • 谢谢伙计。是的,一旦 n 远大于 1000,这会变得非常慢,但很容易阅读。
  • 注意:我只是想回答关于pythonic的问题,如果你对python中fastest的方式感兴趣,这个问题已经很好地涵盖了here.
  • 是的,如果我很快需要一些小的素数,或者如果我的内存为 0,我会写这样的东西。我已经用你的想法提交了另一个答案,它不像 pythonic 但效率更高。
【解决方案3】:

这是另一个受@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"

【讨论】:

  • 这里指的get_primes是哪个imp?
  • 我的问题中的那个。如果加上如果 p*p
  • 顺便说一句,您可以将函数对象传递给timeit,而不是字符串,因为您可以省略“setup”,所以编写起来更快。使用 fnuctools.partial 或 lambda 表达式应用参数。
猜你喜欢
  • 1970-01-01
  • 2010-09-18
  • 2013-05-27
  • 2011-08-24
  • 2016-12-09
  • 1970-01-01
  • 2013-03-20
  • 2021-12-30
  • 2020-03-26
相关资源
最近更新 更多