素数性テストの良いところは、素数だけで除算することです。
private static boolean checkMod( int num) {
for (int i : primes){
if( num % i == 0){
return false;
}
}
return true;
}
悪い点は、これまでに見つかったすべての素数、つまり候補よりも小さいすべての素数で除算することです。つまり、100 万未満の最大の素数 999983 を 78497 の素数で割ると、この数が素数であることがわかります。それは大変な作業です。実際、このアルゴリズムで素数に費やされる作業は、100 万に達すると全作業の約 99.9% を占め、上限が高くなるほど大きくなります。そして、そのアルゴリズムはほぼ 2 次n
です。このように素数を見つけるには、約実行する必要があります。
n² / (2*(log n)²)
部門。
簡単な改善は、分割を早期に停止することです。n
を合成数 (つまり、1 と 以外の約数を持つ 1 より大きい数)n
とし、 をd
の約数としn
ます。
ここでd
、 の除数であるn
ことn/d
は、 が整数であり、 : の除数であるn
ことを意味しますn/(n/d) = d
。したがって、自然に のn
除数をペアにグループ化することができ、各除数d
はペア を生じさせ(d, n/d)
ます。
このようなペアには、次の 2 つの可能性があります。
d = n/d
n = d²
、またはを意味しd = √n
ます。
- 2つは異なり、一方は他方よりも小さく、たとえば
d < n/d
. しかし、それはすぐにd² < n
orに変換されd < √n
ます。
したがって、いずれの場合も、除数の各ペアには (少なくとも) を超えないものが含まれます√n
。したがって、が合成数である場合n
、その最小の除数 (1 以外) は を超えません√n
。
したがって、到達したら試行分割を停止できます√n
。
private static boolean checkMod( int num) {
for (int i : primes){
if (i*i > n){
// We have not found a divisor less than √n, so it's a prime
return true;
}
if( num % i == 0){
return false;
}
}
return true;
}
注: これは、昇順で繰り返される素数のリストに依存します。それが言語によって保証されていない場合は、別の方法を使用する必要がありますArrayList
。
100 万未満の最大の素数 999983 の候補の平方根で試行分割を停止すると、1000 未満の 168 の素数で除算するだけで済みます。これは以前よりもはるかに少ない作業です。試行分割を平方根で停止し、素数のみで割ることは、試行分割が可能な限り良好であり、約必要です。
2*n^1.5 / (3*(log n)²)
の場合n = 1000000
、それは約 750 倍です。悪くないですね。
しかし、それはまだあまり効率的ではありません。以下のすべての素数を見つける最も効率的な方法n
はふるいです。簡単に実装できるのは、古典的なエラトステネスのふるいです。これにより、O(n*log log n) 操作で以下の素数が検出n
され、いくつかの機能強化 (いくつかの小さな素数の倍数を事前に考慮から除外する) により、その複雑さを O(n) 操作に減らすことができます。より良い漸近挙動を持つ比較的新しいふるいは、アトキンのふるいです。n
O(n) 操作で、または O(n/log log n) 操作で、いくつかの小さな素数の倍数を除去する拡張機能を使用します。アトキンのふるいは実装がより複雑であるため、エラトステネスのふるいの適切な実装は、アトキンのふるいの素朴な実装よりも優れたパフォーマンスを発揮する可能性があります。同様の最適化レベルの実装では、制限が大きくならない限り (10 10; 実際には、メモリ アクセス パターンが優れているため、エラトステネスのふるいの方がアトキンのふるいよりも優れていることは珍しくありません)。したがって、エラトステネスのふるいから始めることをお勧めします。最適化に向けた誠実な努力にもかかわらず、そのパフォーマンスが満足のいくものではない場合にのみ、アトキンのふるいを掘り下げます。または、自分で実装したくない場合は、他の誰かがすでに真剣に調整している適切な実装を見つけてください。
問題がn番目の素数を見つけることであった、わずかに異なる設定の回答でもう少し詳しく説明しました。多かれ少なかれ効率的な方法のいくつかの実装は、その回答からリンクされています。特に、エラトステネスのふるいの1つまたは2つの使用可能な(あまり最適化されていませんが)実装です。