二項分布
0 = 黒、1 = 赤として赤の出る確率を
| |
0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 確率 |
|||||||
| |
|||||||
| 観測回数 |
定義と性質
試行において 1 が観測される確率
共通性と近似
ベルヌーイ分布との共通性
ベルヌーイ試行の結果を
ポアソン分布との近似
ポアソン分布は
アルゴリズム
二項分布の試行関数
二項分布に従う整数 true であった数を返せば良い。
def trial(n:Int, p:Double):Int = (0 to n).count{ _ => math.random() < p }
scala> trial(5, 0.6)
res0: Int = 3
scala> trial(5, 0.6)
res1: Int = 2
scala> trial(5, 0.6)
res2: Int = 4
Scala の部分適用を使ってあらかじめパラメータ
scala> def trial(n:Int, p:Double)():Int = (0 to n).count{ _ => math.random() < p }
trial: (n: Int, p: Double)()Int
scala> val x = trial(5, 0.6) _
x: () => Int = $$Lambda$1171/2060445311@47a1bf14
scala> x()
res0: Int = 5
二項係数
階乗は小さな factorialln() を使用する。
def coefficient(n:Int, k:Int):Double = {
math.floor(0.5 + math.exp(factorialln(n) - factorialln(k) - factorialln(n-k)))
}
本来の二項係数は整数となるが、この結果には浮動小数点演算の丸めによる若干の誤差が含まれる。この誤差を解消する目的で計算値に 0.5 を足して floor() を適用している (誤差が 0.25 に近くなるようであれば十分に大きな数であるため一の位の操作は無視できる)。
確率関数
二項分布の確率関数は指数と階乗を含むため数値演算上のオーバーフローを起こしやすいが、一度対数化してから指数演算を行うと簡単になる。
上記より二項分布の確率関数は以下のように実装できる。分布のパラメータである
def p(n:Int, p:Double)(x:Int):Double = math.exp(
factorialln(n) - factorialln(x) - factorialln(n - x) +
x * math.log(p) + (n - x) * math.log(1 - p)
)
実行結果は以下の通り。分かりやすいように部分適用は名前付きパラメータで呼び出している。
scala> val p6 = p(n=6, p=0.6)
p6: Int => Double = $$Lambda$1598/1837120736@e78bbcc
scala> for(x <- 0 to 6) println(p6(x))
0.004096000000000002
0.03686400000000005
0.13824000000000003
0.2764800000000003
0.31104
0.18662400000000007
0.04665599999999999
scala> (for(x <- 0 to 6) yield p6(x)).sum
res69: Double = 1.0000000000000004