条件付き確率とは?定義と計算方法を徹底解説

トランプの山から1枚カードを引いたとき、そのカードがハートである確率は $13/52 = 1/4$ です。では、「引いたカードが赤い」という情報が得られたら、ハートである確率はどう変わるでしょうか?

赤いカードはハート13枚とダイヤ13枚の計26枚です。この中でハートは13枚なので、確率は $13/26 = 1/2$ に更新されます。最初は $1/4$ だった確率が、「赤い」という追加情報によって $1/2$ に変わったのです。

このように、ある事象が起きたことを知った上で、別の事象が起きる確率を考えるのが条件付き確率(conditional probability)です。条件付き確率は確率論の最も基本的な概念の一つであり、以下のような幅広い応用があります。

  • 機械学習: 分類問題における事後確率 $P(\text{クラス}|\text{特徴量})$ の計算
  • 医療統計: 検査結果が陽性のとき、実際に病気である確率の評価
  • 品質管理: ある製造工程で不良品が発生したとき、原因の特定
  • 自然言語処理: 文脈を考慮した次の単語の予測確率

本記事の内容

  • 条件付き確率の直感的な理解と数学的定義
  • 乗法定理(確率の積の法則)の導出
  • 確率の連鎖律(chain rule)
  • 独立事象と条件付き確率の関係
  • 具体例による計算練習
  • Pythonでの可視化

前提知識

この記事を読む前に、以下の概念を理解しておくとスムーズです。

条件付き確率とは — 「情報で絞り込む」

日常的な直感

条件付き確率のイメージは、情報によって「あり得る世界」を絞り込むことです。

サイコロを振る前は、1から6のどの目も等確率で $1/6$ です。しかし、「偶数の目が出た」と教えられたら、可能性は $\{2, 4, 6\}$ の3つに限定されます。この新しい「世界」の中で、たとえば「4が出る確率」は $1/3$ になります。

条件付き確率とは、このように標本空間そのものを制限して(条件を加えて)、その制限された空間の中での確率を考えることです。全体の中での割合ではなく、条件を満たすものの中での割合を見ているのです。

ベン図で理解する

ベン図を使うと、条件付き確率の直感が一層明確になります。

事象 $A$ と事象 $B$ が重なる領域 $A \cap B$ を考えます。「$B$ が起きた」と知ったとき、私たちの注目する「世界」は $B$ の領域に限定されます。その $B$ の中で $A$ も起きている割合が、$B$ のもとでの $A$ の条件付き確率です。

つまり、条件付き確率は分母を全体からBに切り替えた確率と考えることができます。

この直感を数式にしたものが、条件付き確率の数学的定義です。

条件付き確率の数学的定義

定義

事象 $B$ の確率が $P(B) > 0$ であるとき、$B$ が起きたという条件のもとでの $A$ の条件付き確率は次のように定義されます。

$$ \begin{equation} P(A|B) = \frac{P(A \cap B)}{P(B)} \end{equation} $$

この定義式が何を表現しているかを確認しましょう。

  • 分子 $P(A \cap B)$: $A$ と $B$ が同時に起きる確率(両方が起きている割合)
  • 分母 $P(B)$: $B$ が起きる確率(条件として与えられた事象の割合)

分子を分母で割るということは、$B$ という条件で「世界」を正規化していることに他なりません。$B$ の中で $A \cap B$ がどれだけの割合を占めるかを計算しているのです。

定義の妥当性

この定義が確率の公理を満たすことを確認しておきましょう。

非負性: $P(A \cap B) \geq 0$ かつ $P(B) > 0$ なので $P(A|B) \geq 0$

正規化: $P(\Omega | B) = P(\Omega \cap B) / P(B) = P(B) / P(B) = 1$

加法性: 排反な事象 $A_1, A_2, \ldots$ に対して

$$ P\left(\bigcup_i A_i \middle| B\right) = \frac{P\left(\bigcup_i (A_i \cap B)\right)}{P(B)} = \frac{\sum_i P(A_i \cap B)}{P(B)} = \sum_i P(A_i | B) $$

