マルチンゲール入門 — 公平なギャンブルの数学

カジノで賭けをするとき、「前回負けたら次は倍額を賭ける」というマルチンゲール戦略は有名ですが、この戦略は本当に儲かるのでしょうか? 直感的には、いつか勝てば取り返せるように思えます。しかし、確率論の マルチンゲール理論 は、公平なゲームでは(適切な条件のもとで)どのような賭け方をしても期待利益はゼロであることを厳密に示します。

マルチンゲール(martingale) は、「現在までの情報に基づく未来の条件付き期待値が、現在の値に等しい」という性質を持つ確率過程です。これは「公平なギャンブル」の数学的定式化であり、確率論の最も美しく強力な概念の一つです。確率微分方程式、金融工学(オプション価格理論)、統計学(逐次解析)、機械学習など、幅広い分野の理論的基盤になっています。

本記事の内容

  • マルチンゲールの直感(公平なギャンブル)
  • 条件付き期待値の復習
  • マルチンゲールの定義 $E[X_{n+1} \mid \mathcal{F}_n] = X_n$
  • 劣マルチンゲール・優マルチンゲール
  • マルチンゲールの具体例
  • 任意抽出定理(オプショナル・ストッピング定理)の証明スケッチと応用
  • マルチンゲール収束定理
  • Pythonで賭け戦略シミュレーション

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

条件付き期待値の復習

マルチンゲールの定義には 条件付き期待値 が中心的な役割を果たすため、まずその性質を復習します。

σ加法族とフィルトレーション

確率空間 $(\Omega, \mathcal{F}, P)$ において、$\mathcal{G} \subset \mathcal{F}$ がσ加法族であるとき、$E[X \mid \mathcal{G}]$ は $\mathcal{G}$-可測な確率変数で、任意の $A \in \mathcal{G}$ に対して

$$ \int_A E[X \mid \mathcal{G}] \, dP = \int_A X \, dP $$

を満たします。

フィルトレーション(filtration) $\{\mathcal{F}_n\}_{n \geq 0}$ とは、σ加法族の増大列

$$ \mathcal{F}_0 \subset \mathcal{F}_1 \subset \mathcal{F}_2 \subset \cdots \subset \mathcal{F} $$

のことです。$\mathcal{F}_n$ は「時刻 $n$ までに得られる情報」を表します。

条件付き期待値の基本性質

以下の性質を頻繁に使います。$\mathcal{G} \subset \mathcal{H} \subset \mathcal{F}$ とします。

性質1: 塔の性質(tower property / law of iterated expectations)

$$ E[E[X \mid \mathcal{H}] \mid \mathcal{G}] = E[X \mid \mathcal{G}] $$

より細かい情報 $\mathcal{H}$ で条件付けた後、粗い情報 $\mathcal{G}$ で条件付けると、最初から $\mathcal{G}$ で条件付けたのと同じです。

性質2: 取り出し規則(pull-out property)

$Y$ が $\mathcal{G}$-可測であるとき、

$$ E[YX \mid \mathcal{G}] = Y \cdot E[X \mid \mathcal{G}] $$

性質3: 独立性

$X$ が $\mathcal{G}$ と独立であるとき、

$$ E[X \mid \mathcal{G}] = E[X] $$

性質4: 線形性

$$ E[aX + bY \mid \mathcal{G}] = a E[X \mid \mathcal{G}] + b E[Y \mid \mathcal{G}] $$

性質5: 全期待値の法則

$$ E[E[X \mid \mathcal{G}]] = E[X] $$

マルチンゲールの定義

定義

定義(マルチンゲール): 確率過程 $\{X_n\}_{n \geq 0}$ がフィルトレーション $\{\mathcal{F}_n\}$ に関する マルチンゲール(martingale) であるとは、以下の3つの条件を満たすことをいいます。

(M1) 適合性: 各 $n$ に対して、$X_n$ は $\mathcal{F}_n$-可測である。(現在の値は現在の情報で決まる)

(M2) 可積分性: 各 $n$ に対して、$E[|X_n|] < \infty$。(期待値が有限)

(M3) マルチンゲール条件:

$$ E[X_{n+1} \mid \mathcal{F}_n] = X_n \quad \forall n \geq 0 $$

条件(M3)の意味: 「時刻 $n$ までの情報が与えられたとき、次の時刻 $n+1$ での値の条件付き期待値は現在の値 $X_n$ に等しい」。

等価な表現

マルチンゲール条件(M3)は以下の形に等価的に書き換えられます。

増分の条件付き期待値が0:

$$ E[X_{n+1} – X_n \mid \mathcal{F}_n] = 0 $$

