合并的迭代器产生模糊的结果
Posted
技术标签:
【中文标题】合并的迭代器产生模糊的结果【英文标题】:Merged iterators produce obscure results 【发布时间】:2012-08-23 16:06:41 【问题描述】:我正在尝试使用Sieve of Eratosthenes 算法实现素数生成器。我这样做只是为了尝试使用递归iterator merging 来实现 sifter。
我要做的是:
from itertools import count,islice,groupby
from heapq import merge
def primes3():
p = 2
yield p
sifter = (i*p for i in count(p))
s = next(sifter)
for p in count(p+1):
if p==s: # this p is sieved out
print('s: '.format(s))
s = next(sifter)
else:
yield p # this is prime
print('p: '.format(p))
sifter = (k for k, g in groupby(merge(sifter,(i*p for i in count(p))))) #add this serie to the sifter: p*p, p*(p+1), p*(p+2), ...
print(list(islice(primes3(),10)))
输出是:
p: 3
s: 4
p: 5
p: 6
p: 7
p: 8
p: 9
p: 10
p: 11
s: 12
[2, 3, 5, 6, 7, 8, 9, 10, 11, 13]
要筛选的第一个数字是4
。但下一个是12
,而不是应该的6
。这是为什么?我用以下代码检查了它:
>>> sifter = (i*2 for i in count(2))
>>> next(sifter)
4
>>> sifter = (k for k, g in groupby(merge(sifter,(i*3 for i in count(3)))))
>>> print(list(islice(sifter,20)))
[6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 26, 27, 28, 30, 32, 33, 34]
因此,正如我们所见,在测试条件下,sifter 会产生正确的结果。
我在哪里犯错了?我想这一定是一些我看不到的微不足道的事情。
【问题讨论】:
【参考方案1】:我必须同意,这些东西有时会让人非常困惑(但这是一个很好的谜题!)。
事实证明,当 p
的值发生变化时,您的 sifter
迭代器会发生变化(顺便说一下,我使用的是 python 2.6.5 来测试这个,而不是 python 3,所以打印语法有点不同):
>> p = 2
>> sifter = (i*p for i in count(p))
>> for x in range(3):
>> print next(sifter)
4
6
8
>>> # now lets see what happens when we change p
>>> p = 3
>>> for x in range(3):
>>> print next(sifter)
15
18
21
因此,一旦 p 更新,迭代器的 i*p
部分就会使用 p 的 new 进行评估。 p 确实在您的主循环中更新,这就是您没有得到预期结果的原因。
有一个简单的方法可以解决这个问题:创建一个函数来生成 sifter 迭代器,使得迭代器不受 p 的限制:
def gensifter(x):
return (i*x for i in count(x))
并将其放入您的代码中(同样,我将其转换为 python 2.6.5):
def primes3():
p = 2
yield p
sifter = gensifter(p) # <-- changed!
s = next(sifter)
for p in count(p+1):
#print '(testing p = %d\ts = %d)' % (p, s)
if p==s: # this p is sieved out
print 's:', s
s = next(sifter)
else:
yield p # this is prime
print 'p:', p
sifter = (k for k, g in groupby(merge(sifter,gensifter(p)))) # <-- changed!
现在让我们看看结果:
>>> print list(islice(primes3(), 10))
p: 3
s: 4
p: 5
s: 6
p: 7
s: 8
s: 9
s: 10
p: 11
s: 12
p: 13
s: 14
s: 15
s: 16
p: 17
s: 18
p: 19
s: 20
s: 21
s: 22
p: 23
s: 24
s: 25
s: 26
s: 27
s: 28
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
大量的素数!
【讨论】:
谢谢!如果没有你的帮助,我怀疑我会发现问题所在! 还有一个问题。除了创建一个特殊函数(因为函数调用有点慢,据我所知)之外,还有另一种方法可以为 p 创建本地范围吗? 我找到了除了gensifter
函数之外的另一种解决方案:count(p*p,p)
(因为第一个参数是起始值,第二个是步长。
好问题,我真的不知道最有效的方法是什么,但是使用额外的功能并没有太大的开销。算法的真正瓶颈是迭代器随时间的递归嵌套,最终导致RuntimeError: maximum recursion depth exceeded
。在这发生之前,我没有时间准备 2221
我采用的生成素数的方法比这里已经实现的方法效率低得多:code.activestate.com/recipes/117119 另外,我的解决方案导致 *** 的值很少(1000 足以导致溢出):ideone.com/bIZzn以上是关于合并的迭代器产生模糊的结果的主要内容,如果未能解决你的问题,请参考以下文章