No Programming, No Life

新しいNPNLです。http://d.hatena.ne.jp/fumokmm/ から引っ越してきました。

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

Randomクラスを利用

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

Random r = new Random(seed);

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

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()

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

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

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

java.lang.Runtime#freeMemory()

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

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


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

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

シンプルな別解

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

java.lang.Math#random()
Math.random()

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

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

*1:空きメモリ量は刻一刻と変化し続ける。

*2:しかもスレッドセーフ。