これは「次のステップでの変化の期待値がゼロ」、つまり「公平なゲーム」を意味します。

多段先の条件(塔の性質から): $m > n$ に対して、

$$ E[X_m \mid \mathcal{F}_n] = X_n $$

証明: $m = n + 2$ の場合で示します。

$$ \begin{align} E[X_{n+2} \mid \mathcal{F}_n] &= E[E[X_{n+2} \mid \mathcal{F}_{n+1}] \mid \mathcal{F}_n] \quad (\text{塔の性質}) \\ &= E[X_{n+1} \mid \mathcal{F}_n] \quad (\because \text{マルチンゲール条件}) \\ &= X_n \quad (\because \text{マルチンゲール条件}) \end{align} $$

帰納法で一般の $m > n$ に拡張できます。$\square$

無条件期待値の不変性: 全期待値の法則を使うと、

$$ E[X_n] = E[E[X_n \mid \mathcal{F}_0]] = E[X_0] \quad \forall n $$

マルチンゲールの期待値は時間によらず一定です。

劣マルチンゲールと優マルチンゲール

マルチンゲールの「等号」を「不等号」に緩めた概念です。

劣マルチンゲール

定義: $\{X_n\}$ が 劣マルチンゲール(submartingale) であるとは、(M1)(M2)に加えて、

$$ E[X_{n+1} \mid \mathcal{F}_n] \geq X_n \quad \forall n $$

を満たすことをいいます。

直感的には、「平均的に増加する(有利なゲーム)」過程です。$E[X_n]$ は $n$ について非減少です。

優マルチンゲール

定義: $\{X_n\}$ が 優マルチンゲール(supermartingale) であるとは、(M1)(M2)に加えて、

$$ E[X_{n+1} \mid \mathcal{F}_n] \leq X_n \quad \forall n $$

を満たすことをいいます。

直感的には、「平均的に減少する(不利なゲーム)」過程です。$E[X_n]$ は $n$ について非増加です。

関係の整理

$$ \text{マルチンゲール} = \text{劣マルチンゲール} \cap \text{優マルチンゲール} $$

また、$\{X_n\}$ がマルチンゲールであれば、$f$ が凸関数で $E[|f(X_n)|] < \infty$ のとき、イェンセンの不等式 より

$$ E[f(X_{n+1}) \mid \mathcal{F}_n] \geq f(E[X_{n+1} \mid \mathcal{F}_n]) = f(X_n) $$

したがって $\{f(X_n)\}$ は劣マルチンゲールです。特に、$\{X_n^2\}$ や $\{|X_n|\}$ は劣マルチンゲールです。

マルチンゲールの例

例1: 対称ランダムウォーク

$\{Y_k\}$ を独立同分布で $P(Y_k = +1) = P(Y_k = -1) = 1/2$ とし、$S_n = \sum_{k=1}^{n} Y_k$, $S_0 = 0$ とします。

$\mathcal{F}_n = \sigma(Y_1, \dots, Y_n)$ とすると、

$$ \begin{align} E[S_{n+1} \mid \mathcal{F}_n] &= E[S_n + Y_{n+1} \mid \mathcal{F}_n] \\ &= S_n + E[Y_{n+1} \mid \mathcal{F}_n] \\ &= S_n + E[Y_{n+1}] \quad (\because Y_{n+1} \perp \mathcal{F}_n) \\ &= S_n + 0 \\ &= S_n \end{align} $$

よって $\{S_n\}$ はマルチンゲールです。これは「公平なコイン投げの累積スコア」です。

例2: 偏りのあるランダムウォークの変換

$P(Y_k = +1) = p$, $P(Y_k = -1) = 1 – p$ で $p \neq 1/2$ の場合、$S_n$ はマルチンゲールではありません($E[Y_k] = 2p – 1 \neq 0$)。

しかし、$M_n = S_n – n(2p – 1)$ とおくと、

$$ E[M_{n+1} \mid \mathcal{F}_n] = E[S_{n+1} \mid \mathcal{F}_n] – (n+1)(2p-1) = S_n + (2p-1) – (n+1)(2p-1) = M_n $$

よって $\{M_n\}$ はマルチンゲールです。「ドリフトを引き去る」ことでマルチンゲールになります。

例3: 指数マルチンゲール

$\{Y_k\}$ が独立同分布で $E[e^{\theta Y_k}]$ が存在するとき、積率母関数 を $\phi(\theta) = E[e^{\theta Y_k}]$ とします。

$$ M_n = \frac{\exp(\theta S_n)}{\phi(\theta)^n} $$

がマルチンゲールであることを示します。

