2006年07月07日

rand関数について−その2

今日、研究室でいろいろとrand関数をいじくっていたら、前回の考察のミスを発見。なんだもう、みんなわかってるなら黙ってないで教えてくださいよwwwwwwww

まあ、具体的な計算方法を記述してなかったところに問題があったわけですが。

前回のrand関数の考察では、どのようにしてコイン乱数(コインが作る乱数のことで、要するに0または1のどちらかを出力する関数が作る系列というか数列と思っていただければ)を作っていたのかというと

r = rand()%2; ・・・ (*)

mod2をとっていただけなんですよ。わかりやすく言えば、「2で割ったあまり」です。もっとわかりやすく言えば、偶数なら0、奇数なら1を出力しただけなんですが、どうやらこれが間違いだったらしく、その驚愕の新事実に気づいたのが今日の午後3時頃。

VisualC++のrand関数は、0〜32767までの整数値をランダム(正確には〜ってのは省略)に出力するんだけど、この32767ってのは215-1ということで、15ビットなわけですね。だから、一度関数を呼び出せば2進列で15個の0と1の組み合わせを入手できるので、(*)の方法で同時に1文字ずつ0または1の値を取得するよりも、この15ビットをフルで活用した方が無駄がないということは、まあ当たり前だと思います。

その当たり前のことにさっき気がついたので、下図のようなことを考えて15倍(゚д゚)ウマーしたわけですよ。

こういうのは、手で書いた図をスキャナで取り込んでアップした方が良さそうな気がするな。あと、8451の2新表記が間違ってるな。最初は0でした。

今までは最も右に当たる部分(最下位ビット)だけを取り出して1倍(゚*゚)マズーだったところを、今度はそれの15倍(゚д゚)ウマーというわけですね!

そして、取り出した系列の一番左から順に、その乱数列の性能を簡単に確かめていったんですが...検証方法は、各a[i]の2万ビット(0と1が合計2万個ある)に含まれる1の数を数え、それを65536回繰り返し、その結果をヒストグラムに表してます。ようは、きれいな山型(正規分布)をしていればとりあえず性能がよいということですね。ただし、ちゃんとした検証方法ではないのでご注意を。

この結果は意外だったね。特に上位ビットはそんなに性能が悪くないという結果。いやまあ、細かい検証を全然してないから、それらをやればぼろが出るんだろうけどさ。それに周期も短いらしいし。ダメじゃん。

これはひどいな。グラフの1〜15というのは、それぞれがa[1]〜a[15]に相当してます。

左側に行けば行くほど、乱数の性能が悪くなっていると言うことが一目瞭然ですね!! 右端の値だけを使って、これを乱数として使用するぶんには、案外それほど性能に関しては気にしなくても良さそうだけど、一般人が手っ取り早く乱数を使う時なんて、普通は俺みたいに(*)のように使うだろうから、ちょっとこれは問題があるんじゃないかなと。

で、同じような考えで、メルセンヌ・ツイスタ(Mersenne Twister−MT)法でもやってみました。rand関数が15ビットしか出力できなかったことに対し、MT法では、なんとその倍の32ビットの値を出力できる激(゚д゚)ウマーな乱数ジェネレータだったりします。

結果はこんな感じ

これはすごいな。中央部分にちょっとだけ乱れがあるけど、全体的にみて美しいまでの正規分布の形になってるし。人々が、ここまで褒め称える理由がわかった気がする。

おおお、どこを切っても同じ分布になってるしヾ( ゚д゚)ノ"

rand関数を使うときはこんな感じに分割して使うことはないだろうけど、単純に考えて、これらの性能の良い列と、悪い列をまぜこぜで使ってるんだから、その合成された結果というのは必ずしも良い列とはなりませんよね!! そんなわけで、rand関数がいかに危険な関数かということが、わかったかと思います。そして、本当に正しく正確に乱数を出力したいならば(←ややこしいな)、MT法を使った方がずっと良いと言うことが、このことからも理解できたと思います。

って言うと、うちのゼミでは「いやそれだけの検証じゃ全然だめだよ。もっと数学的に証明しないと」ってつっこまれるんだよなwwwww

まあ、このテーマで次回のゼミを発表したいと思うので、興味があったらうちの研究室に来てみてください。熱烈歓迎は・・・たぶん、発表に必死でしませんというかできませんwwwww
posted by iwasaki_p at 23:37| Comment(3) | TrackBack(0) | プログラミング
この記事へのコメント
下記ビットの値が信頼ならないことへの、簡易的な対処としては、値をシフトしてから取り出すって手段もあります。

(rand() >> 4) % 2

みたいな。

当然、自分でキチンとした計算のrand関数に置き換えてしまう方が、適切ではありますが。
Posted by おぎゅぅ at 2006年07月07日 23:58
なるほど!
これぐらいの簡単な処理で、そこそこ安定した出力が得られるなら、フリーソフトのゲームレベルならこれで十分ですね!

研究で複雑な計算をするときや、[0,1]での連続値が欲しい場合、あとは市販ソフトあたりでは、確かにMT法あたりを導入しないとやっていけませんが、まあ、rand関数の周期に収まる程度の乱数ぐらいしか使わない個人ゲームならば、そこまでの精度はそこまで必要ないかな。
Posted by Iwasaki++ at 2006年07月08日 01:37
御無沙汰してます。
後輩に rand() の精度について教えようと思ってググったら2番目に出てきたので、旧ブログだけど記念真紀子。
Posted by ヅラChu at 2008年07月11日 09:17
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/944148
※ブログオーナーが承認したトラックバックのみ表示されます。
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック