• 締切済み

乱数発生ルーチンの使い方について

数値計算において一様乱数を発生させるルーチンがいろいろあります。ソースが公開されているものやコンパイラが提供したりするものです。それらを利用する場合、乱数発生のシーズ(種)を与えてそれに応じて動作するというものが多いだろうと思います。そこで質問ですが、10000個の乱数を1回発生させる場合と100個の乱数を100回発生させる場合とで乱数の感じがかなり違います。いずれの場合も100×100の2次元データ(エクセルのシート状)として出力して作図したらその違いが簡単に分かります。この違いの原因はシーズの与え方が1回と100回という違いだろうと思います。100回のシーズの与え方にパターンが出来てしまうからだと思われます。例えば時間を使ってシーズを与えなおすことも考えられますが、今時のPCだとあっという間なのでシーズが同じだから、同じ乱数が100個できてしまいます。乱数を繰り返し発生させるときにその繰り返しの中でパターン化された乱数にならないように発生させる方法がないでしょうか。シーズが要らない乱数生成ルーチンとかですが。あるいはシーズをランダムに取得する方法が含まれたルーチン(シーズがないように見える)などです。あるいは本当にないものなど。メルセンヌツイスターはどうなのでしょうか。一応、フォートランでの利用を考えていますが、言語依存の問題ではないかもと思いますが。 よろしくお願いします。

みんなの回答

  • Gotthold
  • ベストアンサー率47% (396/832)
回答No.6

> 大量に発生させる必要がある場合は、メモリも使うので始めに作成しておくのではなく途中で発生させたいという場合もあるのかなと思いました。 乱数は必要なときに都度生成すれば済むのでわざわざメモリを使う必要は確かに無いですが、そのときシードを再設定する必要は無いですよ。下手にシードを再設定すると乱数の質が落ちます。 (同じ乱数を再現したい場合は別ですが、そういう想定ではないですよね。)

  • f272
  • ベストアンサー率46% (8024/17151)
回答No.5

「実行プロセスが異なる等の理由があって」とか「低品質なものではビットパターンに偏りが出るので」とかの問題を考えなければ,下記の用にやれば「同じ乱数ばかりが出る」ことはありません。 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)
回答No.4

seed いらずでお手軽なのは arc4random(), arc4random_uniform() ですね。既に暗号用途には使えませんが、それ以外なら大抵の OS で使えると思います。(実装切り替わってるものもある) 次にお手軽なのは seed 元として /dev/random 使う方法です。これにメルセンヌツイスタの系列 (いまなら boost あたり?) 使うようにすれば、一般用途であれば問題ないんじゃないでしょうか。 また seed 問題の他に、低品質なものではビットパターンに偏りが出るので注意が必要です。(下位1ビットが同じパターンの繰り返しになったりするので、切り捨てて上位ビットのみ使うとか)

回答No.3

> 時間に依存したシーズでは呼び出しが早すぎてシーズが変わらないので同じ乱数になってしまいます。 繰り返しになってしまうかもしれませんが、念のため、補足させて頂きます。 同じシーズでの初期化を何度もやると、そこから前と同じ乱数を作ってしまうことになります。 ですので、実行プロセスが異なる等の理由があって、そこまでの乱数発生状況 (簡単にいうと次のシーズ)を 引き継げないということなら プロセス毎でシーズがユニークになる方法を考えねばなりませんが 1つのプロセスで やってるだけなら、乱数発生の初期化(シーズ付与)は、1度だけにすればよいだけですよ。

回答No.2

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を使うなりして、 シーズがパターン化しないように工夫すればよいのではと。

skmsk1941093
質問者

お礼

回答ありがとうございます。パターン化しないシーズを呼び出すという方法があるのは大変ありがたいです。Windows上のフォートランなのですが。 いまのところ、時間に依存したシーズでは呼び出しが早すぎてシーズが変わらないので同じ乱数になってしまいます。

  • f272
  • ベストアンサー率46% (8024/17151)
回答No.1