$$ \begin{align} E[M_{n+1} \mid \mathcal{F}_n] &= E\left[\frac{e^{\theta S_{n+1}}}{\phi(\theta)^{n+1}} \mid \mathcal{F}_n\right] \\ &= \frac{e^{\theta S_n}}{\phi(\theta)^{n+1}} E\left[e^{\theta Y_{n+1}} \mid \mathcal{F}_n\right] \\ &= \frac{e^{\theta S_n}}{\phi(\theta)^{n+1}} \cdot \phi(\theta) \\ &= \frac{e^{\theta S_n}}{\phi(\theta)^n} = M_n \end{align} $$

取り出し規則($e^{\theta S_n}$ は $\mathcal{F}_n$-可測)と独立性($Y_{n+1} \perp \mathcal{F}_n$)を用いました。$\square$

例4: 条件付き期待値過程

可積分な確率変数 $Z$($E[|Z|] < \infty$)に対して、

$$ X_n = E[Z \mid \mathcal{F}_n] $$

はマルチンゲールです。

証明: 塔の性質を用いると、

$$ E[X_{n+1} \mid \mathcal{F}_n] = E[E[Z \mid \mathcal{F}_{n+1}] \mid \mathcal{F}_n] = E[Z \mid \mathcal{F}_n] = X_n $$

$\square$

この例は非常に一般的であり、実は(一様可積分な)マルチンゲールはすべてこの形に書けることが知られています(マルチンゲール収束定理と関連)。

例5: 尤度比過程

確率測度 $P$ と $Q$ が同じσ加法族上で定義され、$Q \ll P$($Q$ は $P$ に対して絶対連続)であるとします。$\mathcal{F}_n$ 上のラドン=ニコディム微分を

$$ L_n = \frac{dQ|_{\mathcal{F}_n}}{dP|_{\mathcal{F}_n}} $$

と定義すると、$\{L_n\}$ は $P$ のもとでマルチンゲールです。

証明: 任意の $A \in \mathcal{F}_n$ に対して、

$$ \begin{align} E_P[L_{n+1} \cdot \mathbf{1}_A] &= Q(A) \quad (\text{ラドン=ニコディムの定義}) \\ &= E_P[L_n \cdot \mathbf{1}_A] \quad (\because A \in \mathcal{F}_n) \end{align} $$

これは $E_P[L_{n+1} \mid \mathcal{F}_n] = L_n$ を意味します。$\square$

尤度比過程は逐次検定(Wald検定)や仮説検定理論の基盤です。

例6: ブラウン運動

前回の記事で示した通り、標準ブラウン運動 $\{W(t)\}$ は連続時間のマルチンゲールです。

さらに、$\{W(t)^2 – t\}$ もマルチンゲールです。伊藤の公式から $d(W^2) = 2W \, dW + dt$ なので、

$$ W(t)^2 – t = 2\int_0^t W(s) \, dW(s) $$

伊藤積分はマルチンゲールなので、$\{W(t)^2 – t\}$ もマルチンゲールです。

任意抽出定理(オプショナル・ストッピング定理)

停止時刻

定義: 確率変数 $\tau : \Omega \to \{0, 1, 2, \dots\} \cup \{\infty\}$ が 停止時刻(stopping time) であるとは、任意の $n \geq 0$ に対して、

$$ \{\tau = n\} \in \mathcal{F}_n $$

が成り立つことをいいます。

直感的には、「$\tau$ で止めるかどうかの判断は、時刻 $\tau$ までの情報のみで行える」ということです。未来の情報を使わない「合法的な」停止規則です。

: ギャンブルで「所持金が0になったら止める」は停止時刻です(現在の所持金は現在の情報)。「将来最大値をとる時刻で止める」は停止時刻ではありません(未来の情報が必要)。

任意抽出定理の主張

定理(任意抽出定理, Optional Stopping Theorem): $\{X_n\}$ がマルチンゲールで、$\tau$ が有界な停止時刻(ある定数 $N$ が存在して $\tau \leq N$ a.s.)であるとき、

$$ E[X_\tau] = E[X_0] $$

証明

$\tau \leq N$ なので、

$$ X_\tau = \sum_{n=0}^{N} X_n \cdot \mathbf{1}(\tau = n) $$

と書けます。

$$ \begin{align} E[X_\tau] &= E\left[\sum_{n=0}^{N} X_n \cdot \mathbf{1}(\tau = n)\right] \\ &= \sum_{n=0}^{N} E[X_n \cdot \mathbf{1}(\tau = n)] \end{align} $$

ここで、$X_n = X_N – \sum_{k=n}^{N-1}(X_{k+1} – X_k)$ と書くと複雑になるので、別のアプローチを取ります。

テレスコーピング: $X_\tau = X_0 + \sum_{n=0}^{N-1}(X_{n+1} – X_n)\mathbf{1}(\tau > n)$ と書けます。これは、「時刻 $n$ でまだ止まっていなければ、$n$ から $n+1$ の増分が加算される」ことを意味します。

$$ E[X_\tau] = E[X_0] + \sum_{n=0}^{N-1} E[(X_{n+1} – X_n)\mathbf{1}(\tau > n)] $$

$\{\tau > n\} = \{\tau \leq n\}^c$ であり、$\{\tau \leq n\} \in \mathcal{F}_n$ なので $\mathbf{1}(\tau > n)$ は $\mathcal{F}_n$-可測です。したがって、

$$ \begin{align} E[(X_{n+1} – X_n)\mathbf{1}(\tau > n)] &= E[E[(X_{n+1} – X_n)\mathbf{1}(\tau > n) \mid \mathcal{F}_n]] \\ &= E[\mathbf{1}(\tau > n) \cdot E[X_{n+1} – X_n \mid \mathcal{F}_n]] \quad (\text{取り出し規則}) \\ &= E[\mathbf{1}(\tau > n) \cdot 0] \quad (\because \text{マルチンゲール条件}) \\ &= 0 \end{align} $$

したがって、

$$ E[X_\tau] = E[X_0] + 0 = E[X_0] $$

$\square$

一般的な任意抽出定理

有界でない停止時刻に対しても、追加の条件のもとで同様の結論が成り立ちます。

定理(一般の任意抽出定理): $\{X_n\}$ がマルチンゲールで、$\tau$ が停止時刻であるとき、以下のいずれかの条件が成り立てば $E[X_\tau] = E[X_0]$:

  1. $\tau$ が a.s. 有限で、$\{X_{n \wedge \tau}\}$ が一様可積分
  2. $E[\tau] < \infty$ かつ増分 $|X_{n+1} - X_n|$ が a.s. 有界

応用: ギャンブラーの破産問題

対称ランダムウォーク $S_n = \sum_{k=1}^n Y_k$($P(Y_k = \pm 1) = 1/2$)で、初期所持金 $S_0 = a$($0 < a < N$)とします。$\tau = \min\{n : S_n = 0 \text{ or } S_n = N\}$ とします。

$\{S_n\}$ はマルチンゲールであり、$0 \leq S_{n \wedge \tau} \leq N$ で有界です。停止されたマルチンゲール $\{S_{n \wedge \tau}\}$ は一様可積分なので、任意抽出定理が適用できます。

$$ E[S_\tau] = E[S_0] = a $$

$P(S_\tau = N) = q$, $P(S_\tau = 0) = 1 – q$ とすると、

$$ N \cdot q + 0 \cdot (1-q) = a $$

$$ q = \frac{a}{N} $$

初期所持金 $a = 5$、目標 $N = 10$ なら、目標達成確率は $5/10 = 50\%$ です。

さらに、$\{S_n^2 – n\}$ もマルチンゲールであることを利用して $E[\tau]$ を求めます。

$$ E[S_\tau^2 – \tau] = E[S_0^2 – 0] = a^2 $$

$$ E[S_\tau^2] = N^2 \cdot q + 0 \cdot (1-q) = N^2 \cdot \frac{a}{N} = Na $$

$$ Na – E[\tau] = a^2 $$

$$ E[\tau] = Na – a^2 = a(N – a) $$

$a = 5$, $N = 10$ なら $E[\tau] = 5 \times 5 = 25$ ステップです。

マルチンゲール収束定理

Doobのマルチンゲール収束定理

定理: $\{X_n\}$ が劣マルチンゲールで、$\sup_n E[X_n^+] < \infty$(ここで $X^+ = \max(X, 0)$)であるとき、

$$ X_\infty = \lim_{n \to \infty} X_n $$

が a.s. に存在して有限値をとる。

特に、非負マルチンゲール $\{X_n\}$($X_n \geq 0$)は常に a.s. に収束します($E[X_n] = E[X_0] < \infty$ なので $\sup_n E[X_n^+] = E[X_0] < \infty$)。

直感的な説明

劣マルチンゲールは「平均的に増加する」過程ですが、$E[X_n^+]$ が有界ということは、上への発散が抑えられていることを意味します。このとき、過程は上方向にも下方向にも無限に振動し続けることができず、ある有限値に落ち着かざるを得ません。

