跳轉到

9.2 期望值與變異數的計算#

概念說明#

期望值 E[X] = Σ x·P(X=x) 是隨機變數的「加權平均」;變異數 Var(X) = E[(X−μ)²] 衡量散布程度,其平方根是標準差。兩者是統計推論(9.3 節)與蒙地卡羅誤差估計(6.3 節)的基石。

計算層面有三個要點:

  1. 兩大公式:變異數用 E[X²] − (E[X])² 計算比定義式省一趟掃描,但單精度浮點下易發生災難性抵銷(兩個大數相減),實作要用單趟線性演算法(Welford 法)。
  2. 分布的變換:從均勻亂數 U ~ Unif(0,1) 製造任意分布,最通用的兩招是反變換法(X = F⁻¹(U))與拒絕抽樣;常態分布則用 Box-Muller 變換。
  3. 模擬驗證理論:大數法則說樣本平均收斂到期望值;中央極限定理說樣本平均的分布趨近常態,標準誤 σ/√n。這些都可以用電腦直接驗證——親眼看到理論曲線蓋在直方圖上,統計才算學懂。

經典例子「收集郵票問題」(買幾包卡片才能集滿 n 種?)的期望值 E = n·Hₙ(調和數),是「用期望值線性相加拆解複雜問題」的最佳示範。

演算法步驟#

單趟計算平均與變異數(Welford 法):

  1. 初始化 count = 0, mean = 0, M2 = 0。
  2. 對每個新值 x:count += 1;delta = x − mean;mean += delta/count;M2 += delta·(x − mean)。
  3. 變異數 = M2/(count − 1)(樣本變異數)。

反變換法產生任意離散/連續分布:

  1. 累積分布 F(x) 求反函數(離散:查累積機率表)。
  2. 抽 U ~ Unif(0,1),回傳最小的 x 使 F(x) ≥ U。

Python 程式碼範例#

import numpy as np, random, math

# --- Welford 單趟演算法 vs 天真做法 ---
def welford(data):
    n = mean = m2 = 0
    for x in data:
        n += 1
        d = x - mean
        mean += d / n
        m2 += d * (x - mean)
    return mean, m2 / (n - 1)

big = 1e9 + np.concatenate([np.zeros(10),  # 巨大均值 + 微小波動
                            np.random.default_rng(0).normal(0, 1, 10)])
mu1, var1 = welford(big)
mu2 = big.mean(); var2 = big.var(ddof=1)
print(f"Welford 變異數 = {var1:.6f}")
print(f"兩步法變異數   = {var2:.6f}(真值 ≈ 1.05,兩步法有抵銷誤差)")

# --- 反變換法:模擬幾何分布(丟銅板到第一次正面) ---
def geom(u, p=0.5):
    return math.ceil(math.log(1 - u) / math.log(1 - p))

rng = np.random.default_rng(1)
u = rng.uniform(0, 1, 200_000)
xs = [geom(v) for v in u]
print(f"幾何分布模擬平均 {np.mean(xs):.3f}(理論 1/p = 2)")
print(f"模擬變異數 {np.var(xs):.3f}(理論 (1-p)/p² = 2)")

# --- Box-Muller:產生常態亂數並驗證 CLT ---
def box_muller(u1, u2):
    r = np.sqrt(-2 * np.log(u1))
    return r * np.cos(2 * np.pi * u2)

u1, u2 = rng.uniform(0, 1, 100_000), rng.uniform(0, 1, 100_000)
z = box_muller(u1, u2)
print(f"Box-Muller 樣本: 平均 {z.mean():.4f}, 標準差 {z.std():.4f}, "
      f"偏度 {float(((z - z.mean())**3).mean() / z.std()**3):.4f}")

# CLT 驗證:擲骰子 30 次取平均,重複 20 萬次
rolls = rng.integers(1, 7, size=(200_000, 30))
sample_means = rolls.mean(axis=1)
mu_theory, sd_theory = 3.5, math.sqrt(35/12) / math.sqrt(30)
print(f"樣本平均 ≈ {sample_means.mean():.4f}(理論 3.5),"
      f"標準誤 ≈ {sample_means.std():.4f}(理論 {sd_theory:.4f})")

# --- 收集郵票問題 E = n·Hn ---
def coupon_collect(n, trials=20_000):
    total = 0
    for _ in range(trials):
        seen, count = set(), 0
        while len(seen) < n:
            seen.add(rng.integers(0, n)); count += 1
        total += count
    return total / trials

for n in [5, 10]:
    print(f"集滿 {n} 種:模擬 {coupon_collect(n):.1f} 次,"
          f"理論 n·Hn = {n * sum(1/k for k in range(1, n+1)):.1f}")

誤差分析#

模擬估計期望值的誤差由 CLT 給出:估計量的標準誤 = σ/√n,95% 信賴區間 ±1.96σ/√n。要把誤差降 10 倍就得 100 倍樣本——這與 6.3 節蒙地卡羅同源。計算層面:天真兩步法 Var = E[X²]−μ² 的相對誤差可放大到 ε·E[X²]/Var(X),當資料「均值大、波動小」(如金融日報酬)時完全失真;Welford 法數值穩定且單趟串流(資料不必一次載入),是 numpy、pandas 內部的做法。理論側的重要工具是期望值的線性性:E[X+Y] = E[X]+E[Y] 無需獨立性, coupon collector、演算法平均複雜度分析(如 quicksort 的 O(n log n))都靠它拆解。此外記得 Var(aX+b) = a²Var(X) 的縮放規則——把單位搞錯(標準差 vs 變異數)是實務統計最常見的 bug。

練習#

  1. 用反變換法模擬「指數分布」(F⁻¹(u) = −ln(1−u)/λ),取 λ = 1,驗證樣本均值 ≈ 1、樣本變異數 ≈ 1(指數分布的特性)。
  2. 對 Welford 法構造一組會讓兩步法失真的資料(均值 10⁸、標準差 1、n = 1000),量測兩法誤差;再用 math.fsum 改進兩步法,比較結果。
  3. 模擬驗證「E[max(X,Y)] = μ + σ/√π」對兩個獨立標準常態成立(理論值 μ + σ/√π ≈ 0.5642),並用 10 萬次模擬估計其標準誤。