Think Twice
IT技術メモ | Javaのメモ
Created: 2009-01-16 / Updated: 2021-06-21

Javaで毎回完全に異なる乱数を取得する方法


当メモは2009-01-16に投稿されたものを加筆修正し、再掲したものです。

目次


Randomクラスを利用

Javaで乱数を利用する場合、java.util.Randomあたりを使うと思います。
java.util.Randomは乱数ジェネレータですので、乱数の種(long seed)を与えて、それぞれ違った順序で乱数を発生させるジェネレータを生成することができます。

Copy
Random r = new Random(seed);

ということなんですが、この種に同じ値を指定してしまうと、毎回同じ順番で同じ値が取得できてしまい、あまり嬉しくありません。

Copy
long seed = 1L;
Random r = new Random(seed);
assert r.nextDouble() == 0.7308781907032909;
assert r.nextDouble() == 0.41008081149220166;
assert r.nextDouble() == 0.20771484130971707;
                      // ↑1Lだとこの順番で乱数が生成される
r = new Random(seed);
assert r.nextDouble() == 0.7308781907032909;
assert r.nextDouble() == 0.41008081149220166;
assert r.nextDouble() == 0.20771484130971707;
                      // ↑1Lだとこの順番で乱数が生成される

種(seed)に完全にランダムなlong値を

ってことは、種(seed)に完全なランダムなlong値を指定できさえすれば、毎回完全に異なる乱数ジェネレータを生成できるはず。

java.lang.System#currentTimeMillis()

よく使われるものに、現在時刻のミリ秒を使うものがある。

Copy
long seed = System.currentTimeMillis(); // 現在時刻のミリ秒
Random r = new Random(seed);

ただ、これだと極稀に複数スレッドから同時にアクセスした時にseedが被ってしまう危険性がある(かもしれない)。

java.lang.Runtime#freeMemory()

そこで、環境に依存する値として、空きメモリ量1を指定する方法は以下のようになる。

Copy
long seed = Runtime.getRuntime().freeMemory(); // 空きメモリ量
Random r = new Random(seed);

これで、JavaVMを実行しているマシンの実行時の空きメモリ量に応じて乱数が生成されることになるため、かなり安全性が高まると思われる。
さらに追い討ちで、現在時刻のミリ秒+空きメモリ量なんていう種(seed)を利用するのもありかもしれない。

Copy
long seed = System.currentTimeMillis() + Runtime.runtime.freeMemory();
Random r = new Random(seed);

シンプルな別解

これで完璧!と思っていたのですが、実は単純にもっとシンプルな別解があったようです。

java.lang.Math#random()

Copy
Math.random()

これは、最初にこのメソッドが利用された時に、new java.util.Random()でジェネレータが一回だけ作成されて、以降ずっとそのジェネレータが利用されるらしいです。しかもスレッドセーフ。

ソースを見るとこんな感じになっています。こちらはOpenJDK11のもの。

java/lang/Math.java より抜粋
Copy
public static double random() {
    return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
    :
    :
private static final class RandomNumberGeneratorHolder {
    static final Random randomNumberGenerator = new Random();
}

戻されるdouble値は毎回異なるし、何の問題もないな。なんだ…こんなにシンプルな解決法があったんだ。


参考

元記事

参照


  1. 空きメモリ量は刻一刻と変化し続ける。 ↩︎