もう少し正確には、$X_n$ が区間 $[a, b]$($a < b$)を何度も上下に横切る回数(アップクロッシング数)が有限であることを示し(Doobのアップクロッシング不等式)、そこから極限の存在を導きます。

注意: 収束先が元の期待値と一致するとは限らない

$E[X_\infty] = E[X_0]$ は必ずしも成り立ちません。等号が成り立つための追加条件が 一様可積分性 です。

$\{X_n\}$ が一様可積分なマルチンゲールであれば、$X_n \to X_\infty$ a.s. かつ $L^1$ で収束し、$E[X_\infty] = E[X_0]$ が成り立ちます。さらに $X_n = E[X_\infty \mid \mathcal{F}_n]$ と表されます。

Pythonでの実装

マルチンゲールの基本シミュレーション

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

n_steps = 500
n_paths = 20

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

# --- (1) 対称ランダムウォーク(マルチンゲール) ---
for i in range(n_paths):
    steps = np.random.choice([-1, 1], size=n_steps)
    S = np.cumsum(steps)
    S = np.insert(S, 0, 0)
    axes[0, 0].plot(S, alpha=0.5, linewidth=0.5)
axes[0, 0].axhline(y=0, color='r', linestyle='--', linewidth=1.5)
axes[0, 0].set_title("Martingale: Symmetric Random Walk\n$E[S_{n+1}|\\mathcal{F}_n] = S_n$")
axes[0, 0].set_xlabel("Step n")
axes[0, 0].set_ylabel("$S_n$")
axes[0, 0].grid(True, alpha=0.3)

# --- (2) 偏ったランダムウォーク(劣マルチンゲール, p=0.55) ---
p = 0.55
for i in range(n_paths):
    steps = np.where(np.random.random(n_steps) < p, 1, -1)
    S = np.cumsum(steps)
    S = np.insert(S, 0, 0)
    axes[0, 1].plot(S, alpha=0.5, linewidth=0.5)
# 期待値の理論線: E[S_n] = n(2p-1)
n_array = np.arange(n_steps + 1)
axes[0, 1].plot(n_array, n_array * (2*p - 1), 'r-', linewidth=2,
                label=f'E[S_n] = {2*p-1:.2f}n')
axes[0, 1].set_title(f"Submartingale: Biased Random Walk (p={p})\n$E[S_{{n+1}}|\\mathcal{{F}}_n] \\geq S_n$")
axes[0, 1].set_xlabel("Step n")
axes[0, 1].set_ylabel("$S_n$")
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)

# --- (3) 指数マルチンゲール ---
theta = 0.5
# P(Y=1) = P(Y=-1) = 1/2 の場合
# phi(theta) = (e^theta + e^{-theta})/2 = cosh(theta)
phi = np.cosh(theta)

for i in range(n_paths):
    steps = np.random.choice([-1, 1], size=n_steps)
    S = np.cumsum(steps)
    S = np.insert(S, 0, 0)
    M_exp = np.exp(theta * S) / phi**n_array
    axes[1, 0].plot(M_exp, alpha=0.5, linewidth=0.5)
axes[1, 0].axhline(y=1, color='r', linestyle='--', linewidth=1.5, label='$E[M_n] = 1$')
axes[1, 0].set_title(f"Exponential Martingale ($\\theta$={theta})\n$M_n = e^{{\\theta S_n}} / \\phi(\\theta)^n$")
axes[1, 0].set_xlabel("Step n")
axes[1, 0].set_ylabel("$M_n$")
axes[1, 0].set_ylim(0, 5)
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)

# --- (4) E[X_n] の不変性の検証 ---
n_paths_check = 10000
final_values_martingale = np.zeros(n_paths_check)
final_values_sub = np.zeros(n_paths_check)

for i in range(n_paths_check):
    # マルチンゲール
    steps_m = np.random.choice([-1, 1], size=n_steps)
    final_values_martingale[i] = np.sum(steps_m)
    # 劣マルチンゲール
    steps_s = np.where(np.random.random(n_steps) < 0.55, 1, -1)
    final_values_sub[i] = np.sum(steps_s)

# E[S_n] を各ステップで計算
mean_paths_m = np.zeros(n_steps + 1)
mean_paths_s = np.zeros(n_steps + 1)
all_paths_m = np.zeros((1000, n_steps + 1))
all_paths_s = np.zeros((1000, n_steps + 1))

for i in range(1000):
    steps_m = np.random.choice([-1, 1], size=n_steps)
    all_paths_m[i] = np.insert(np.cumsum(steps_m), 0, 0)
    steps_s = np.where(np.random.random(n_steps) < 0.55, 1, -1)
    all_paths_s[i] = np.insert(np.cumsum(steps_s), 0, 0)

