1. 概要

このチュートリアルでは、Javaで乱数を生成するさまざまな方法について説明します。

2. JavaAPIの使用

Java APIは、目的を達成するためのいくつかの方法を提供します。 それらのいくつかを見てみましょう。

2.1. java .lang.Math

Mathクラスのrandomメソッドは、0.0(包括的)から1.0(排他的)の範囲のdouble値を返します。定義された特定の範囲の乱数を取得するためにどのように使用するかを見てみましょう。 minおよびmaxによる:

int randomWithMathRandom = (int) ((Math.random() * (max - min)) + min);

2.2. java .util.Random

Java 1.7より前は、乱数を生成する最も一般的な方法はnextIntを使用することでした。 このメソッドを使用するには、パラメーターを使用する場合と使用しない場合の2つの方法があります。 パラメータなしの呼び出しは、ほぼ等しい確率でint値のいずれかを返します。 したがって、負の数になる可能性が非常に高くなります。

Random random = new Random();
int randomWithNextInt = random.nextInt();

netxInt呼び出しをboundパラメーターとともに使用すると、次の範囲内の数値が取得されます。

int randomWintNextIntWithinARange = random.nextInt(max - min) + min;

これにより、0(包括的)からパラメーター(排他的)までの数値が得られます。 したがって、バインドされたパラメーターは0より大きくなければなりません。そうでない場合、 java.lang.IllegalArgumentExceptionが発生します。

Java 8では、java.util.stream.IntStreamを返す新しいintメソッドが導入されました。それらの使用方法を見てみましょう。

パラメータのないintsメソッドは、int値の無制限のストリームを返します。

IntStream unlimitedIntStream = random.ints();

単一のパラメーターを渡して、ストリームサイズを制限することもできます。

IntStream limitedIntStream = random.ints(streamSize);

そしてもちろん、生成される範囲の最大値と最小値を設定できます。

IntStream limitedIntStreamWithinARange = random.ints(streamSize, min, max);

2.3. java .util.concurrent.ThreadLocalRandom

Java 1.7リリースでは、ThreadLocalRandomクラスを介して乱数を生成する新しいより効率的な方法が提供されました。 これには、ランダムクラスとの3つの重要な違いがあります。

  • ThreadLocalRandomの新しいインスタンスを明示的に開始する必要はありません。 これにより、無駄なインスタンスを大量に作成し、ガベージコレクターの時間を無駄にするという間違いを回避できます。
  • ThreadLocalRandom のシードを設定できないため、実際の問題が発生する可能性があります。 シードを設定する必要がある場合は、この方法で乱数を生成することは避けてください。
  • ランダムクラスはマルチスレッド環境でうまく機能しません

それでは、それがどのように機能するかを見てみましょう。

int randomWithThreadLocalRandomInARange = ThreadLocalRandom.current().nextInt(min, max);

Java 8以降では、新しい可能性があります。 まず、nextIntメソッドには2つのバリエーションがあります。

int randomWithThreadLocalRandom = ThreadLocalRandom.current().nextInt();
int randomWithThreadLocalRandomFromZero = ThreadLocalRandom.current().nextInt(max);

次に、さらに重要なことに、intsメソッドを使用できます。

IntStream streamWithThreadLocalRandom = ThreadLocalRandom.current().ints();

2.4. java .util.SplittableRandom

Java 8は、非常に高速なジェネレーターであるSplittableRandomクラスももたらしました。

JavaDocでわかるように、これは並列計算で使用するためのジェネレーターです。 インスタンスはスレッドセーフではないことを知っておくことが重要です。 したがって、このクラスを使用するときは注意が必要です。

nextIntおよびintsメソッドを使用できます。 nextInt を使用すると、次の2つのパラメーター呼び出しを使用して、上限と下限の範囲を直接設定できます。

SplittableRandom splittableRandom = new SplittableRandom();
int randomWithSplittableRandom = splittableRandom.nextInt(min, max);

