Javaにおける乱数生成の手法と実装

Math.random()メソッド

このメソッドは0から1の範囲のdouble型の乱数を生成します。具体的には0 ≤ random ≤ 1の値を取ります。

        for (int counter = 0; counter < 5; counter++) {
            System.out.println(Math.random());
        }

内部実装の仕組み

最初にこのメソッドが呼び出された際、実際にはnew java.util.Random()のように擬似乱数生成器が作成されます。以降のすべての呼び出しでは、この生成された擬似乱数生成器が使用されます。

    public static double random() {
        return RandomNumberGeneratorHolder.generator.nextDouble();
    }
    private static final class RandomNumberGeneratorHolder {
        static final Random generator = new Random();
    }
* <p>このメソッドは複数スレッドでの適切な使用を可能にするために
* 正しく同期されています。ただし、多くのスレッドが高速で
* 擬似乱数を生成する必要がある場合、各スレッドが独自の
* 擬似乱数生成器を持つことで競合を軽減できます。

マルチスレッド環境では、1つのスレッドのみが時刻に基づいた種で擬似乱数生成器を作成し、他のスレッドはその生成器を使用して乱数を取得します。したがってMath.random()メソッドはスレッドセーフです。

java.util.Randomユーティリティクラス

java.utilパッケージにはRandomクラスが含まれており、Randomオブジェクトを新規作成して乱数を生成できます。整数、float、double、long型の乱数を生成でき、J2MEプログラムでよく使われる乱数取得方法です。

JavaDocより:

 * このクラスのインスタンスは擬似乱数ストリームを生成するために使用されます。
 * クラスは48ビットのシードを使用し、線形合同法式によって変更されます。
 * (ドナルド・クヌース著『コンピュータプログラミングの芸術 第2巻』3.2.1節参照)

基本アルゴリズム:線形合同法擬似乱数生成器(LGC)

欠点:予測可能性

攻撃者は観測された出力値からシードを計算することが可能です。これはjava.util.Randomの場合、2^48よりも大幅に短い時間で実行できます。 出力から容易にシード値を計算できます。 2つの出力値のみを観察することで、約2^16の時間で将来のRandom出力を予測できることが示されています。 そのため次の出力される乱数を予測できます。 セキュリティ関連用途には決してLCGを使用すべきではありません。 情報セキュリティを重視するアプリケーションでは、SecureRandomを使用してください。

Randomクラスが提供するメソッド:API

  • nextBoolean() - 均等分布のtrueまたはfalseを返す
  • nextBytes(byte[] array)
  • nextDouble() - 0.0から1.0の間の均等分布のdoubleを返す
  • nextFloat() - 0.0から1.0の間の均等分布のfloatを返す
  • nextGaussian() - 0.0から1.0の間のガウス分布(正規分布)のdoubleを返す
  • nextInt() - 均等分布のintを返す
  • nextInt(int limit) - 0からlimitまでの均等分布のintを返す(0を含む、limitを含まない)
  • nextLong() - 均等分布のlongを返す
  • setSeed(long seed) - シードを設定

java.util.Randomクラスには2つの構築方法があります:

  1. シード付き
  2. シードなし(デフォルトでは現在のシステムクロックをシードとして使用)

シードが同じであれば、生成される乱数も同じになります:シードが確定し、乱数アルゴリズムも確定しているため、出力は決定的です。

        Random generator1 = new Random(9999);
        Random generator2 = new Random(9999);
        for (int index = 0; index < 5; index++) {
            System.out.println(generator1.nextInt() + " = " + generator2.nextInt());
        }

java.util.concurrent.ThreadLocalRandomユーティリティクラス

ThreadLocalRandomはJDK 7以降で提供され、java.util.Randomを継承しています。

各スレッドには独立した乱数生成器があり、並列処理による乱数生成において複数スレッド間の競合を解決できます。効率が向上します!

ThreadLocalRandomは直接newでインスタンス化するのではなく、最初に静的メソッドcurrent()を使用してThreadLocal<ThreadLocalRandom>インスタンスを取得し、その後java.util.Randomクラスが提供するメソッドを呼び出して各種乱数を取得します。

    class WorkerThread extends Thread {
        @Override
        public void run() {
            for (int iteration = 0; iteration < 2; iteration++) {
                System.out.println(Thread.currentThread().getName() + ": " + ThreadLocalRandom.current().nextDouble());
            }
        }
    }

java.security.SecureRandom

java.util.Randomを継承しています。java.util.Randomのインスタンスは暗号的に安全ではありません。代わりにSecureRandomを使用して、セキュリティセンシティブなアプリケーションで使用する暗号的に安全な擬似乱数生成器を取得することを検討してください。

SecureRandomはOSからランダムデータ(キーストローク間隔など - 多くのOSはこれらのデータを収集し、ファイルに保存します - Linux/Solarisの場合は/dev/randomおよび/dev/urandom)を取得し、それをシードとして使用します。 OSはマウスクリック、キーボード入力などのランダムイベントを収集し、SecureRandomはこれらのランダムイベントをシードとして使用します。

  • SecureRandomは暗号化された強力な乱数生成器(RNG)を提供し、シードが予測不可能であることを要求し、非決定的出力を生成します。
  • SecureRandomは実装に依存しないアルゴリズムも提供するため、呼び出し元(アプリケーションコード)は特定のRNGアルゴリズムを要求し、そのアルゴリズムを持つSecureRandomオブジェクトを返します。

アルゴリズム名のみを指定する場合:

SecureRandom random = SecureRandom.getInstance("SHA1PRNG");

アルゴリズム名とプロバイダを指定する場合:

SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
        SecureRandom secureGen = SecureRandom.getInstance("SHA1PRNG");

        byte[] byteArray = new byte[16];
        secureGen.nextBytes(byteArray);
        System.out.println(Arrays.toString(byteArray));

        for (int count = 0; count < 10; count++) {
            System.out.println(secureGen.nextInt());
        }

SecureRandom使用時にエントロピープール戦略を設定できます。設定が不適切な場合、問題が発生する可能性があります。JDBC接続OracleでConnection reset例外が発生する場合があります。

Spring BootアプリケーションがOracleデータベースに接続する際にconnection reset; errorcode 17002 state 08006というエラーが発生します。

Java 乱数 Random 対 SecureRandom

JVM上の乱数とエントロピープール戦略

タグ: Java random number-generation threadlocalrandom securerandom

6月21日 23:23 投稿