mean_paths_m = np.mean(all_paths_m, axis=0)
mean_paths_s = np.mean(all_paths_s, axis=0)

axes[1, 1].plot(mean_paths_m, 'b-', linewidth=2, label='Martingale E[$S_n$]')
axes[1, 1].plot(mean_paths_s, 'r-', linewidth=2, label='Submartingale E[$S_n$]')
axes[1, 1].axhline(y=0, color='k', linestyle='--', linewidth=0.5)
axes[1, 1].set_title("E[$X_n$] over time (1000 paths)")
axes[1, 1].set_xlabel("Step n")
axes[1, 1].set_ylabel("E[$S_n$]")
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("martingale_examples.png", dpi=150, bbox_inches="tight")
plt.show()

任意抽出定理の数値検証

ギャンブラーの破産問題を用いて、任意抽出定理 $E[S_\tau] = S_0$ を数値的に検証します。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

# ギャンブラーの破産問題
# 対称ランダムウォーク: P(+1) = P(-1) = 1/2
# 停止時刻: S_tau = 0 (破産) or S_tau = N (目標達成)
N = 20  # 目標
a_values = np.arange(1, N)  # 初期所持金

n_simulations = 50000

results = {
    'a': [],
    'win_prob_sim': [],
    'win_prob_theory': [],
    'E_tau_sim': [],
    'E_tau_theory': [],
    'E_Stau_sim': [],
}

for a in a_values:
    wins = 0
    total_steps = 0
    final_values = []

    for _ in range(n_simulations):
        fortune = a
        steps_count = 0
        while 0 < fortune < N:
            fortune += np.random.choice([-1, 1])
            steps_count += 1
        wins += (fortune == N)
        total_steps += steps_count
        final_values.append(fortune)

    results['a'].append(a)
    results['win_prob_sim'].append(wins / n_simulations)
    results['win_prob_theory'].append(a / N)
    results['E_tau_sim'].append(total_steps / n_simulations)
    results['E_tau_theory'].append(a * (N - a))
    results['E_Stau_sim'].append(np.mean(final_values))

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

# (1) 目標達成確率
axes[0].plot(a_values, results['win_prob_sim'], 'bo', markersize=4,
             label='Simulation')
axes[0].plot(a_values, results['win_prob_theory'], 'r-', linewidth=2,
             label='Theory: a/N')
axes[0].set_title(f"Win Probability (N={N})")
axes[0].set_xlabel("Initial fortune a")
axes[0].set_ylabel("P(reach N)")
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# (2) 期待停止時間
axes[1].plot(a_values, results['E_tau_sim'], 'bo', markersize=4,
             label='Simulation')
axes[1].plot(a_values, results['E_tau_theory'], 'r-', linewidth=2,
             label='Theory: a(N-a)')
axes[1].set_title(f"Expected Stopping Time (N={N})")
axes[1].set_xlabel("Initial fortune a")
axes[1].set_ylabel("E[τ]")
axes[1].legend()
axes[1].grid(True, alpha=0.3)

# (3) E[S_τ] = a の検証(任意抽出定理)
axes[2].plot(a_values, results['E_Stau_sim'], 'bo', markersize=4,
             label='Simulation E[$S_τ$]')
axes[2].plot(a_values, a_values, 'r-', linewidth=2,
             label='Theory: E[$S_τ$] = a')
axes[2].set_title("Optional Stopping Theorem: E[$S_τ$] = $S_0$")
axes[2].set_xlabel("Initial fortune a")
axes[2].set_ylabel("E[$S_τ$]")
axes[2].legend()
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("optional_stopping_theorem.png", dpi=150, bbox_inches="tight")
plt.show()

print(f"任意抽出定理の検証 (N={N}):")
for i in [0, 4, 9, 14, 18]:
    a = results['a'][i]
    print(f"  a={a:2d}: E[S_τ] = {results['E_Stau_sim'][i]:.2f} "
          f"(理論値: {a}), "
          f"P(win) = {results['win_prob_sim'][i]:.4f} "
          f"(理論値: {results['win_prob_theory'][i]:.4f}), "
          f"E[τ] = {results['E_tau_sim'][i]:.1f} "
          f"(理論値: {results['E_tau_theory'][i]})")

マルチンゲール戦略(倍額戦略)の破産確率シミュレーション

「負けたら倍額賭ける」マルチンゲール戦略は、理論的にはどうなるのでしょうか。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