したがって、$P(\cdot | B)$ は $B$ を新しい標本空間とした上での正当な確率測度になっています。

直感との整合性

冒頭のトランプの例で確認しましょう。

$A = \{\text{ハート}\}$、$B = \{\text{赤いカード}\}$ として

$$ P(A|B) = \frac{P(A \cap B)}{P(B)} = \frac{P(\text{ハート})}{P(\text{赤})} = \frac{13/52}{26/52} = \frac{13}{26} = \frac{1}{2} $$

ハートは赤いカードの一種なので $A \cap B = A$ です。赤いカード26枚中、ハートは13枚なので $1/2$ になり、直感と一致します。

条件付き確率の定義を理解したところで、この定義から直接導かれる重要な公式 — 乗法定理 — を見ていきましょう。

乗法定理(確率の積の法則)

2事象の乗法定理

条件付き確率の定義式を変形すると、同時確率を条件付き確率で表すことができます。

$$ P(A|B) = \frac{P(A \cap B)}{P(B)} $$

の両辺に $P(B)$ をかけると

$$ \begin{equation} P(A \cap B) = P(A|B) \cdot P(B) \end{equation} $$

同様に、$A$ と $B$ の役割を入れ替えると

$$ P(A \cap B) = P(B|A) \cdot P(A) $$

これが乗法定理(multiplication rule)です。「$A$ と $B$ が同時に起きる確率」は、「まず $B$ が起き、その上で $A$ が起きる確率」と等しいのです。

具体例: 2回連続でエースを引く

52枚のトランプから2枚を引くとき(引いたカードは戻さない)、2枚ともエースである確率を求めましょう。

$A_1 =$ 「1枚目がエース」、$A_2 =$ 「2枚目がエース」とすると

$$ P(A_1 \cap A_2) = P(A_2 | A_1) \cdot P(A_1) $$

$P(A_1) = 4/52 = 1/13$ です。1枚目にエースを引いた後、残り51枚中エースは3枚なので

$$ P(A_2 | A_1) = \frac{3}{51} = \frac{1}{17} $$

よって

$$ P(A_1 \cap A_2) = \frac{1}{17} \times \frac{1}{13} = \frac{1}{221} \approx 0.0045 $$

2枚連続でエースを引く確率は約0.45%です。

確率の連鎖律(chain rule)

乗法定理を3つ以上の事象に拡張すると、確率の連鎖律が得られます。

3つの事象 $A_1, A_2, A_3$ について

$$ P(A_1 \cap A_2 \cap A_3) = P(A_3 | A_1 \cap A_2) \cdot P(A_2 | A_1) \cdot P(A_1) $$

これは右から左に読むと直感的です。「まず $A_1$ が起き($P(A_1)$)、$A_1$ が起きた上で $A_2$ が起き($P(A_2|A_1)$)、$A_1$ と $A_2$ が両方起きた上で $A_3$ が起きる($P(A_3|A_1 \cap A_2)$)」という段階的な条件付けです。

一般に $n$ 個の事象 $A_1, \ldots, A_n$ について

$$ \begin{equation} P\left(\bigcap_{i=1}^{n} A_i\right) = \prod_{i=1}^{n} P\left(A_i \middle| \bigcap_{j=1}^{i-1} A_j\right) \end{equation} $$

ここで $P(A_1 | \bigcap_{j=1}^{0} A_j) = P(A_1)$ と解釈します。

連鎖律の導出

連鎖律は乗法定理を再帰的に適用して導出できます。

まず $n = 2$ のとき、乗法定理そのものです。

$$ P(A_1 \cap A_2) = P(A_2 | A_1) \cdot P(A_1) $$

$n = 3$ のとき、$A_1 \cap A_2$ を1つの事象と見て乗法定理を適用すると

$$ P(A_1 \cap A_2 \cap A_3) = P(A_3 | A_1 \cap A_2) \cdot P(A_1 \cap A_2) $$

右辺の $P(A_1 \cap A_2)$ に $n = 2$ の結果を代入すると

$$ P(A_1 \cap A_2 \cap A_3) = P(A_3 | A_1 \cap A_2) \cdot P(A_2 | A_1) \cdot P(A_1) $$

同じ手順を帰納的に繰り返すことで、一般の $n$ に対する連鎖律が証明されます。

確率の連鎖律は、自然言語処理における言語モデルの基盤にもなっています。文中の単語列 $w_1, w_2, \ldots, w_n$ の同時確率を

$$ P(w_1, w_2, \ldots, w_n) = P(w_1) \cdot P(w_2|w_1) \cdot P(w_3|w_1, w_2) \cdots P(w_n|w_1, \ldots, w_{n-1}) $$

と分解し、各条件付き確率をニューラルネットワーク(Transformer等)で推定するのが現代の言語モデルです。

乗法定理と連鎖律で「同時確率を条件付き確率に分解する」方法がわかりました。次に、条件付き確率の特殊な場合 — 独立性 — について見てみましょう。

独立事象と条件付き確率

独立の定義

2つの事象 $A$ と $B$ が独立(independent)であるとは、一方の事象が起きるかどうかが他方の確率に影響を与えないことです。数式で書くと

$$ P(A|B) = P(A) \quad \text{($P(B) > 0$ のとき)} $$

あるいは、条件付き確率の定義から同値な条件として

$$ \begin{equation} P(A \cap B) = P(A) \cdot P(B) \end{equation} $$

が得られます。この形は $P(A) = 0$ や $P(B) = 0$ の場合も含むため、独立の定義として採用されることが多いです。

独立の直感

独立とは「情報を得ても確率が変わらない」ことです。

サイコロを2回振る場面を考えます。1回目の出目 $A$ と2回目の出目 $B$ は独立です。1回目に6が出たことを知っても、2回目に6が出る確率は変わりません。

一方、52枚のトランプから引いたカードの色(赤か黒か)と絵柄(ハート、ダイヤ、スペード、クラブ)は独立ではありません。「赤いカード」という情報を得ると、絵柄がハートかダイヤであるという情報が得られるからです。

条件付き独立

$A$ と $B$ が $C$ が与えられたもとで条件付き独立(conditionally independent)であるとは

$$ P(A \cap B | C) = P(A|C) \cdot P(B|C) $$

が成り立つことです。$A \perp B | C$ と書きます。

重要な注意点として、独立と条件付き独立は別の概念です。$A$ と $B$ が独立でも、$C$ のもとで条件付き独立とは限りません。逆もまた然りです。

たとえば、2人の学生がそれぞれ独立にテストを受けるとします($A$ = 学生1が合格、$B$ = 学生2が合格)。$A$ と $B$ は独立です。しかし、$C$ = 「テストが非常に難しかった」という情報が与えられると、$A$ が起きた(学生1が合格した)ことは「テストの難易度に対する情報」を提供するため、$B$ の確率に影響を与えます。つまり、$A$ と $B$ は $C$ のもとで条件付き独立とは限りません。

ここまで条件付き確率の理論を整理しました。次に、様々な具体例を通じて計算を練習しましょう。

具体例による計算練習

例1: モンティ・ホール問題

有名な確率のパラドクスを条件付き確率で解いてみましょう。

3つのドアがあり、1つの後ろに車、残り2つの後ろにヤギがいます。あなたがドア1を選んだ後、司会者(全てのドアの中身を知っている)がヤギのいるドア3を開きます。あなたはドア2に変更すべきでしょうか?

$C_i =$ 「車がドア $i$ の後ろにある」とすると、事前確率は

$$ P(C_1) = P(C_2) = P(C_3) = \frac{1}{3} $$

$H_3 =$ 「司会者がドア3を開く」とすると、各仮説のもとでの $H_3$ の確率は

  • $P(H_3 | C_1) = 1/2$(車がドア1なので、司会者はドア2か3を選べる)
  • $P(H_3 | C_2) = 1$(車がドア2なので、司会者はドア3しか開けない)
  • $P(H_3 | C_3) = 0$(車がドア3なので、司会者はドア3を開けない)

