乱数とサイコロ

疑似乱数生成機のアルゴリズム線形合同法*1の場合、以下のようなサイコロの目の求め方は不適切だという。

int n = rand() % 6 + 1;

概念的には以下のように行う必要があるらしい。

int n = rand() / (RAND_MAX / 6 + 1) + 1;

これなら、たまたま偶数や奇数が連続する状態でも、範囲内に散っていればサイコロの目は偏らなくなる。ただし、正確に 1/6 にはならない。例えばわかりやすく RAND_MAX が 99 の場合、rand() は 0〜99 の 100 種類を返すことになるが、すべての値が重複せずに得られた場合、以下のような出現回数になり、6 の目が割を食う。

  • 1: 17 回
  • 2: 17 回
  • 3: 17 回
  • 4: 17 回
  • 5: 17 回
  • 6: 15 回

「rand() % 6 + 1」の場合は、出現回数の差が以下のように 1 以内に収まる。

  • 1: 17 回
  • 2: 17 回
  • 3: 17 回
  • 4: 17 回
  • 5: 16 回
  • 6: 16 回

実際には RAND_MAX はもっと大きく、かつ用途がゲームみたいなものであれば、気にするほどではないと思う。

ちなみに線形合同法は C の標準関数 rand()、Java の Random、VBVBA の Rnd などが当てはまる。C の rand() にバイパスしているだけのスクリプト言語もたくさんあるだろうと思う。

*1:線形合同法とは、ざっくりいうと「次の乱数は直前の乱数をシードにして求めますよ」ということ。同じ乱数が出てきたら、そこから周期が始まる、ということだ