超幾何分布
定義
2種類A,BからなるN個のものがあり、それぞれM,N−M個ある。この集団から勝手にn個取り出したときに、Aがx個、Bがn−x個であるとする。
xの最小値は
- 0(ただしn≤N−M)
- n−(N−M)(ただしn>N−M)
xの最大値は
- n(ただしn<M)
- M(ただしn≥M)
です。実は上記の最小値の大小関係について1にtypoの疑いがあります。最小値について等号が成立するのが、私は1のほうだと考えていますが、参考1の方は2の方に等号が付きます。
例としてN=7,M=3の場合を考えてみます。AとBの物体をそれぞれaとbと表すことにします。すると
aaabbbb
のように並べることができるでしょう。n<N−M=4だとすると、n=3まで物体をとることになります。もし等号も加えてn≤N−M=4であれば、n=4まで物体をとれて、かつn=4個のbをとるとaは0なのでx=0です。逆に、n≥N−M=4だとすると、n=4以上の物体をとることになります。n=4では依然としてx=0になりうるのです。そのためn>N−Mという条件であるので、参考の方にtypoの疑いがあるのです。
超幾何分布はこの時の確率分布です。組み合わせの計算により
f(x)x=NCnMCx⋅N−MCn−x=Max(0,n−(N−M))),……,Min(n,M)
となります。
数値計算への導入
この分布を数値的に計算するためは組み合わせの数を計算しないといけません。しかし、組み合わせの数にでてくる数には階乗がふくまれるため、通常のIntやBigIntegerで計算してもたかが知れています。そこで対数に変換することで、比較的大きな数についての組み合わせの数を計算します。
対数にすることで計算を頑張る。コンビネーションはnCkとすると
nCk=k!(n−k)!n!=i=1∏kin−i+1
です。両編にlnをとると
lnnCk=ln(i=1∏kin−i+1)=i=1∑k[ln(n−i+1)−lni]
です。∑で書けばプログラムに反映しやすくなります。
実装
下記では捕獲再捕獲法に関する例を計算したものです。セットアップとしては、ある湖に魚が1000匹いる状況を考えます。湖にいる魚のうち200匹を捕まえて、尻尾に赤い目印をつけて放流したとします。いま湖から魚を5匹獲ったとすると、赤い目印がついた魚が1匹のとき、0匹の時の確率はどれくらいか?という問題です。
import kotlin.math.ln
import kotlin.math.exp
import kotlin.math.min
fun Int.logCombination(k: Int): Double {
if (k < 0 || k > this) return Double.NEGATIVE_INFINITY
val kk = min(k, this - k)
return (1..k).sumOf { i ->
ln((this - i + 1).toDouble()) - ln(i.toDouble())
}
}
fun hypergeometricProbability(
N: Int, M: Int, n: Int, x: Int
): Double {
if (x < 0 || x > n || x > M || n - x > N - M) {
return 0.0
}
val logProb = M.logCombination(x) +
(N - M).logCombination(n - x) -
N.logCombination(n)
return exp(logProb)
}
fun main() {
val prob1 = hypergeometricProbability(1000, 200, 5, 1)
println("P(X=1) = $prob1")
val prob0 = hypergeometricProbability(1000, 200, 5, 0)
println("P(X=0) = $prob0")
var sumP = 0.0
for(i in 0..5) {
sumP += hypergeometricProbability(1000, 200, 5, i)
}
println("Sum of probabilities = $sumP")
}
これの計算結果は
P(X=1) = 0.41062695331123905
P(X=0) = 0.3268590548357458
Sum of probabilities = 0.9999999999999992
となり、5匹中1匹に色がついている確率が41%、0匹の確率が32%となり、実は1匹のほうが確率が高いのです。
また、分布を可視化してみると

参考資料
統計学入門 東京大学教養学部統計学教室編 東京大学出版会
リポジトリ
更新履歴
- 2026-01-08: Kotlinの組み合わせの計算をKotlinらしく改善