乱数の使い方

k.inabaさんに久々に絡んでみようと思う。

http://www.kmonos.net/wlog/97.html#_2206090523
http://www001.upp.so-net.ne.jp/isaku/rand.html

さらに、初期化ミスのため、例えば以下のプログラムを JAVA で実行すると、

    public class RandTest
    {
        static java.util.Random r=new java.util.Random();
        public static void main (String[] args)
        {
            double x,Min=1,Max=0;
            for (int i=0;i<1024;i++) {
                r.setSeed(i); x=r.nextDouble();
                if (x>Max) Max=x;
                if (x

結果は、

Min=0.6753377750582709 Max=0.7669794809891215

となり、0から1023までの種で初期化したにもかかわらず、最初の乱数は0.7近辺しか現われない。

このサイトなんかでは、メルセンヌツイスタを使いましょう、とのことなんだけども、そもそもこの実験って意味無いんですけどー。乱数のSeedを、いくつも与えてみました、というのはそもそも乱数の使い方を間違っています。Seed1とSeed2という二つのSeedを与えたときの乱数が、互いに無相関である保障はほとんどの乱数で存在していません。それはメルセンヌツイスタでも一緒。
だから例えば、モンテカルロ実験を並列に実行しようとしたときに、マシン1..マシンnで、別のSeedを用いて計算すればよい、と単純に考えてはいけません。場合によれば、マシン1のn項目とマシン2のn+1項目が常に一緒、みたいな乱数が生成されるかもしれません。もしそうすれば、この実験は適切に並列化できていないことになり、結果として期待されている精度が出ないことになります。この危険性は、例え初期Seedとして自然乱数を使ったとしても、常に起こりえます。
では、こういった問題にどう対処するべきか、といえば、通常はその内部アルゴリズム上、絶対にかぶらないようなSeedを複数用意します。例えば、線形合同法で周期が100M程度であれば、マシン1には適当なものを、マシン2にはこのSeedから10M回生成を行った後の値を、・・・・マシン10には90M回生成を行った後の値を、とやります。そしてこの方法は、メルセンヌツイスタの場合も大体同じです。
自分で書きたくない場合は、こんなものがあります(SPRNG: http://sprng.cs.fsu.edu/)

で、メルセンヌツイスタも同じ問題を抱えているのに、なぜメルセンヌツイスタではあまりその問題が観測されないのか。それは、1.メルセンヌツイスタはアルゴリズムが複雑で解析しにくく、2.周期が長いのでSeedによって偶然かぶることが比較的少なく、3.宗教的にもメルセンヌツイスタは正しいと思われているから、です。用法用量を守って正しく使いましょう。