三信封游戏
在你面前有三个信封,每个信封里的金额分别是 。你知道 在 上均匀分布,即 (为方便起见,将 视为连续实数)。
你先打开一个信封,看到金额 。此时你可以选择直接拿走,也可以选择继续打开其他信封。但是,之后每打开一个新信封都要额外支付 200,且一旦打开新信封,前一个信封必须放弃。
如果你的目标是最大化最终净收益,那么应该选择怎样的开封策略?最终的净收益期望又是多少?
打开第一个信封后的策略
你看到第一个信封金额 后,它可能对应三种角色:
由于 在区间上均匀,而变量替换带来 Jacobian,三种角色的后验相对权重满足
提示
这里的 Jacobian 可以直观理解成尺度修正系数:当把变量从 换成 时,区间长度会被放大 倍,所以对应密度要除以 。
想象你开封后看到的金额 ,考虑:
- :由于 ,所以 。
- :由于 ,所以 。
- :由于 ,所以 。
可以直观想象,相比于 和 ,在 时,更有可能观测到 这个现象(因为 均匀分布的区间更小)。相当于, 对 这个现象的贡献最大。
因此在固定 的条件下, 的权重会自然出现 。
据此可得分段后验(只写有可行解的区间):
将该后验带入后续决策并倒推,第一步最优规则可以写成:
直观上也很好理解:
当 时,第一只只能是 档,不可能再有更大金额,继续开只会增加成本。
当第一只不是 档时(等价于当前观测满足 ),继续开的期望严格高于立刻拿走:原因是你付出的成本上界是固定的(最多两次共 400),而收益端仍保留“上档金额”的信息价值。
提示
用最直观的不等式看,,因此 、,说明即便扣除开封成本,后续候选金额的净值仍为正。
更关键的点在于,后续决策是先看再选:若第二步信息不利可以立即停在 ,若信息有利再开第三只。所以继续开对应的是一个带最优停止的条件期望。
正因为这种可选性,第一步在 时满足 ,因此应继续,而不是直接拿走第一个信封。
打开第二个信封后的策略
现在假设你已经支付一次成本打开第二只,看到金额 。是否再开第三只,取决于第三只金额 的条件期望:
- 现在停手净收益:
- 再开第三只净收益期望:
因此决策条件是
记
则只会出现两类比例关系。
情况一:M=4m
说明已经看到的是 ,第三只必然是 。此时再开第三只当且仅当
化成可执行规则:
- 若当前 (手上是较大那只),一定不再开。
- 若当前 (手上是较小那只),当且仅当 再开。
情况二:M=2m
说明已见两只相邻,但不确定是 还是 。
设 是两只已开封金额为 的概率,即
由同样的 Jacobian 权重推得
提示
推导思路与前文一致:把“观测到的最小值 ”看成由 线性缩放得到。已知看到的是一对 ,只可能来自两种假设:
- ,此时 ;
- ,此时 (即 )。
因为 ,先验密度是常数,区分两种假设的关键就是变量替换的 Jacobian:
再结合可行区间: 只在 有效, 只在 有效。于是归一化后
于是第三只条件期望为
再与阈值 比较,就得到第二步策略:
- 若当前拿的是较小那只(),当且仅当 再开。
- 若当前拿的是较大那只():
- 时再开;
- 时不再开;
- 时再开;
- 时不再开。
期望收益
下面在风险中性(线性效用)下,计算该最优策略的公允价格(期望净收益)。
提示
这里 风险中性(线性效用) 指效用函数取 :同样期望值的收益方案被认为等价,不额外奖励稳定性也不额外惩罚波动。因此本文可以直接以“期望净收益最大”为决策目标。
令
- :第一只恰好是 时的最优净收益;
- :第一只恰好是 时的最优净收益;
- :第一只恰好是 时的最优净收益。
- 第一只是 :按上一节规则分段可得
- 第一只是 :同样按上一节规则分段可得
- 第一只是 :当 时,第一步已满足 ,应立即停手;当 时按第二步规则分段,得到
总期望:第一只信封等概率对应三档,因此
分段积分后可得
作为对照,如果完全不使用“额外开封权”,随机拿第一只的期望仅为
因此策略带来的信息价值约为
代码验证
这里给出两种验证方式:
蒙特卡洛模拟:直接模拟“先验采样 + 按策略决策”。原理是大量重复随机实验,用样本均值近似理论期望;样本越多结果越稳定,但本质是数值近似,存在随机波动。
Bellman 递推:显式构造后验并计算值函数。原理是按“最后一步到第一步”做动态规划,在每个状态比较“停手”和“继续”两种动作的价值;与蒙特卡洛相比,它不是靠随机试验逼近,而是基于状态转移与条件期望直接求最优决策结构。
蒙特卡洛模拟验证
蒙特卡洛模拟验证将上文的策略编码,并依照此进行大量重复实验,观测最后结果的收敛值。
# Made by Wanakachi
import random
def should_open_third(a, b):
"""
已看过第一只 a、第二只 b(且已支付一次 200)后,
是否值得再花 200 开第三只。
"""
m, M = min(a, b), max(a, b)
# 情况 1:已看到 {X,4X},第三只必为 2X = 2m
if abs(M - 4 * m) < 1e-12:
expected_last = 2 * m
# 情况 2:已看到相邻两档 {X,2X} 或 {2X,4X}
elif abs(M - 2 * m) < 1e-12:
if 100 <= m < 200:
q = 1.0
elif 200 <= m <= 1000:
q = 2 / 3
elif 1000 < m <= 2000:
q = 0.0
else:
raise ValueError(f"unexpected m={m}")
# 若是 {X,2X},剩下的是 4m;若是 {2X,4X},剩下的是 m/2
expected_last = q * (4 * m) + (1 - q) * (m / 2)
else:
raise ValueError(f"bad pair: {a}, {b}")
# 再开第三只条件:E[C|A,B] > B + 200
return expected_last > b + 200
def play_one_game():
x = random.uniform(100, 1000)
envelopes = [x, 2 * x, 4 * x]
# 三只信封打开顺序随机
order = random.sample([0, 1, 2], 3)
# 第一次打开
a = envelopes[order[0]]
# 第一步策略:A < 2000 继续,否则停止
if a >= 2000:
return a
# 第二次打开(已支付一次成本)
b = envelopes[order[1]]
# 决定是否开第三只
if should_open_third(a, b):
c = envelopes[order[2]]
return c - 400
else:
return b - 200
def monte_carlo(n=1_000_000):
total = 0.0
for _ in range(n):
total += play_one_game()
return total / n
for n in [10_000, 100_000, 500_000, 2_000_000]:
est = monte_carlo(n)
print(f"{n:>9,d} 次模拟: 期望收益 ≈ {est:.4f}")随着样本数增大,结果会围绕理论值 波动并逐步收敛。
Bellman 递推验证
Bellman 递推验证不硬编码阈值,而是显式计算后验并做动态规划。核心方程是
# Made by Wanakachi
from math import isclose
import numpy as np
COST = 200.0
ROLES = (1.0, 2.0, 4.0) # 金额分别为 r * X
X_MIN, X_MAX = 100.0, 1000.0
EPS = 1e-12
def normalize(items):
s = sum(w for _, w in items)
return [(obj, w / s) for obj, w in items]
def posterior_after_first(a):
"""
已知第一次看到 A=a,返回后验假设 [(hyp, prob), ...]
hyp = {"x": x, "r1": r1},表示 a = r1 * x
"""
hyps = []
for r1 in ROLES:
x = a / r1
if X_MIN - EPS <= x <= X_MAX + EPS:
# 后验权重与 Jacobian 对应:proportional to 1 / r1
hyps.append(({"x": x, "r1": r1}, 1.0 / r1))
return normalize(hyps)
def posterior_after_second(a, b):
"""
已知看到 a, b 后的后验。
hyp = {"x": x, "r1": r1, "r2": r2, "r3": r3}
其中第三只金额是 r3 * x
"""
hyps2 = []
for h1, p1 in posterior_after_first(a):
x, r1 = h1["x"], h1["r1"]
remaining = [r for r in ROLES if r != r1]
for r2 in remaining:
if isclose(r2 * x, b, abs_tol=1e-9):
r3 = [r for r in remaining if r != r2][0]
# 给定第一次假设后,第二次从剩余两个里均匀抽到
hyps2.append(({"x": x, "r1": r1, "r2": r2, "r3": r3}, p1 * 0.5))
return normalize(hyps2)
def expected_last(a, b):
post = posterior_after_second(a, b)
return sum(p * (h["r3"] * h["x"]) for h, p in post)
def V2(a, b):
return max(b, expected_last(a, b) - COST)
def continue_value_after_first(a):
"""
第一步若选择继续,进入第二步后的期望最优值(尚未扣第一笔 200)
"""
total = 0.0
for h1, p1 in posterior_after_first(a):
x, r1 = h1["x"], h1["r1"]
remaining = [r for r in ROLES if r != r1]
val = 0.5 * V2(a, remaining[0] * x) + 0.5 * V2(a, remaining[1] * x)
total += p1 * val
return total
def V1(a):
return max(a, continue_value_after_first(a) - COST)
def initial_value_grid(n=20001):
"""
数值积分近似 E[V*] = E[(V1(X)+V1(2X)+V1(4X))/3]
"""
xs = np.linspace(100.0, 1000.0, n)
vals = [(V1(x) + V1(2 * x) + V1(4 * x)) / 3.0 for x in xs]
return float(np.mean(vals))
print("Approx initial value =", initial_value_grid())
print("Theory =", 47230 / 27)该代码会给出与解析解一致的结果,数值积分逼近 。
小结
这个问题的关键不是“猜哪个信封更大”,而是每次观察后都进行后验更新,并把“信息价值 - 开封成本”作为决策标准。
最终结论:
- 第一阶段策略: 继续开, 停止。
- 第二阶段策略:按 的比值分成 与 两类,再比较 与 。
- 线性效用下的公允价格(最优期望净收益):