この使用方法では、maxパラメーターがminより大きいことを確認します。 それ以外の場合は、IllegalArgumentExceptionが発生します。 ただし、正の数と負の数のどちらで作業するかはチェックされません。 したがって、パラメータのいずれかが負になる可能性があります。 また、1パラメーターと0パラメーターの呼び出しも利用できます。 これらは、前に説明したのと同じように機能します。

intsメソッドも利用できます。 これは、int値のストリームを簡単に取得できることを意味します。 明確にするために、制限付きまたは無制限のストリームを選択できます。 限られたストリームの場合、番号生成範囲の上限と下限を設定できます。

IntStream limitedIntStreamWithinARangeWithSplittableRandom = splittableRandom.ints(streamSize, min, max);

2.5. java .security.SecureRandom

セキュリティに敏感なアプリケーションがある場合は、SecureRandomの使用を検討する必要があります。これは暗号的に強力なジェネレーターです。 デフォルトで構築されたインスタンスは、暗号的にランダムなシードを使用しません。 したがって、次のいずれかを行う必要があります。

  • シードを設定します—その結果、シードは予測できなくなります
  • java.util.secureRandomSeedシステムプロパティをtrueに設定します

このクラスは、java.util.Randomから継承します。 したがって、上記で見たすべての方法を利用できます。 たとえば、 int 値のいずれかを取得する必要がある場合は、パラメーターなしでnextIntを呼び出します。

SecureRandom secureRandom = new SecureRandom();
int randomWithSecureRandom = secureRandom.nextInt();

一方、範囲を設定する必要がある場合は、boundパラメーターを使用して呼び出すことができます。

int randomWithSecureRandomWithinARange = secureRandom.nextInt(max - min) + min;

パラメータがゼロより大きくない場合、この使用方法ではIllegalArgumentExceptionがスローされることを覚えておく必要があります。

3. サードパーティAPIの使用

これまで見てきたように、Javaには、乱数を生成するための多くのクラスとメソッドが用意されています。 ただし、この目的のためのサードパーティAPIもあります。

それらのいくつかを見ていきます。

3.1. org.apache.commons.math3.random.RandomDataGenerator

ApacheCommonsプロジェクトのcommonsmathematicsライブラリには多くのジェネレーターがあります。 最も簡単で、おそらく最も便利なのは、RandomDataGeneratorです。 ランダム生成にはWell19937cアルゴリズムを使用します。 ただし、アルゴリズムの実装を提供することはできます。

使い方を見てみましょう。 まず、依存関係を追加する必要があります。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.6.1</version>
</dependency>

commons-math3 の最新バージョンは、 MavenCentralにあります。

次に、それを使用して作業を開始できます。

RandomDataGenerator randomDataGenerator = new RandomDataGenerator();
int randomWithRandomDataGenerator = randomDataGenerator.nextInt(min, max);

3.2. it.unimi.dsi.util.XoRoShiRo128PlusRandom

確かに、これは最速の乱数ジェネレーターの実装の1つです。 ミラノ大学の情報科学部で開発されました。

このライブラリは、 MavenCentralリポジトリでも利用できます。 それでは、依存関係を追加しましょう。

<dependency>
    <groupId>it.unimi.dsi</groupId>
    <artifactId>dsiutils</artifactId>
    <version>2.6.0</version>
</dependency>

このジェネレーターは、java.util.Randomから継承します。 ただし、 JavaDoc を見ると、nextIntメソッドを使用する方法が1つしかないことがわかります。 とりわけ、このメソッドは、ゼロパラメーターと1パラメーターの呼び出しでのみ使用できます。 その他の呼び出しはすべて、java.util.Randomメソッドを直接使用します。

たとえば、範囲内のランダムな数値を取得する場合は、次のように記述します。

XoRoShiRo128PlusRandom xoroRandom = new XoRoShiRo128PlusRandom();
int randomWithXoRoShiRo128PlusRandom = xoroRandom.nextInt(max - min) + min;

4. 結論

乱数生成を実装する方法はいくつかあります。 ただし、最善の方法はありません。 したがって、ニーズに最も適したものを選択する必要があります。

完全な例は、GitHubにあります。