M. O'Neill の素晴らしい論文に従って、Python でエラトステネスのふるいの怠惰な無限バージョンをいくつか実装してみました。論文ではより高速に実行されるはずであると主張されているヒープベースのバージョンが、実際には 2 倍以上遅かったことに驚きました。
この論文には 2 つの例が含まれています。1 つは dict に基づいており、私が (Haskell から) 翻訳したものです。
from itertools import count
def dict_sieve():
yield 2
yield 3
candidates = count(5, 2)
composites = {9:{3}} # map composites to their prime factors
for candidate in candidates:
try:
factors = composites.pop(candidate)
except KeyError: # if it's not in the dict, it's prime
yield candidate
composites[candidate**2] = {candidate} # Euler's optimization: start from prime**2
else:
for prime in factors: # go through the prime factors and increment their keys
try:
composites[candidate+prime*2].add(prime) # use prime*2 because we are ignoring evens
except KeyError:
composites[candidate+prime*2] = {prime}
この論文の 2 番目の例では、プライオリティ キューをデータ構造として使用する方法を示しています。また、単純なインクリメントではなく遅延リストを使用しますが、これは公正なテストのために行っていません。itertools.count
(さらに、私は遅延リストにのインスタンスを使用していましたが、実行が少し遅くなることがわかりました)。
from itertools import count
from heapq import heappush, heapreplace
def heap_sieve():
yield 2
yield 3
candidates = count(5,2)
composites = [(9, 3)] # a priority queue of composite/factor pairs, keyed by composite
for candidate in candidates:
prime_flag = True
while composites[0][0] == candidate: # loop because there may be duplicates
prime_flag = False # signal to the if statement below
composite, prime = composites[0]
heapreplace(composites, (composite + prime*2, prime))
if prime_flag:
yield candidate
heappush(composites, (candidate**2, candidate))
ここでは再現されていませんが、制限を下回るすべての素数のリストを生成する「熱心な」バージョンとともに、2つの時間を計りました。
In [44]: from itertools import islice
In [45]: %timeit list(islice(dict_sieve(), 100000))
...: %timeit list(islice(heap_sieve(), 100000))
...: %timeit eager_sieve(1299710) # 1299709 is the 100,000th prime
...:
1 loops, best of 3: 2.12 s per loop
1 loops, best of 3: 4.94 s per loop
1 loops, best of 3: 677 ms per loop
「熱心な」バージョンがはるかに高速であることは驚くべきことではありません。基本的には、メモリ使用量、上限を指定しなければならないという不便さ、および CPU 時間の間のトレードオフです。しかし、紙がより効率的であると主張しているときに、バージョンがはるかに遅いことに驚きました. heapq
私の実装に問題がありますか?それとも、私たち全員が知っているように、口述が超高速でheapq
ある (そして比較的遅い) ということですか?