- 締切済み
乱数発生ルーチンの使い方について
数値計算において一様乱数を発生させるルーチンがいろいろあります。ソースが公開されているものやコンパイラが提供したりするものです。それらを利用する場合、乱数発生のシーズ(種)を与えてそれに応じて動作するというものが多いだろうと思います。そこで質問ですが、10000個の乱数を1回発生させる場合と100個の乱数を100回発生させる場合とで乱数の感じがかなり違います。いずれの場合も100×100の2次元データ(エクセルのシート状)として出力して作図したらその違いが簡単に分かります。この違いの原因はシーズの与え方が1回と100回という違いだろうと思います。100回のシーズの与え方にパターンが出来てしまうからだと思われます。例えば時間を使ってシーズを与えなおすことも考えられますが、今時のPCだとあっという間なのでシーズが同じだから、同じ乱数が100個できてしまいます。乱数を繰り返し発生させるときにその繰り返しの中でパターン化された乱数にならないように発生させる方法がないでしょうか。シーズが要らない乱数生成ルーチンとかですが。あるいはシーズをランダムに取得する方法が含まれたルーチン(シーズがないように見える)などです。あるいは本当にないものなど。メルセンヌツイスターはどうなのでしょうか。一応、フォートランでの利用を考えていますが、言語依存の問題ではないかもと思いますが。 よろしくお願いします。
- みんなの回答 (6)
- 専門家の回答
みんなの回答
- Gotthold
- ベストアンサー率47% (396/832)
> 大量に発生させる必要がある場合は、メモリも使うので始めに作成しておくのではなく途中で発生させたいという場合もあるのかなと思いました。 乱数は必要なときに都度生成すれば済むのでわざわざメモリを使う必要は確かに無いですが、そのときシードを再設定する必要は無いですよ。下手にシードを再設定すると乱数の質が落ちます。 (同じ乱数を再現したい場合は別ですが、そういう想定ではないですよね。)
- f272
- ベストアンサー率46% (8477/18147)
「実行プロセスが異なる等の理由があって」とか「低品質なものではビットパターンに偏りが出るので」とかの問題を考えなければ,下記の用にやれば「同じ乱数ばかりが出る」ことはありません。 program test implicit none real :: rnd1(10000),rnd2(100) integer :: i,j,clock integer :: seedsize integer,allocatable :: seed(:) call random_seed(size=seedsize) allocate(seed(seedsize)) !デフォルトの種 call random_seed(get=seed) !確認用 ! do i=1,seedsize ! write(*,*) "seed(",i,")=",seed(i) ! end do ! !自分で種を設定する場合の例 call system_clock(count=clock) seed(1) = clock call random_seed(put=seed) !確認用 ! do i=1,seedsize ! write(*,*) "seed(",i,")=",seed(i) ! end do do i=1,10000 call random_number(rnd1(i)) end do ! do j=1,100 !!! 種を決めるのはこのタイイングではありません !!! ループの外側で決めてください do i=1,100 call random_number(rnd2(i)) end do write(*,*) "rnd2=",rnd2(:7) end do write(*,*) "rnd1=",rnd1(:7) end program test
- trapezium
- ベストアンサー率62% (276/442)
seed いらずでお手軽なのは arc4random(), arc4random_uniform() ですね。既に暗号用途には使えませんが、それ以外なら大抵の OS で使えると思います。(実装切り替わってるものもある) 次にお手軽なのは seed 元として /dev/random 使う方法です。これにメルセンヌツイスタの系列 (いまなら boost あたり?) 使うようにすれば、一般用途であれば問題ないんじゃないでしょうか。 また seed 問題の他に、低品質なものではビットパターンに偏りが出るので注意が必要です。(下位1ビットが同じパターンの繰り返しになったりするので、切り捨てて上位ビットのみ使うとか)
- superside0
- ベストアンサー率64% (461/711)
> 時間に依存したシーズでは呼び出しが早すぎてシーズが変わらないので同じ乱数になってしまいます。 繰り返しになってしまうかもしれませんが、念のため、補足させて頂きます。 同じシーズでの初期化を何度もやると、そこから前と同じ乱数を作ってしまうことになります。 ですので、実行プロセスが異なる等の理由があって、そこまでの乱数発生状況 (簡単にいうと次のシーズ)を 引き継げないということなら プロセス毎でシーズがユニークになる方法を考えねばなりませんが 1つのプロセスで やってるだけなら、乱数発生の初期化(シーズ付与)は、1度だけにすればよいだけですよ。
- superside0
- ベストアンサー率64% (461/711)
http://www.sat.t.u-tokyo.ac.jp/~omi/random_variables_generation.html#Comparison によると、rand()関数のような線形合同法を多次元で使うと均質性が生まれてしまうが、 メルセンヌ・ツイスタだと、これが克服されるように書いてありました。 ただ、今回のケースは、この件ではなく 単に同じシーズを(ループで)使いまわしして、同じ乱数の行列を作ってしまっているだけのことのようなので、これはメルセンヌ・ツイスタを使っても同じことになります。 なので、No.1のご回答にように、シーズを与えるは先頭の1回のみにすればよいだけですね。 もしも、100回というのが、実行プロセスが違ってるので それぞれにユニークなシーズが必要ということなら、 microtime(1/1000秒)を使うなり、 プロセス番号やcpuクロック回数を使うなり Linux(bash)なら環境変数の$RANDOMを使うなりして、 シーズがパターン化しないように工夫すればよいのではと。
- f272
- ベストアンサー率46% (8477/18147)
100個の乱数を100回発生させる場合に,毎回乱数の種を与えているのですか?そんなことをセずに最初に1回だけ種を与えて,その後に乱数を100個づつ100回発生させるのではないでしょうか。
お礼
回答ありがとうございます。長時間のシミュレーションだと計算の途中で乱数を発生させたくなるような場合もあろうかと思います。大量に発生させる必要がある場合は、メモリも使うので始めに作成しておくのではなく途中で発生させたいという場合もあるのかなと思いました。 プログラムとしては関数を呼び出すだけということなので屈託なく呼び出してみたら同じ乱数ばかりが出るということに気づいて(気づかなかったら大けがということ)いろいろ調べ直したところです。
お礼
回答ありがとうございます。パターン化しないシーズを呼び出すという方法があるのは大変ありがたいです。Windows上のフォートランなのですが。 いまのところ、時間に依存したシーズでは呼び出しが早すぎてシーズが変わらないので同じ乱数になってしまいます。