これは宿題ではありません、私は興味があります。
ここではINFINITEがキーワードです。
for p in primes()
として使いたいです。これはHaskellの組み込み関数だと思います。
したがって、答えは「Just do a Sieve」ほど単純ではありません。
まず第一に、あなたは連続する素数がいくつ消費されるかわかりません。さて、一度に100個を作り出すことができるとしましょう。同じSieveアプローチと素数式の頻度を使用しますか?
私は非並行アプローチを好みます。
読んで(そして書いてくれてありがとう;))!
OPはefficientの実装を要求するので、David Eppstein/Alex Martelliが active state 2002 code を大幅に改善しています(ここで- 彼の答え ):候補に正方形が表示されるまで、素数の情報を辞書に記録しないでください。生成されたn個の素数に対して、スペースの複雑さをO(n)ではなくO(sqrt(n))に下げます(- π(sqrt(n log n)) 〜2 sqrt(n log n)/ log(n log n)〜2 sqrt(n/log n))。その結果、時間の複雑さも改善されます。つまり、 それはより速く実行されます 。
「スライディングシーブ」を各基本素数の現在の倍数のディクショナリとして(つまり、現在のプロダクションポイントの平方根より下に)、それらのstep値とともに作成します。
from itertools import count
# ideone.com/aVndFM
def postponed_sieve(): # postponed sieve, by Will Ness
yield 2; yield 3; yield 5; yield 7; # original code David Eppstein,
sieve = {} # Alex Martelli, ActiveState Recipe 2002
ps = postponed_sieve() # a separate base Primes Supply:
p = next(ps) and next(ps) # (3) a Prime to add to dict
q = p*p # (9) its sQuare
for c in count(9,2): # the Candidate
if c in sieve: # c's a multiple of some base prime
s = sieve.pop(c) # i.e. a composite ; or
Elif c < q:
yield c # a prime
continue
else: # (c==q): # or the next base prime's square:
s=count(q+2*p,2*p) # (9+6, by 6 : 15,21,27,33,...)
p=next(ps) # (5)
q=p*p # (25)
for m in s: # the next multiple
if m not in sieve: # no duplicates
break
sieve[m] = s # original test entry: ideone.com/WFv4f
(ここの古い元のコードは、以下の the answer によって Tim Peters で見られるように変更を組み込むように編集されています)。関連する議論については this も参照してください。
同様 2-3-5-7ホイール ベースのコード 実行2.15倍高速 (これは3/2 * 5/4 * 7/6 = 2.1875
の理論的改善に非常に近い)です。
後世のために、ここにPython 3のWill Nessの美しいアルゴリズムを書き直します。いくつかの変更が必要です(イテレータには.next()
メソッドがなくなりましたが、新しいnext()
組み込み関数)その他の変更はおもしろいものです(新しい_yield from <iterable>
_を使用すると、元の4つのyield
ステートメントが置き換えられます。もっと読みやすくするためです(私は使いすぎのファンではありません;-) 1文字の変数名)。
オリジナルよりも大幅に高速ですが、アルゴリズム上の理由ではありません。スピードアップのほとんどは、元のadd()
関数を削除し、代わりにインラインで実行したためです。
_def psieve():
import itertools
yield from (2, 3, 5, 7)
D = {}
ps = psieve()
next(ps)
p = next(ps)
assert p == 3
psq = p*p
for i in itertools.count(9, 2):
if i in D: # composite
step = D.pop(i)
Elif i < psq: # prime
yield i
continue
else: # composite, = p*p
assert i == psq
step = 2*p
p = next(ps)
psq = p*p
i += step
while i in D:
i += step
D[i] = step
_
これはもともと私のコードではありませんが、投稿する価値はあります。オリジナルはここにあります: http://code.activestate.com/recipes/117119/
def gen_primes():
D = {}
q = 2 # first integer to test for primality.
while True:
if q not in D:
# not marked composite, must be prime
yield q
#first multiple of q not already marked
D[q * q] = [q]
else:
for p in D[q]:
D.setdefault(p + q, []).append(p)
# no longer need D[q], free memory
del D[q]
q += 1
それはジェネレーターなので、他のように使用します。
primes = gen_primes()
for p in primes:
print p
デスクトップで100万の素数を生成してセットに入れるには、1.62秒かかります。
segmented sieveを実行します。セグメントのサイズは、使用可能なメモリまたはビットセットの最大サイズによって決まります。
各セグメントについて、ある間隔[n; n + segment_size)をビットセットとして設定し、すべての素数を上限の平方根より下にふるいにかけます。
密な数のセットを扱うため、ビットセットを使用すると、ハッシュテーブルやツリーデータ構造よりもメモリの使用量が少なくなります。
それを行う別の方法:
import itertools
def primeseq():
prime = [2]
num = 0
yield 2
for i in itertools.count(3, 2):
is_prime = True
for num in prime:
if i % num == 0:
is_prime = False
break
Elif num ** 2 > i:
break
if is_prime:
prime.append(i)
yield i
そして別の答えは、ここのerat3
の答えよりもメモリ効率が良い:
import heapq
def heapprimegen():
hp= []
yield 2
yield 3
cn= 3
nn, inc= 3, 6
while 1:
while cn < nn:
yield cn
heapq.heappush(hp, (3*cn, 2*cn))
cn+= 2
cn= nn+2
nn, inc= heapq.heappushpop(hp, (nn+inc, inc))
辞書ではなく素数のヒープ(リスト)を維持します。明らかに速度が落ちます。
これは複雑なヒープベースの実装です。これは他のヒープベースの実装ほど高速ではありませんが(私の別の回答の速度の比較を参照)、使用するメモリははるかに少なくなります。
この実装では、同じ数の要素を含む2つのヒープ(tuとwv)を使用します。各要素はintペアです。 _q**2
_(q
は素数)までのすべての素数を見つけるために、各ヒープには最大で2*pi(q-1)
要素が含まれます。ここで、pi(x)
はx
以下の正の素数の数。したがって、整数の総数はせいぜい4*pi(floor(sqrt(n)))
です。 (半分の量をヒープにプッシュすることにより、メモリ上で2の因数を得ることができますが、これによりアルゴリズムが遅くなります。)
上記の他のdictおよびヒープベースのアプローチ(たとえば、erat2b、heap_prime_gen_squaresおよびheapprimegen)は、約2 * pi(n)の整数を格納します。これは、それらが素数を見つけるたびにヒープまたはdictを拡張するためです。比較として:1_000_000素数を見つけるために、この実装は4141未満の整数を格納し、他の実装は1_000_000整数より多くを格納します。
_import heapq
def heap_prime_gen_smallmem():
yield 2
yield 3
f = 5
fmar3 = 2
q = 7
q6 = 7 * 6
qmar3 = 4
tu = [(25, 30), (35, 30)]
vw = [(25, 30), (35, 30)]
while True:
qmar3 += 2
if qmar3 == 6:
qb = q + 4
q6b = q6 + 24
qmar3 = 2
else:
qb = q + 2
q6b = q6 + 12
if q < tu[0][0]:
d = q * q
while f < d:
a, b = vw[0]
if f < a:
yield f
else:
a, b = vw[0]
heapq.heapreplace(vw, (a + b, b))
a, b = vw[0]
while f >= a:
heapq.heapreplace(vw, (a + b, b))
a, b = vw[0]
fmar3 += 2
if fmar3 == 6:
f += 4
fmar3 = 2
else:
f += 2
c = q * qb
heapq.heappush(tu, (d, q6))
heapq.heappush(tu, (c, q6))
heapq.heappush(vw, (d, q6))
heapq.heappush(vw, (c, q6))
else:
a, b = tu[0]
heapq.heapreplace(tu, (a + b, b))
a, b = tu[0]
while q >= a:
heapq.heapreplace(tu, (a + b, b))
a, b = tu[0]
q = qb
q6 = q6b
_
これはシンプルですが、dictの代わりにヒープを使用したもので、ひどく遅くはありません。
import heapq
def heap_prime_gen_squares():
yield 2
yield 3
h = [(9, 6)]
n = 5
while True:
a, b = h[0]
while n < a:
yield n
heapq.heappush(h, (n * n, n << 1))
n += 2
heapq.heapreplace(h, (a + b, b)) # Replace h[0], which is still (a, b).
最初の100万素数のユーザー時間の速度測定(数値が小さいほど良い):
したがって、dictベースのアプローチが最速のようです。
これは、Python2で記述された非常に高速な無限ジェネレーターですが、Python3に簡単に調整できます。これを使用して10 ** 9までの素数を追加するには、以下を使用します。
from itertools import takewhile
from functools import partial
from operator import gt
print (sum(takewhile(partial(gt, 10**9), prime_gen_inf())))
これはセグメント化されたふるいであり、Will Nessのアルゴリズムより高速ですが明らかにエレガントではありません。
from operator import mul
from functools import reduce
def prod(x): return reduce(mul, x, 1)
def build_sieve(wheel):
w = prod(wheel)
w_phi = prod([p-1 for p in wheel])
rems = [a for a in range(w) if all(a % p for p in wheel)]
assert len(rems) == w_phi
inv = {a:pow(a, w_phi - 1, w) for a in rems}
try:
known_p = wheel + rems[1 : rems.index(rems[1]*rems[1])]
except ValueError:
known_p = wheel + rems[1:]
return wheel, w, w_phi, rems, inv, known_p
#Adjust the chunk variable based on your computer's architecture.
#
#Adjust the line with #! if you don't need "true" infinite. If you don't need
#primes larger than 1<<32, use array('H', []), if 1<<64 use 'L', if 1<<128 (in
#Python3) use 'Q', otherwise use empty list [].
#To save memory, comment out the lines with #*, and uncomment the commented-out
#lines
import itertools
from itertools import islice, count, compress, izip
chain_f = itertools.chain.from_iterable
from array import array
def prime_gen_inf(chunk=250000, sieve_info = build_sieve([2,3,5,7])):
""" Indefinitely yields primes """
wheel, w, w_phi, rems, inv, known_p = sieve_info
for p in known_p: yield p
new_n = 0;
while True:
size = min(chunk, (p * p - new_n) / w)
sieve = bytearray([1]) * size * w_phi
n, new_n = new_n, new_n + size * w
if not n:
zero = bytearray([0])
seen = len(known_p) - len(wheel) + 1
sieve[:seen:1] = zero * seen
p_gen = islice(prime_gen_inf(), len(wheel), None)
new_p = next(p_gen)
ps = [] #! array('H', [])
p_invs = bytearray([]) #*
while new_p * new_p < new_n:
ps.append(new_p)
p_invs.append(inv[new_p % w]) #*
new_p = next(p_gen)
for p, p_inv, modp in izip(ps, p_invs, [-n % p for p in ps]): #*
s = [(modp + p * (p_inv * (r - modp) % w)) / w for r in rems] #*
#for p in ps:
# s = [(-n%p + p * (inv[p%w] * (r - -n%p) % w)) / w for r in rems]
for i, start in enumerate(s):
slice_size = ((size - start - 1) / p + 1)
sieve[i + start * w_phi :: p * w_phi] = zero * slice_size
for p in compress(chain_f(izip(*[count(n+r, w) for r in rems])), sieve):
yield p
Haskellでの実行方法に少し忠実なジェネレーターを以下に示します。既知の素数のコンポジットに対してフィルタリングし、残りの素数をリストに追加します。
def gen_primes():
primes = []
i = 2
while True:
prime = True
for p in primes:
if not (i % p):
prime = False
break
if prime:
yield i
primes.append(i)
i += 1
私は数回前に無限素数ジェネレータについての記事を書きました:
http://stacktrace.it/2008/01/progetto-eulero-problema-3/
それはイタリア語ですが、Googleを使用して厄介な翻訳があるかもしれません: http://tinyurl.com/yzpyeom
私は投稿が古いことを知っていますが、私はこの質問に出くわしました...次のコードは非常に単純なアイデアに基づいています:エラトステネスのふるいを増やすことです。この解決策は、ここにある最良の解決策よりも本当に遅いですが、理解しやすく、読みやすいように設計されています...
ふるいの結果を格納するために整数を使用しました。バイナリ形式では、整数は_0
_ sおよび_1
_ s、_0
_のリストで、i
はi
が素数でない場合、_1
_は素数である可能性があります。必要な無限大は、Python 3つの整数が無限であるという事実の結果です。
_def primes():
container, size = 1 << 2, 3 # we start with 0b100 (from right to left: 0 and 1 are not primes, 2 is
last_prime = 1
while True:
prime = next((j for j in range(last_prime+1, size) if container & 1 << j), None) # find the next prime
while not prime:
container, size = expand(container, size, 2**16) # add 65536 cells and sieve the container
prime = next((j for j in range(last_prime+1, size) if container & 1 << j), None)
yield prime
last_prime = prime
_
コンテナを拡張するには?コンテナーの左側に(バイナリ形式で)一連の_1
_ sを追加し、ふるいにかけるだけです。これは、標準的なふるいと同じですが、若干の違いがあります。標準のふるいで、素数のi
を見つけた場合、_i*i
_でセルを交差し始め、i
のステップが追加されます。
ここでは、これはコンテナの最初の部分に対して行われた可能性があります。 _i*i
_よりも遠い場合は、コンテナーの新しい部分の最初から開始する必要があります。
_def expand(container, size, n):
new_size = size + n
container += (1 << (new_size + 1) - 1) - (1 << size) # add n 1's
for i in range(2, new_size):
if container & (1 << i): # i is a prime
t = sum(1 << j for j in range(max(i, size // i)*i, new_size, i)) # set 1 for all mutiple
container &= ~t # cross the cells
return container, new_size
_
100万の素数をテストします。
_import itertools
assert 78498 == len(list(itertools.takewhile(lambda p: p<1000000, primes())))
_