ベイズの定理を適用すると

$$ P(C_1 | H_3) = \frac{P(H_3|C_1) P(C_1)}{P(H_3)} = \frac{(1/2)(1/3)}{P(H_3)} $$

$$ P(C_2 | H_3) = \frac{P(H_3|C_2) P(C_2)}{P(H_3)} = \frac{(1)(1/3)}{P(H_3)} $$

$P(H_3)$ は全確率の定理から

$$ P(H_3) = \frac{1}{2} \cdot \frac{1}{3} + 1 \cdot \frac{1}{3} + 0 \cdot \frac{1}{3} = \frac{1}{6} + \frac{1}{3} = \frac{1}{2} $$

よって

$$ P(C_1 | H_3) = \frac{1/6}{1/2} = \frac{1}{3}, \quad P(C_2 | H_3) = \frac{1/3}{1/2} = \frac{2}{3} $$

ドアを変更すべきです。変更すれば当たる確率は $2/3$、変更しなければ $1/3$ です。

例2: 2段階の品質検査

工場の生産ラインでは、製品の5%が不良品です。検査装置Aは不良品を90%の確率で検出し、良品を3%の確率で不良品と誤判定します。検査Aで「不良」と判定された製品が、追加検査Bでも「不良」と判定される確率が95%であるとき、2段階の検査で両方「不良」と判定された製品が本当に不良品である確率を求めます。

$D =$ 不良品、$A^+ =$ 検査Aで不良判定、$B^+ =$ 検査Bで不良判定とします。

まず、検査Aの後にベイズの定理を適用します。

$$ P(D | A^+) = \frac{P(A^+|D) P(D)}{P(A^+|D) P(D) + P(A^+|\bar{D}) P(\bar{D})} $$

各値を代入すると

$$ P(D | A^+) = \frac{0.90 \times 0.05}{0.90 \times 0.05 + 0.03 \times 0.95} = \frac{0.045}{0.045 + 0.0285} = \frac{0.045}{0.0735} \approx 0.612 $$

次に、検査Bの情報を加えます。検査Aで不良と判定された製品が本当に不良のとき、検査Bでも不良と判定される確率が $P(B^+ | D, A^+) = 0.95$ とします。検査Aで不良と判定されたが実は良品のとき、検査Bで不良と判定される確率は $P(B^+ | \bar{D}, A^+) = 0.05$ とします。

$$ P(D | A^+, B^+) = \frac{P(B^+|D, A^+) P(D|A^+)}{P(B^+|D, A^+) P(D|A^+) + P(B^+|\bar{D}, A^+) P(\bar{D}|A^+)} $$

各値を代入すると

$$ P(D | A^+, B^+) = \frac{0.95 \times 0.612}{0.95 \times 0.612 + 0.05 \times 0.388} = \frac{0.5814}{0.5814 + 0.0194} = \frac{0.5814}{0.6008} \approx 0.968 $$

2段階の検査で両方「不良」と判定された製品は、約96.8%の確率で本当に不良品です。検査を重ねることで信頼度が大幅に向上することがわかります。

これらの計算をPythonで実装し、可視化してみましょう。

Pythonでの実装と可視化

条件付き確率の基本的な可視化

まず、条件付き確率の概念をベン図で可視化します。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, FancyArrowPatch
from matplotlib.collections import PatchCollection

fig, axes = plt.subplots(1, 3, figsize=(16, 5))

# --- (a) ベン図で条件付き確率を説明 ---
ax = axes[0]
circle_a = Circle((0.35, 0.5), 0.3, fill=False, edgecolor='blue',
                  linewidth=2, label='A')
circle_b = Circle((0.65, 0.5), 0.3, fill=False, edgecolor='red',
                  linewidth=2, label='B')
ax.add_patch(circle_a)
ax.add_patch(circle_b)

# 交差部分を塗る
theta = np.linspace(0, 2 * np.pi, 300)
xa = 0.35 + 0.3 * np.cos(theta)
ya = 0.5 + 0.3 * np.sin(theta)
xb = 0.65 + 0.3 * np.cos(theta)
yb = 0.5 + 0.3 * np.sin(theta)

