Javaでの素数の生成
1. 前書き
このチュートリアルでは、Javaを使用して素数を生成するさまざまな方法を示します。
数が素数であるかどうかを確認したい場合は、その方法についてa quick guideを参照してください。
2. 素数
コア定義から始めましょう。 A prime number is a natural number greater than one that has no positive divisors other than one and itself.
たとえば、1と7はその正の整数因子であるため7は素数ですが、12は1、4、6に加えて除数3と2を持つためではありません。
3. 素数の生成
このセクションでは、特定の値よりも低い素数を効率的に生成する方法を説明します。
3.1. Java 7以前–ブルートフォース
public static List primeNumbersBruteForce(int n) {
List primeNumbers = new LinkedList<>();
for (int i = 2; i <= n; i++) {
if (isPrimeBruteForce(i)) {
primeNumbers.add(i);
}
}
return primeNumbers;
}
public static boolean isPrimeBruteForce(int number) {
for (int i = 2; i < number; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
ご覧のとおり、primeNumbersBruteForceは2からnまでの数値を反復処理し、isPrimeBruteForce()メソッドを呼び出して、数値が素数であるかどうかを確認しています。
このメソッドは、2からnumber-1までの範囲の数値で各数値の分割可能性をチェックします。
If at any point we encounter a number that is divisible, we return false.最後に、その数が前の数のいずれでも割り切れないことがわかった場合、その素数を示すtrueを返します。
3.2. 効率と最適化
以前のアルゴリズムは線形ではなく、O(n ^ 2)の時間計算量を持ちます。 アルゴリズムも効率的ではなく、明らかに改善の余地があります。
isPrimeBruteForce()メソッドの条件を見てみましょう。
数が素数でない場合、この数は2つの因子、つまりaとbに因数分解できます。 number = a * b。 If both a and b were greater than the square root of n, a*b would be greater than n.
そのため、これらの要因の少なくとも1つは数値の平方根以下でなければならず、数値が素数であるかどうかを確認するには、確認対象の数値の平方根以下の要因のみをテストする必要があります。
偶数はすべて2で割り切れるため、素数が偶数になることはありません。
さらに、偶数はすべて2で割り切れるため、素数が偶数になることはありません。
上記のアイデアを念頭に置いて、アルゴリズムを改善しましょう。
public static List primeNumbersBruteForce(int n) {
List primeNumbers = new LinkedList<>();
if (n >= 2) {
primeNumbers.add(2);
}
for (int i = 3; i <= n; i += 2) {
if (isPrimeBruteForce(i)) {
primeNumbers.add(i);
}
}
return primeNumbers;
}
private static boolean isPrimeBruteForce(int number) {
for (int i = 2; i*i < number; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
3.3. Java 8の使い方
Java8イディオムを使用して以前のソリューションを書き直す方法を見てみましょう。
public static List primeNumbersTill(int n) {
return IntStream.rangeClosed(2, n)
.filter(x -> isPrime(x)).boxed()
.collect(Collectors.toList());
}
private static boolean isPrime(int number) {
return IntStream.rangeClosed(2, (int) (Math.sqrt(number)))
.filter(n -> (n & 0X1) != 0)
.allMatch(n -> x % n != 0);
}
3.4. エラトステネスのふるいを使用する
素数を効率的に生成するのに役立つさらに別の効率的な方法があります。これはSieveOfEratosthenesと呼ばれます。 その時間効率はO(n logn)です。
このアルゴリズムの手順を見てみましょう。
-
2からnまでの連続する整数のリストを作成します:(2、3、4、…、n)
-
最初に、pを2、最初の素数に等しくします。
-
pから始めて、pの増分でカウントアップし、リスト内でp自体より大きいこれらの各数値をマークします。 これらの番号は2p、3p、4pなどになります。それらのいくつかはすでにマークされている可能性があることに注意してください
-
マークされていないリストで、pより大きい最初の数値を見つけます。 そのような番号がなかった場合、停止します。 それ以外の場合は、pをこの数(次の素数)と等しくし、手順3から繰り返します。
最後に、アルゴリズムが終了すると、リスト内のマークされていない数字はすべて素数になります。
コードは次のようになります。
public static List sieveOfEratosthenes(int n) {
boolean prime[] = new boolean[n + 1];
Arrays.fill(prime, true);
for (int p = 2; p * p <= n; p++) {
if (prime[p]) {
for (int i = p * 2; i <= n; i += p) {
prime[i] = false;
}
}
}
List primeNumbers = new LinkedList<>();
for (int i = 2; i <= n; i++) {
if (prime[i]) {
primeNumbers.add(i);
}
}
return primeNumbers;
}
3.5. エラトステネスのふるいの実例
n = 30でどのように機能するか見てみましょう。
上記の画像を検討してください。アルゴリズムによって行われるパスは次のとおりです。
-
ループは2で始まるため、2をマークせずに残し、2の約数をすべてマークします。 画像では赤い色でマークされています
-
ループは3に移動するため、3をマークしないままにして、まだマークされていない3の除数をすべてマークします。 画像では緑色でマークされています
-
ループは4に移動し、すでにマークされているので、続行します
-
ループは5に移動するため、5をマークせずに残し、まだマークされていない5の除数をすべてマークします。 画像では紫色でマークされています
-
ループがnの平方根に等しくなるまで、上記の手順を続けます
4. 結論
このクイックチュートリアルでは、「N」値まで素数を生成する方法を示しました。
これらの例の実装はover on GitHubにあります。