def martingale_strategy(initial_bankroll, base_bet, p_win, max_rounds):
    """
    マルチンゲール戦略のシミュレーション
    負けたら倍額、勝ったら基本額に戻す

    Parameters
    ----------
    initial_bankroll : float, 初期資金
    base_bet : float, 基本賭け金
    p_win : float, 1回の勝率
    max_rounds : int, 最大ラウンド数

    Returns
    -------
    bankroll_history : list, 資金推移
    """
    bankroll = initial_bankroll
    bet = base_bet
    history = [bankroll]

    for _ in range(max_rounds):
        if bankroll < bet:
            # 資金不足で賭けられない → 破産
            break
        if np.random.random() < p_win:
            # 勝ち
            bankroll += bet
            bet = base_bet  # 基本額に戻す
        else:
            # 負け
            bankroll -= bet
            bet *= 2  # 倍額にする
        history.append(bankroll)

    return history


# パラメータ設定
initial_bankroll = 1000
base_bet = 10
max_rounds = 500

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

# --- (1) 公平なゲーム (p=0.5) でのマルチンゲール戦略 ---
p_fair = 0.5
n_paths = 30

for i in range(n_paths):
    history = martingale_strategy(initial_bankroll, base_bet, p_fair, max_rounds)
    color = 'tab:red' if history[-1] <= 0 else 'tab:blue'
    axes[0, 0].plot(history, alpha=0.5, linewidth=0.5, color=color)
axes[0, 0].axhline(y=initial_bankroll, color='k', linestyle='--', linewidth=1)
axes[0, 0].set_title(f"Martingale Strategy (p={p_fair}, fair game)")
axes[0, 0].set_xlabel("Round")
axes[0, 0].set_ylabel("Bankroll")
axes[0, 0].grid(True, alpha=0.3)

# --- (2) 不利なゲーム (p=0.48, カジノ) でのマルチンゲール戦略 ---
p_casino = 0.48
for i in range(n_paths):
    history = martingale_strategy(initial_bankroll, base_bet, p_casino, max_rounds)
    color = 'tab:red' if history[-1] <= 0 else 'tab:blue'
    axes[0, 1].plot(history, alpha=0.5, linewidth=0.5, color=color)
axes[0, 1].axhline(y=initial_bankroll, color='k', linestyle='--', linewidth=1)
axes[0, 1].set_title(f"Martingale Strategy (p={p_casino}, casino)")
axes[0, 1].set_xlabel("Round")
axes[0, 1].set_ylabel("Bankroll")
axes[0, 1].grid(True, alpha=0.3)

# --- (3) 破産確率 vs ラウンド数 ---
n_simulations = 5000
max_rounds_range = [50, 100, 200, 500, 1000, 2000]

ruin_probs_fair = []
ruin_probs_casino = []

for mr in max_rounds_range:
    ruins_fair = 0
    ruins_casino = 0
    for _ in range(n_simulations):
        h_fair = martingale_strategy(initial_bankroll, base_bet, 0.5, mr)
        if h_fair[-1] <= 0:
            ruins_fair += 1
        h_casino = martingale_strategy(initial_bankroll, base_bet, 0.48, mr)
        if h_casino[-1] <= 0:
            ruins_casino += 1
    ruin_probs_fair.append(ruins_fair / n_simulations)
    ruin_probs_casino.append(ruins_casino / n_simulations)

axes[1, 0].plot(max_rounds_range, ruin_probs_fair, 'bo-', linewidth=2,
                markersize=6, label='Fair (p=0.5)')
axes[1, 0].plot(max_rounds_range, ruin_probs_casino, 'rs-', linewidth=2,
                markersize=6, label='Casino (p=0.48)')
axes[1, 0].set_title("Ruin Probability vs Number of Rounds")
axes[1, 0].set_xlabel("Max rounds")
axes[1, 0].set_ylabel("Ruin probability")
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)

# --- (4) 3つの戦略の比較 ---
def flat_strategy(initial_bankroll, bet, p_win, max_rounds):
    """定額賭け戦略"""
    bankroll = initial_bankroll
    history = [bankroll]
    for _ in range(max_rounds):
        if bankroll < bet:
            break
        if np.random.random() < p_win:
            bankroll += bet
        else:
            bankroll -= bet
        history.append(bankroll)
    return history

def kelly_strategy(initial_bankroll, p_win, max_rounds):
    """ケリー基準による賭け戦略"""
    bankroll = initial_bankroll
    history = [bankroll]
    # ケリー比率: f* = 2p - 1 (1:1のオッズの場合)
    f_star = max(2 * p_win - 1, 0.01)  # 最低1%
    for _ in range(max_rounds):
        bet = max(bankroll * f_star, 1)
        if bankroll < 1:
            break
        if np.random.random() < p_win:
            bankroll += bet
        else:
            bankroll -= bet
        history.append(bankroll)
    return history