# A∩Bの領域を近似的に塗る
xx, yy = np.meshgrid(np.linspace(0, 1, 500), np.linspace(0, 1, 500))
in_a = (xx - 0.35)**2 + (yy - 0.5)**2 <= 0.3**2
in_b = (xx - 0.65)**2 + (yy - 0.5)**2 <= 0.3**2
in_both = in_a & in_b
ax.contourf(xx, yy, in_both.astype(float), levels=[0.5, 1.5],
            colors=['purple'], alpha=0.3)

ax.text(0.2, 0.5, 'A', fontsize=18, fontweight='bold', color='blue',
        ha='center')
ax.text(0.8, 0.5, 'B', fontsize=18, fontweight='bold', color='red',
        ha='center')
ax.text(0.5, 0.5, r'$A \cap B$', fontsize=12, ha='center',
        fontweight='bold', color='purple')
ax.set_xlim(-0.05, 1.05)
ax.set_ylim(0.05, 0.95)
ax.set_aspect('equal')
ax.set_title(r'$P(A|B) = \frac{P(A \cap B)}{P(B)}$', fontsize=14)
ax.axis('off')

# --- (b) サイコロの条件付き確率の例 ---
ax = axes[1]
# P(偶数|4以上) = P({4,6}) / P({4,5,6}) = 2/3
outcomes = list(range(1, 7))
colors_dice = ['lightgray'] * 6
# 4以上を強調
for i in [3, 4, 5]:  # 0-indexed: 4,5,6
    colors_dice[i] = 'lightyellow'
# 偶数かつ4以上を強調
for i in [3, 5]:  # 4, 6
    colors_dice[i] = 'lightgreen'

bars = ax.bar(outcomes, [1/6]*6, color=colors_dice, edgecolor='gray',
              linewidth=1.5, width=0.6)
ax.set_xlabel('Dice outcome', fontsize=12)
ax.set_ylabel('Probability', fontsize=12)
ax.set_title(r'$P(\mathrm{even} | \geq 4) = \frac{2}{3}$', fontsize=13)
ax.set_xticks(outcomes)

# 凡例
from matplotlib.patches import Patch
legend_elements = [
    Patch(facecolor='lightgreen', edgecolor='gray', label=r'Even $\cap$ ($\geq$4)'),
    Patch(facecolor='lightyellow', edgecolor='gray', label=r'$\geq$4 only'),
    Patch(facecolor='lightgray', edgecolor='gray', label='Other'),
]
ax.legend(handles=legend_elements, fontsize=9)
ax.grid(True, alpha=0.3, axis='y')

# --- (c) 独立 vs 非独立の比較 ---
ax = axes[2]
# 独立の例: サイコロ2回
# 非独立の例: トランプから非復元抽出
labels = ['Independent\n(Two dice rolls)', 'Dependent\n(Card draw w/o replace)']
p_a = [1/6, 4/52]  # P(A): 6が出る / エースを引く
p_a_given_b = [1/6, 3/51]  # P(A|B): 1回目が6→2回目に6 / 1枚エース→次もエース
p_a_cap_b = [1/36, 12/2652]  # P(A∩B)
p_a_times_p_b = [(1/6)*(1/6), (4/52)*(4/52)]  # P(A)*P(B)

x_pos = np.arange(len(labels))
width = 0.3
bars1 = ax.bar(x_pos - width/2, p_a_cap_b, width, color='steelblue',
               alpha=0.8, label=r'$P(A \cap B)$')
bars2 = ax.bar(x_pos + width/2, p_a_times_p_b, width, color='salmon',
               alpha=0.8, label=r'$P(A) \cdot P(B)$')

ax.set_ylabel('Probability', fontsize=12)
ax.set_title('Independence Test:\n' + r'$P(A \cap B) = P(A) \cdot P(B)$?',
             fontsize=13)
ax.set_xticks(x_pos)
ax.set_xticklabels(labels, fontsize=10)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3, axis='y')