100個の乱数を100回発生させる場合に,毎回乱数の種を与えているのですか?そんなことをセずに最初に1回だけ種を与えて,その後に乱数を100個づつ100回発生させるのではないでしょうか。

skmsk1941093
質問者

お礼

回答ありがとうございます。長時間のシミュレーションだと計算の途中で乱数を発生させたくなるような場合もあろうかと思います。大量に発生させる必要がある場合は、メモリも使うので始めに作成しておくのではなく途中で発生させたいという場合もあるのかなと思いました。 プログラムとしては関数を呼び出すだけということなので屈託なく呼び出してみたら同じ乱数ばかりが出るということに気づいて(気づかなかったら大けがということ)いろいろ調べ直したところです。

関連するQ&A

  • 乱数 メルセンヌツイスターについて

    メルセンヌツイスター法(MT法)について少し調べることになったのですが、メルセンヌツイスターのサイトにC言語のソースがあったので動かしてみました。 確かに乱数が発生するのですが、ソースをみてもどういう動きで乱数を生成してるのかがわかりません。 メルセンヌツイスターはどうやって乱数をつくりだしているのでしょうか? わかる方がいらっしゃいましたら、教えてください。

  • 乱数生成、メルセンヌツイスターの使い方

    http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html 乱数生成にメルセンヌツイスターというものを使おうとしたのですが、 色々試してみてもいまいち使い方が分かりませんでした。 例えば0から100までの間の乱数を得たい場合などはどのように 書けばよいのでしょうか・・?

  • メルセンヌツイスターを使った2次元乱数

    Mersenne Twisterを使って2次元の乱数を下記のように 生成しています。 1. 乱数を取得. x座標の値とする。 2. 1)で用いた乱数生成を利用して乱数を取得. y座標の値とする。 こうした作成したx,y座標のデータを見ますと、一様性が あまりないように見えます。 これは、2次元の乱数の扱いが間違っているのでしょうか? あるいは、周期が非常に長い乱数でも、2次元的に一様性を 保つためには、凖乱数を使うのがいいのでしょうか。

  • メルセンヌツイスターによる乱数の使い方

    メルセンヌツイスターによる乱数を生成したいのですが、下記コードにすると、mt_rand関数を呼ぶたびにシードが初期化され他乱数が生成されます。 やりたいことは、main関数で一度シード101で初期化した後は、mt_rand関数内では、初期化することなく継続した乱数を生成したいのです。 そのためa_mt_rand関数のようにすると、mtが定義されていないとなるエラーとなります。 namespaceの問題と思うのですが、色々試してみましたが、できませんでした。 どのようにすればできるでしょうか。 vc++11、windows11 使用 参考サイト C++ 乱数ライブラリ std::random の使い方 リンクはうまく貼れませんでした。 #pragma hdrstop #include <iostream> #include <stdio.h> #include <random> using namespace std; void mt_rand(void); int main(int argc, char *argv[]) { int ptr; std::mt19937 mt(101); // メルセンヌツイスターの32ビット版、引数は初期シード std::uniform_int_distribution <> rand100(0, 100); // [0, 99] 範囲の一様乱数 ptr = rand100(mt); printf("ptr=%d\n",ptr); mt_rand(); } void mt_rand(void) { int ptr=0; std::mt19937 mt(101); // メルセンヌツイスターの32ビット版、引数は初期シード std::uniform_int_distribution <> rand100(0, 100); // [0, 99] 範囲の一様乱数 ptr = rand100(mt); printf("%d\n",ptr); } void a_mt_rand(void) { int ptr=0; ptr = rand100(mt); printf("%d\n",ptr); }

  • 乱数の初期化について

    Cでモンテカルロシミュレーションを行っています。 乱数Merssenne twisterをつかって、各試行ごとに乱数を 時間で以下のように初期化しています。 init_genrand ((unsigned)time(NULL)) ; だいたい10000~100000回くらいシミュレーションを行う必要があって、 各試行は使用しているPCだと一瞬で終了します。 このときに上記の方法で種を初期化すると、 きちんと確認はしていないのですが、 試行時間が短すぎで種の時間が進んでいないような状態が起こります。 パソコンの時間で初期化する場合、間隔が短すぎると初期化種がかぶる ことはありますか。 もしあるなら、1回の試行時間が短いシミュレーションを各々初期化できる ような初期化の方法を教えていただけないでしょうか。 環境はcore2duo2.16、vista32、コンパイラはVisualC++2008です。 よろしくお願いします。

  • access2003の乱数で・・・

    はじめまして。access初心者です。 Rnd乱数を使ってテーブルからランダムにフォームに表示するおみくじのようなモノを作っています。テーブルには「CD」のフィールドを作り連番で整数を並べ、発生する乱数はこの範囲に指定し、テーブルのレコードを追加することでおみくじの回答を増やせる仕組にしています。 実際、動かすとランダムにおみくじの回答がフォームに表示されるのですが、一度accessを閉じて開いた場合の1回目の回答が毎回同じになってしまいます。(続けて2回目ですとランダムに出ます)原因や回避方法はあるのでしょうか?また、ファイルの保存先はファイルサーバーである事も何か影響あるのでしょうか? ご教授願えれば幸いです。

  • 凖モンテカルロ法 or モンテカルロ + MT

    3次元に広がる物理量を面積分する方法について検討中です。 凖モンテカルロという方法でLDSという数列を用いて計算をすると、積分計算の 誤差の軽減が速まるらしいということがわかりました。原理はまだ勉強中ですが、 対象の空間を区切って、それぞれの細空間に乱数のポイントを配置することが いい方向に働くように見られます。 これに対して、通常のモンテカルロ法でメルセンヌツイスター(MT)を用いた場合との 違いがいまいちわかりません。MTで一様乱数ができるのであれば、凖モンテカルロ のようにする必要はないのでしょうか?

  • 足して100になるような乱数のアルゴリズム

    次のような、8つの変数にそれぞれランダム(0から100まで)に発生させた数を入れて 毎回合計で100になるようにしたいです。 a[0] = 12; a[1] = 21; a[2] = 8; a[3] = 30; a[4] = 0; a[5] = 14; a[6] = 5; a[7] = 10; もう1回実行したとしても a[0] = 2; a[1] = 14; a[2] = 62; a[3] = 5; a[4] = 0; a[5] = 0; a[6] = 1; a[7] = 16; と合計で100になります。 a[0] = 0; a[1] = 0; a[2] = 100; a[3] = 0; a[4] = 0; a[5] = 0; a[6] = 0; a[7] = 0; と滅多にありないですけど、このようになる可能性もあると思います。 このような乱数を発生させるためにはどのようなアルゴリズムになるのでしょうか?

  • 乱数

    乱数x、y(0〈x〈1)、(0〈y〈1)をn回発生させて、これらを座標とする点をx‐y座標平面上にプロットしてこの点と原点との距離が1以下となるときの回数をカウントしm回としてn:m=(長さ1の正方形の面積):(円の4分の1の面積)となる。これを利用して円周率を求めるプログラムなんてどう作るんですか?

  • 乱数の発生 Randomizeを入れた方が良いのか

    VBAについて教えてください。 --------------------------- Sub Randomizeを入れた場合() Dim intMax As Integer Dim intMin As Integer Randomize intMin = 1 intMax = 10 Debug.Print Int((intMax - intMin + 1) * Rnd + intMin) End Sub --------------------------- Sub Randomizeを入れていない場合() Dim intMax As Integer Dim intMin As Integer intMin = 1 intMax = 10 Debug.Print Int((intMax - intMin + 1) * Rnd + intMin) End Sub --------------------------- 上記二つのマクロを実行しても 同じような結果が得られる気がするのですがRandomizeは必要なのでしょうか? 一つのサブプロシージャーの中で1回しか乱数を発生させないのなら Randomizeを使って乱数を初期化する必要はないのですか?