それぞれ素数をテストする数値のセットを反復処理しないでください。それは信じられないほど遅くなります。あなたが探しているアルゴリズムは、エラトステネスのセグメントふるいと呼ばれます。
エラトステネスのふるいは非常に高速ですが、O(n) のスペースが必要です。これは、連続するセグメントでふるい分けを実行することにより、ふるい素数の O(sqrt(n)) と bitarray の O(1) に減らすことができます。最初のセグメントでは、セグメント内にある各ふるい素数の最小倍数が計算され、ふるい素数の倍数が通常の方法で合成としてマークされます。すべてのふるい素数が使用されると、セグメント内の残りのマークされていない数が素数になります。次に、次のセグメントでは、各ふるい素数の最小倍数は、前のセグメントでふるい分けを終了した倍数であるため、ふるい分けは終了するまで続行されます。
20 のセグメントで 100 から 200 までふるい分ける例を考えてみましょう。5 つのふるい素数は 3、5、7、11、13 です。100 から 120 までの最初のセグメントでは、bitarray には 10 個のスロットがあり、スロット 0 は 101 に対応し、スロット k は 100 + 2k - 1 に対応し、スロット 9 に対応します。セグメント内の最小の 3 の倍数は 105 で、スロット 2 に対応します。スロット 2+3=5 および 5+3=8 も 3 の倍数です。5 の最小の倍数はスロット 2 で 105 であり、スロット 2+5=7 も 5 の倍数です。7 の最小の倍数は 105 です。スロット 2 では、スロット 2+7=9 も 7 の倍数です。
関数 primes は、引数 lo、hi、および delta を取ります。lo と hi は、lo < hi で偶数でなければならず、lo は、ceiling(sqrt(hi)) よりも大きくなければなりません。セグメント サイズはデルタの 2 倍です。長さ m の配列 ps には、sqrt(hi) より小さいふるい素数が含まれます。偶数は無視されるため、2 が削除されます。配列 qs には、対応するふるい素数の現在のセグメント内の最小倍数のふるい bitarray へのオフセットが含まれます。各セグメントの後、lo はデルタの 2 倍進むため、ふるいビット配列のインデックス j に対応する数値は、lo + 2j + 1 です。
function primes(lo, hi, delta)
sieve := makeArray(0..delta-1) # bitarray
# calculate m and ps as described in text
qs := makeArray(0..m-1) # least multiples
for i from 0 to m-1
qs[i] := (-1/2 * (lo + ps[i] + 1)) % ps[i]
while lo < hi
for i from 0 to delta-1
sieve[i] := True
for i from 0 to m-1
for j from qs[i] to delta step ps[i]
sieve[j] := False
qs[i] := (qs[i] - delta) % ps[i]
for i from 0 to delta-1
t := lo + 2*j + 1
if sieve[i] and t < hi
output t
lo := lo + 2*delta
上記の例では、これは素数 (100, 200, 10) と呼ばれます。上記の例では、qs は最初は [2,2,2,10,8] であり、最小の倍数 105、105、105、121、および 117 に対応し、2 番目のセグメントでは [1,2,6, 0,11]、最小の倍数 123、125、133、121、および 143 に対応します。このアルゴリズムは非常に高速です。1 秒以内に数百万の素数を生成できるはずです。
素数を使ったプログラミングについて詳しく知りたい場合は、私のブログでこのエッセイをお勧めします。