# 値のアノテーション
for bar in bars1:
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.001,
            f'{bar.get_height():.4f}', ha='center', fontsize=9)
for bar in bars2:
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.001,
            f'{bar.get_height():.4f}', ha='center', fontsize=9)

plt.tight_layout()
plt.savefig('conditional_probability_basics.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフから、条件付き確率の基本的な性質が読み取れます。

  1. 左図(ベン図): 条件付き確率 $P(A|B)$ は、$B$ の領域(赤い円)を新しい「全体」として、その中で $A \cap B$(紫の領域)が占める割合です。$B$ が起きたことを知ることで、注目する「世界」が $B$ の中に制限されるのがポイントです。

  2. 中央図(サイコロの例): サイコロの出目が4以上(黄色 + 緑)という条件のもとで偶数(緑)である確率は $2/3$ です。全体(6通り)ではなく、条件を満たす部分(3通り)の中で数えていることが色分けで明確にわかります。

  3. 右図(独立性の検証): サイコロ2回の場合は $P(A \cap B) = P(A) \cdot P(B)$(青と赤の棒が同じ高さ)で独立ですが、トランプの非復元抽出では $P(A \cap B) < P(A) \cdot P(B)$(青が赤より低い)で独立ではありません。1枚引いた後にデッキの構成が変わるためです。

モンティ・ホール問題のシミュレーション

理論で求めた結果をシミュレーションで確認します。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
n_simulations = 100000

# モンティ・ホール問題のシミュレーション
wins_stay = 0
wins_switch = 0

for _ in range(n_simulations):
    # 車をランダムに配置(0, 1, 2のいずれか)
    car = np.random.randint(3)
    # プレイヤーの選択(常にドア0を選ぶ)
    choice = 0

    # 司会者がヤギのドアを開く(プレイヤーの選択と車のドア以外)
    available = [d for d in range(3) if d != choice and d != car]
    opened = available[np.random.randint(len(available))]

    # 変更先のドア
    switch_to = [d for d in range(3) if d != choice and d != opened][0]

    if choice == car:
        wins_stay += 1
    if switch_to == car:
        wins_switch += 1

prob_stay = wins_stay / n_simulations
prob_switch = wins_switch / n_simulations

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# (a) シミュレーション結果
ax = axes[0]
strategies = ['Stay', 'Switch']
probs = [prob_stay, prob_switch]
theory = [1/3, 2/3]
x_pos = np.arange(2)
width = 0.3

bars1 = ax.bar(x_pos - width/2, probs, width, color='steelblue',
               alpha=0.8, label='Simulation')
bars2 = ax.bar(x_pos + width/2, theory, width, color='salmon',
               alpha=0.8, label='Theory')
ax.set_ylabel('Win Probability', fontsize=12)
ax.set_title(f'Monty Hall Problem ({n_simulations:,} trials)', fontsize=13)
ax.set_xticks(x_pos)
ax.set_xticklabels(strategies, fontsize=12)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3, axis='y')

for bar in bars1:
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
            f'{bar.get_height():.4f}', ha='center', fontsize=11)
for bar in bars2:
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
            f'{bar.get_height():.4f}', ha='center', fontsize=11)

# (b) 累積勝率の推移
ax = axes[1]
n_track = 10000
cum_stay = np.zeros(n_track)
cum_switch = np.zeros(n_track)

for i in range(n_track):
    car = np.random.randint(3)
    choice = 0
    available = [d for d in range(3) if d != choice and d != car]
    opened = available[np.random.randint(len(available))]
    switch_to = [d for d in range(3) if d != choice and d != opened][0]

    cum_stay[i] = (cum_stay[i-1] * i + (1 if choice == car else 0)) / (i + 1) if i > 0 else (1 if choice == car else 0)
    cum_switch[i] = (cum_switch[i-1] * i + (1 if switch_to == car else 0)) / (i + 1) if i > 0 else (1 if switch_to == car else 0)

