Mímisbrunnr知恵の泉

← シミュレーション 一覧

🎓 レベル:基礎 | 重要度:A(必須)

📎 前提:擬似乱数(線形合同法・メルセンヌツイスタ) | 関連:棄却法(受理・棄却サンプリング)

要点(BLUF)

1. 原理:確率積分変換

連続な累積分布関数(CDF)FF を持つ確率変数 XX について、U=F(X)U = F(X)U(0,1)\mathcal{U}(0,1) に従います(確率積分変換)。これを逆に使うのが逆関数法です。

UU(0,1)U \sim \mathcal{U}(0,1) とし、X=F1(U)X = F^{-1}(U) と定義すると、

P(Xx)=P ⁣(F1(U)x)=P ⁣(UF(x))=F(x)P(X \le x) = P\!\left(F^{-1}(U) \le x\right) = P\!\left(U \le F(x)\right) = F(x)

最後の等号は UU が一様だから(P(Uu)=uP(U \le u) = u)。つまり XX の CDF はちょうど FF になり、XX は目的の分布に従います。手順は2ステップだけ:

  1. UU(0,1)U \sim \mathcal{U}(0,1) を引く
  2. X=F1(U)X = F^{-1}(U) を返す
flowchart LR
    U["一様乱数 U ~ U(0,1)"] -->|"X = Finv(U)"| X["目的分布のサンプル X"]

2. 連続分布の例:指数分布

指数分布 Exp(λ)\text{Exp}(\lambda) の CDF は F(x)=1eλxF(x) = 1 - e^{-\lambda x}。これを uu について解くと逆関数が得られます。

u=1eλx    x=F1(u)=ln(1u)λu = 1 - e^{-\lambda x} \;\Longrightarrow\; x = F^{-1}(u) = -\frac{\ln(1-u)}{\lambda}
import numpy as np

# 乱数シードを固定
rng = np.random.default_rng(2)

lam = 1.5
u = rng.random(500_000)            # 一様乱数
x = -np.log(1 - u) / lam           # 指数分布の逆関数 Finv(u)

print(f"逆関数法 Exp の平均 = {x.mean():.4f}  (理論 {1/lam:.4f})")
print(f"逆関数法 Exp の分散 = {x.var():.4f}  (理論 {1/lam**2:.4f})")

出力:

逆関数法 Exp の平均 = 0.6669  (理論 0.6667)
逆関数法 Exp の分散 = 0.4432  (理論 0.4444)

出力の意味:一様乱数を ln(1u)/λ-\ln(1-u)/\lambda に通すだけで、平均 0.6669・分散 0.4432 が指数分布の理論値 1/λ=0.6671/\lambda = 0.6671/λ2=0.4441/\lambda^2 = 0.444 と一致。CDF の逆が解析的に書けるので、棄却もループも要らず1サンプルにつき一様乱数1個で済む——これが逆関数法の効率です。(実装では 1U1-UUU も同分布なので -np.log(u)/lam でも可。)

3. 離散分布の例:イカサマサイコロ

離散分布でも、CDF を階段関数として「累積確率を超える最初の値」を返せば逆関数法になります。確率 p=(0.1,0.2,0.3,0.15,0.15,0.1)p = (0.1, 0.2, 0.3, 0.15, 0.15, 0.1) のサイコロを作ります。

import numpy as np

# 乱数シードを固定
rng = np.random.default_rng(3)

p = np.array([0.1, 0.2, 0.3, 0.15, 0.15, 0.1])
cdf = np.cumsum(p)                      # 累積確率(階段)
u = rng.random(300_000)
faces = np.searchsorted(cdf, u) + 1     # u が入る区間 = 目の値
freq = np.bincount(faces, minlength=7)[1:] / len(u)

print(f"生成された頻度 = {np.round(freq, 3)}")
print(f"目標の確率     = {p}")

出力:

生成された頻度 = [0.1   0.199 0.301 0.15  0.15  0.1  ]
目標の確率     = [0.1  0.2  0.3  0.15 0.15 0.1 ]

出力の意味searchsorted で「一様乱数が累積確率のどの段に落ちるか」を引くだけで、生成頻度が目標確率と小数3桁まで一致。離散版の逆関数法は、各カテゴリの確率を区間幅として [0,1)[0,1) を分割し、一様乱数がどの区間に落ちたかで分類する操作です。

4. 長所と限界

数式の直観的意味

逆関数法は「確率の目盛り([0,1][0,1])を、値の目盛り(xx)に翻訳する」操作です。CDF FF は「値 → 累積確率」、その逆 F1F^{-1} は「累積確率 → 値」。一様乱数で累積確率を等確率に選べば、F1F^{-1}確率密度の高いところを密に、低いところを疎に値へ割り当てる——だから自然に目的分布が再現されます。離散版で確率を区間幅にするのも、まったく同じ「確率を値に翻訳する」発想です。

⚠️ よくある誤解・落とし穴

対応シミュレーション参照

本文の指数分布(連続)・イカサマサイコロ(離散)の逆関数法コード(default_rng(2)default_rng(3))。

関連ノート