速度分布に応じた乱数生成

マクスウェルの速度分布は、定数を省略すると次のような形になる。


f(x)=x^2 \e^{-x^2}


正規分布と比較すると、少し数値が大きい部分にせり出していて、平均に対して左右非対称である。ゲームでは正規分布に近い乱数を使うことが多いが、クリティカルボーナス(ある数値以上ならさらに数値が上乗せされる)を含めると、この形に近くなる。ならば、この形で乱数を作ることが出来れば、処理が簡単になるというものだ。


ところが、正規分布は乱数を繰り返し発生させるだけで簡単に作れる(自然にそうなってしまう)一方で、好みの分布をとる乱数を生成するのは結構面倒なものだ。次のような方法が考えられるが、問題は多い。

  1. 一様な乱数を使って、正規化した分布から対応する確率を得て、その確率で値を使うかどうか判定(棄却サンプリング)する。その値を使わないなら判定をやり直す。この方法では、例えば確率1%なら100回判定に失敗しないと採択されないため、偏りの大きい分布では非常に時間がかかる。振り直しのあるサイコロがこれに近い。
  2. 分布に応じたサンプルのセットを用意し、その中から1つを選ぶ。セットがあれば時間はかからないが、容量がかさばるのが問題である。確率1/1000なら、少なくとも他に999個のサンプルが必要。くじ引きと同じ方式。
  3. 分布はサンプル群の写像であるので、ある範囲の一様乱数から分布へ対応する関数を作れば良い(説明は適当)。速くて容量も取らないが、関数を探す必要がある。

速度が欲しくて容量も取りたくないなら3番目の選択肢しかない。だた、どうやれば所望の関数が手に入るのか分からないので、試行錯誤していたらこうなった。

(defvar epsilon 1d-12)
(defun f ()
  (expt (- (log (max epsilon (random 1d0)) 10)) 4d-1))



だいたいあってるだろうか。ゲームで使う分には問題ないので、正確な理論は数学者に任せることにしよう。