ax.plot(range(1, n_track+1), cum_stay, 'b-', alpha=0.7, linewidth=1.5,
        label='Stay')
ax.plot(range(1, n_track+1), cum_switch, 'r-', alpha=0.7, linewidth=1.5,
        label='Switch')
ax.axhline(1/3, color='blue', linestyle='--', alpha=0.4)
ax.axhline(2/3, color='red', linestyle='--', alpha=0.4)
ax.set_xlabel('Number of Trials', fontsize=12)
ax.set_ylabel('Cumulative Win Rate', fontsize=12)
ax.set_title('Convergence of Win Rate', fontsize=13)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.set_xscale('log')

plt.tight_layout()
plt.savefig('monty_hall_simulation.png', dpi=150, bbox_inches='tight')
plt.show()

このシミュレーション結果から、モンティ・ホール問題の直感に反する結論が確認できます。

  1. 左図: 100,000回のシミュレーションで、変更した場合の勝率(約0.667)は変更しなかった場合の勝率(約0.333)のちょうど2倍です。理論値($1/3$ と $2/3$)と非常によく一致しています。

  2. 右図: 試行回数の増加に伴い、累積勝率が理論値に収束していく様子が見えます。初期には揺らぎがありますが、100回を超えた辺りから安定し始め、1,000回を超えるとほぼ理論値に一致しています。これは大数の法則の現れです。

確率の連鎖律の可視化

最後に、確率の連鎖律を樹形図で可視化し、各枝の確率を示します。

import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1, figsize=(14, 8))

# 品質検査の樹形図
# 不良率 5%, 検査A感度 90%, 検査A偽陽性率 3%
# 検査B(Aで不良判定後): 不良品を95%検出, 良品の5%を誤判定

# レベル0: 不良 vs 良品
ax.text(0.05, 0.5, 'Product', fontsize=13, fontweight='bold',
        ha='center', va='center',
        bbox=dict(boxstyle='round,pad=0.3', facecolor='lightyellow'))

# レベル1: 不良品 / 良品
y_defect = 0.75
y_good = 0.25
ax.annotate('Defective\nP=0.05', xy=(0.15, 0.55), xytext=(0.25, y_defect),
            fontsize=11, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.3', facecolor='lightsalmon'),
            arrowprops=dict(arrowstyle='->', color='red', lw=1.5))
ax.annotate('Good\nP=0.95', xy=(0.15, 0.45), xytext=(0.25, y_good),
            fontsize=11, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.3', facecolor='lightgreen'),
            arrowprops=dict(arrowstyle='->', color='green', lw=1.5))

# レベル2: 検査Aの結果
# 不良品 → A+, A-
ax.annotate('A+\nP(A+|D)=0.90', xy=(0.33, y_defect), xytext=(0.48, 0.88),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightsalmon', alpha=0.7),
            arrowprops=dict(arrowstyle='->', color='red', lw=1.2))
ax.annotate('A-\nP(A-|D)=0.10', xy=(0.33, y_defect), xytext=(0.48, 0.62),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightgray', alpha=0.7),
            arrowprops=dict(arrowstyle='->', color='gray', lw=1.2))

# 良品 → A+, A-
ax.annotate('A+\nP(A+|G)=0.03', xy=(0.33, y_good), xytext=(0.48, 0.38),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightsalmon', alpha=0.5),
            arrowprops=dict(arrowstyle='->', color='orange', lw=1.2))
ax.annotate('A-\nP(A-|G)=0.97', xy=(0.33, y_good), xytext=(0.48, 0.12),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightgreen', alpha=0.7),
            arrowprops=dict(arrowstyle='->', color='green', lw=1.2))

# レベル3: 検査Bの結果(A+のもの)
# 不良+A+ → B+
ax.annotate('B+: P=0.95', xy=(0.56, 0.88), xytext=(0.72, 0.95),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightsalmon', alpha=0.5),
            arrowprops=dict(arrowstyle='->', color='red', lw=1.2))
ax.annotate('B-: P=0.05', xy=(0.56, 0.88), xytext=(0.72, 0.82),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightgray', alpha=0.5),
            arrowprops=dict(arrowstyle='->', color='gray', lw=1.2))

# 良品+A+ → B+
ax.annotate('B+: P=0.05', xy=(0.56, 0.38), xytext=(0.72, 0.45),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightsalmon', alpha=0.3),
            arrowprops=dict(arrowstyle='->', color='orange', lw=1.2))
ax.annotate('B-: P=0.95', xy=(0.56, 0.38), xytext=(0.72, 0.31),
            fontsize=10, ha='center', va='center',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='lightgreen', alpha=0.5),
            arrowprops=dict(arrowstyle='->', color='green', lw=1.2))

# 最終確率
ax.text(0.9, 0.95, 'Joint P\n= 0.05 x 0.90 x 0.95\n= 0.04275', fontsize=9,
        ha='center', va='center',
        bbox=dict(boxstyle='round,pad=0.2', facecolor='white', edgecolor='red'))
ax.text(0.9, 0.45, 'Joint P\n= 0.95 x 0.03 x 0.05\n= 0.001425', fontsize=9,
        ha='center', va='center',
        bbox=dict(boxstyle='round,pad=0.2', facecolor='white', edgecolor='orange'))

ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_title('Probability Tree: Two-Stage Quality Inspection\n'
             'Chain Rule: P(D, A+, B+) = P(B+|D,A+) P(A+|D) P(D)',
             fontsize=14)
ax.axis('off')

plt.tight_layout()
plt.savefig('chain_rule_tree.png', dpi=150, bbox_inches='tight')
plt.show()

# 最終的な事後確率の計算
p_d_a_b = 0.05 * 0.90 * 0.95  # P(D, A+, B+)
p_g_a_b = 0.95 * 0.03 * 0.05  # P(G, A+, B+)
posterior = p_d_a_b / (p_d_a_b + p_g_a_b)
print(f"P(Defective | A+, B+) = {p_d_a_b:.6f} / {p_d_a_b + p_g_a_b:.6f} = {posterior:.4f}")

この樹形図から、確率の連鎖律がどのように同時確率の計算に使われるかが視覚的に理解できます。

  1. 連鎖律の適用: 「不良品 かつ 検査Aで不良判定 かつ 検査Bで不良判定」の同時確率は、各段階の条件付き確率の積 $P(D) \times P(A^+|D) \times P(B^+|D, A^+) = 0.05 \times 0.90 \times 0.95 = 0.04275$ として計算されます。

  2. ベイズの定理への接続: 事後確率 $P(D|A^+, B^+)$ は、分子が「不良品で両方陽性」の同時確率、分母が「両方陽性」の周辺確率(不良品のルートと良品のルートの合計)で、$0.04275 / (0.04275 + 0.001425) \approx 0.968$ となります。

  3. 2段階検査の効果: 検査Aだけでは $P(D|A^+) \approx 0.612$ だったのが、検査Bを追加することで $P(D|A^+, B^+) \approx 0.968$ まで上昇しています。条件付き確率の逐次適用により、段階的に信頼度が向上していくことがわかります。

まとめ

本記事では、条件付き確率の定義から乗法定理、確率の連鎖律、そして具体的な応用例まで解説しました。

  • 条件付き確率 $P(A|B) = P(A \cap B) / P(B)$ は、事象 $B$ が起きたことを知った上での $A$ の確率であり、「分母を全体から $B$ に切り替えた確率」と理解できる
  • 乗法定理 $P(A \cap B) = P(A|B) P(B)$ は同時確率を条件付き確率に分解する基本公式であり、確率の連鎖律へと一般化される
  • 確率の連鎖律は、同時確率を段階的な条件付き確率の積として表現し、言語モデルなどの基盤となっている
  • 独立事象とは $P(A|B) = P(A)$ となる場合であり、一方の情報が他方の確率に影響を与えない
  • モンティ・ホール問題や品質検査の例が示すように、条件付き確率を正しく適用することで直感に反する結果を理論的に説明できる

次のステップとして、以下の記事も参考にしてください。