みなさん。
指定した範囲の乱数を作りたい場合は、どうしているでしょうか。
今日は、指定した範囲の乱数生成の小ネタを紹介します。

指定した範囲の乱数を作成したい(前編)←いまここっ!
指定した範囲の乱数を作成したい(後編)
指定した範囲の乱数を作成したい(実戦編)

さて、例えばサイコロを作りたいと思います。
サイコロの目は1~6ですね。

そして乱数生成器が用意されていると思います。
この乱数生成器は、4バイト=32ビットの一様分布する整数値が出力されるもので、
random32()と関数を呼び出せば、出力されるものとします。
( random32() は、C言語の乱数 rand()と似たような関数だと思ってください。 )

どのような、コードを思い浮かべるでしょうか。
恐らく最も簡単なコードは、次のようなコードです。

サイコロの目 = random32() mod 6 + 1
(mod 6 は、6で割った余りの数とします)

はい。
たしかに、「random32() mod 6」で0~5の範囲の値を作成して、
1を足して、1~6の乱数が出ると思います。

今度は、実数の乱数生成器で、サイコロを作る話です。
この実数の乱数生成器は、64ビットのdouble型の実数を作成するもので
[0, 1) = {x | 0 ≤ x < 1} の乱数、つまり0以上、1未満の乱数を得ることができます。
randomf()と関数を呼び出せば、出力されるものとします。
( randomf() は、JavaScript の Math.random() と似たような関数だと思ってください。)

この場合も、どのような、コードを思い浮かべるでしょうか。
最も簡単なコードは、次のようなコードです。

サイコロの目 = floor(6 * randomf()) + 1
(floor() は、床関数です)

はい。
これも、たしかに、「6 * randomf()」で[0, 6)の乱数を作成して、
床関数で0~5の整数値にして、1を足すことで、1~6の乱数が出力されます。

簡単で、すぐに乱数が欲しいという目的であれば、これまでの話は正しいと思います。

ただし、知っている方も多いかと思いますが、
本当に本当に正確な乱数を欲しい場合は、問題があるコードなのです。
何が問題なのでしょうか。

まずは、整数の乱数を使用した場合です。

問題を分かりやすく考えるため誤差を大きくしましょう。
2ビットの乱数生成器(0~3までの4パターンの数字を出力する)random4()が用意されており、
ここから0~2まで、3パターンの数字を出力する乱数を作り出したいと思います。

この2ビットの乱数生成器の性能を表にまとめます。

乱数値0123
確率1/41/41/41/4

それでは、先ほど同じような考え方で
y = random4() mod 3
を計算してみるとどうでしょうか

乱数値0123
y (3で割った余り)0120

つまり、出力される0~2の値は、
「random4() mod 3」の計算で作成すると、
下記のように0が出る確率が高くなってしまうのです。

残念な乱数出力結果1

y012
確率2/41/41/4

今度は、実数の乱数生成器の話です。
「 floor(6 * randomf()) + 1」とサイコロを作成した場合、同じように偏りが生まれる問題があるのです。
浮動小数点型に限界があるのです。
一様乱数な0から1未満の浮動小数点を作成したとしても、仮数部のビット数分の種類しかないのです。
(ただ、出力値がdouble型ならば、
先ほどの、randomf()は、約2^52ビット=9007兆の数字のパターンを持っています。
これだけ大きければ実際は、問題はないと思います。)

分かりやすく考えるため、しょぼい乱数生成器を使用します。
2ビット整数の乱数生成器の話と同じように仮数部が、
2ビットの実数の乱数生成器randomf4()を使用して、0~3の整数を作ってみましょう。
(randomf4()は、0から1未満を生成するが、0.00 0.25 0.50 0.75の4パターンしか出力されない)

この仮数部2ビットの実数乱数生成器の性能を表にまとめます。

乱数値0/41/42/43/4
確率1/41/41/41/4

それでは、先ほど同じように
y = randomf4() * 3
z = floor(y)
を計算してみるとどうでしょうか

乱数値0/41/42/43/4
y (3倍した)0/43/46/49/4
z (床関数)0012

つまり、出力される0~2の値は、
「random4() % 3」の計算で作成すると、
0が出る確率が高くなってしまうのです。

残念な乱数出力結果2

y012
確率2/41/41/4

では、どうすればいいんじゃあって思うかもしれません。
まあ、普通に使用する分は、最初の話があったような方法で十分です。
ただし、使う人を選ばない乱数生成器を作成したいとか、
どのような乱数生成器の性能でも、しっかりした乱数を作りたいといった場合は、
工夫して上記の問題を解決する必要があります。

指定した範囲の乱数を作成したい(後編)へ続くッ!!!

関連記事

  • 指定した範囲の乱数を作成したい(後編)指定した範囲の乱数を作成したい(後編) みなさん、こんにちは。 今日は、昨日の続き、「指定した範囲の乱数を作りたい」を続きに話していきたいと思います。 というか、解答編みたいな感じです。 指定した範囲の乱数を作成したい(前編) 指定した範囲の乱数を作成したい(後編)←いまここっ! 指定した範囲の乱数を作成したい(実戦編) ・ それでは、ちょっとおさらいをしながら進めていきます。 今回は、 […] Posted in アルゴリズム
  • 指定した範囲の乱数を作成したい(実戦編)指定した範囲の乱数を作成したい(実戦編) みなさん、こんにちは。 さて、前回、前々回にわたって、 指定した範囲の乱数の作成の仕方を書きました。 改めて奥が深いですね。 読み忘れた方のためにリンクしますっ(^o^) 指定した範囲の乱数を作成したい(前編) 指定した範囲の乱数を作成したい(後編) 指定した範囲の乱数を作成したい(実戦編)←いまここっ! これまでの話では、 小さなより精度が […] Posted in アルゴリズム
  • JScriptバッチ処理用ライブラリ 20130825公開JScriptバッチ処理用ライブラリ 20130825公開 私のサイトで公開している JScriptバッチ処理用ライブラリのバージョン「20130825」を公開しました。 主な内容は、JavaのString.formatを、JScriptでも使用できるようにするというものです。 JavaのString.formatは、大体はCのsprintfと似ているのですが、もっといろいろできます。 ただ、今回JScriptで使用できる […] Posted in ライブラリ制作
  • Fizz-Buzz問題を解いてみた!Fizz-Buzz問題を解いてみた! 今日はネットでFizz-Buzz問題という面白そうなものを見つけた。 Fizz-Buzz問題 はじめに どうしてプログラマに・・・プログラムが書けないのか? http://www.aoky.net/articles/jeff_atwood/why_cant_programmers_program.htm かなりの試行錯誤の末に、コードを書こうともがいている人たちとい […] Posted in アルゴリズム
  • Bresenhamアルゴリズムで線分の計算は早いのかなBresenhamアルゴリズムで線分の計算は早いのかな 以前、直線を描くのにこんなのを作った。 特に工夫点もない、素直な方法である。 #module "linem" #deffunc line2 int line_x1,int line_y1,int line_x2,int line_y2 line_xabs = abs(line_x1-line_x2) line_yabs = […] Posted in アルゴリズム
  • C言語のアドレス演算子の不思議C言語のアドレス演算子の不思議 前々から、C言語のポインタで不思議だったこと。 最適化C 言語 […] Posted in プログラミング