p_test = 0.52  # わずかに有利
n_paths_comp = 200
max_r = 300

# 各戦略の最終資産を集める
final_martingale = []
final_flat = []
final_kelly = []

for _ in range(n_paths_comp):
    h1 = martingale_strategy(initial_bankroll, base_bet, p_test, max_r)
    h2 = flat_strategy(initial_bankroll, base_bet, p_test, max_r)
    h3 = kelly_strategy(initial_bankroll, p_test, max_r)
    final_martingale.append(h1[-1])
    final_flat.append(h2[-1])
    final_kelly.append(h3[-1])

strategies = ['Martingale\n(doubling)', 'Flat Bet\n(constant)', 'Kelly\nCriterion']
final_means = [np.mean(final_martingale), np.mean(final_flat), np.mean(final_kelly)]
final_medians = [np.median(final_martingale), np.median(final_flat), np.median(final_kelly)]

x_pos = np.arange(3)
width = 0.35
axes[1, 1].bar(x_pos - width/2, final_means, width, label='Mean', color='steelblue',
               alpha=0.8, edgecolor='black', linewidth=0.5)
axes[1, 1].bar(x_pos + width/2, final_medians, width, label='Median', color='coral',
               alpha=0.8, edgecolor='black', linewidth=0.5)
axes[1, 1].axhline(y=initial_bankroll, color='k', linestyle='--', linewidth=1.5,
                    label=f'Initial: {initial_bankroll}')
axes[1, 1].set_xticks(x_pos)
axes[1, 1].set_xticklabels(strategies)
axes[1, 1].set_title(f"Strategy Comparison (p={p_test}, {max_r} rounds)")
axes[1, 1].set_ylabel("Final Bankroll")
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.savefig("martingale_betting_strategies.png", dpi=150, bbox_inches="tight")
plt.show()

# 破産確率の表示
ruin_m = sum(1 for x in final_martingale if x <= 0) / n_paths_comp
ruin_f = sum(1 for x in final_flat if x <= 0) / n_paths_comp
ruin_k = sum(1 for x in final_kelly if x <= 0) / n_paths_comp

print(f"戦略比較 (p={p_test}, {max_r}ラウンド, 初期資金={initial_bankroll}):")
print(f"  マルチンゲール戦略: 平均={np.mean(final_martingale):.0f}, "
      f"中央値={np.median(final_martingale):.0f}, 破産率={ruin_m:.2%}")
print(f"  定額賭け戦略:       平均={np.mean(final_flat):.0f}, "
      f"中央値={np.median(final_flat):.0f}, 破産率={ruin_f:.2%}")
print(f"  ケリー基準:         平均={np.mean(final_kelly):.0f}, "
      f"中央値={np.median(final_kelly):.0f}, 破産率={ruin_k:.2%}")

マルチンゲール戦略の結果からわかることを整理します。

  • 公平なゲーム: マルチンゲール戦略は短期的には小さな利益を頻繁に得ますが、たまに大きな損失を被ります。任意抽出定理より、期待利益はゼロです。
  • 不利なゲーム(カジノ): ラウンド数が増えるほど破産確率は上昇します。マルチンゲール戦略はハウスエッジを克服できません。
  • わずかに有利なゲーム: マルチンゲール戦略は破産リスクが高い一方、ケリー基準は破産を避けつつ長期的な成長を最大化します。

マルチンゲール理論の核心は、公平なゲームでは賭け方を工夫しても期待利益を変えることはできない という点です。これは任意抽出定理の直接的な帰結です。

まとめ

本記事では、マルチンゲールの理論について定義から応用まで解説しました。

  • マルチンゲールは $E[X_{n+1} \mid \mathcal{F}_n] = X_n$ で定義され、「公平なゲーム」を数学的に定式化したもの
  • 条件付き期待値の塔の性質から $E[X_m \mid \mathcal{F}_n] = X_n$($m > n$)および $E[X_n] = E[X_0]$(期待値不変)が従う
  • 劣マルチンゲール($\geq$)は有利なゲーム、優マルチンゲール($\leq$)は不利なゲームに対応
  • 対称ランダムウォーク、指数マルチンゲール、条件付き期待値過程、尤度比過程、ブラウン運動が代表例
  • 任意抽出定理は「合法的な停止規則で止めても期待値は変わらない」ことを保証する
  • マルチンゲール収束定理は、有界な劣マルチンゲールが a.s. に収束することを保証する
  • マルチンゲール戦略(倍額戦略)は破産リスクが高く、公平なゲームの期待利益はゼロのまま

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