2009年4月11日土曜日

正規乱数

次のクラスは、正規乱数を発生させるためのもの。Math.randomのように、一様に乱数が発生するのではなく、正規分布(ガウス分布)にしたがって、発生するというものだ。Box-Muller法で実装してみた。

ある特定の数値付近の値(期待値)が最も高確率で発生するが、場合によってはすごく違う値が発生するのだ。どのくらいの確率でずれるかは、「期待値の半分の確率で発生する値を指定する」ことで調整するようにしてある。

コンストラクタの第1引数が、その期待値を、第2引数が、期待値の半分の確率で発生する値までの距離になる。パブリックな読み取り専用プロパティ、floatとintegerがある。それぞれ、Number型、int型の正規乱数を返す。

package {
  public class Gaussian {
    private static const _2pi:Number = Math.PI;
    private static const _hwhm:Number = 2.35 / 2;
    private var _half:Number = _hwhm;
    private var _peak:Number = 0;

    public function Gaussian(expectation:Number,hwhm:Number) {
      _half = hwhm / _hwhm;
      _peak = expectation;
    }

    private function normalDistribution():Number {
      var u1:Number = Math.random();
      var u2:Number = Math.random();

      return _half * Math.sqrt(-2 * Math.log(u1)) * Math.cos(_2pi * u2);
    }

    public function get float():Number {
      return normalDistribution() + _peak;
    }

    public function get integer():int {
      var g:Number = normalDistribution();
      return int(g+((g > 0)? .5 : -.5))+_peak;
    }
  }
}


たとえば、次のように利用する。


var generator1:Gaussian = new Gaussian(10,2);
var a:int = generator1.integer;

var generator2:Gaussian = new Gaussian(10,10);
var b:int = generator2.integer;



上記例では、変数aは、10になる確率が最も高い。ただし、その半分ほどの確率で、8(=10-2)あるいは12(=10+2)になるのである(赤い棒グラフ)。変数bも、10になる確率が最も高い。半分ほどの確率で、0(=10-10)または20(=10+10)になる(青い棒グラフ)。それゆえに、aが10になる確率はbが10になる確率よりも大きい。そして、aもbも10から大きく外れた値になる確率は限りなくゼロに近いが、ゼロではない。

0 件のコメント: