4. 大数定律与中心极限定理:为什么Monte Carlo方法有效?
说实话,很多初学者学Monte Carlo模拟,上来就写代码、跑路径、算VaR。但很少有人停下来问一句:凭什么我跑一万次随机路径,结果就能逼近真实值?
我当年刚入行时也犯过这个错。有一次给老板汇报压力测试结果,他问我:“你模拟了5000次,凭什么说这个数字可信?”我当时支支吾吾,答不上来。后来我才明白,支撑Monte Carlo方法的,其实是两个数学定理——大数定律和中心极限定理。
说白了,没有这两个定理,Monte Carlo就是一堆随机数在瞎折腾。有了它们,随机数才能变成可靠的量化工具。
4.1 大数定律:为什么样本均值会收敛?
大数定律讲的是这么一件事:当试验次数足够多时,样本均值会趋近于期望值。
举个例子。你抛一枚公平硬币,正面概率是0.5。抛10次,可能正面出现7次,频率0.7。但抛1000次呢?正面频率大概率在0.5附近。抛100万次呢?几乎就是0.5。
这就是大数定律在起作用。在Monte Carlo模拟中,我们正是利用这个性质:
- 每次模拟路径,相当于一次随机试验
- 所有路径的平均收益,就是样本均值
- 路径数量越大,样本均值越接近真实期望
核心结论:Monte Carlo模拟的精度,本质上取决于模拟次数N。N越大,结果越可靠。但注意——大数定律只告诉我们“会收敛”,没告诉我们“收敛多快”。
我在做衍生品定价时遇到过一个问题:模拟10万条路径,结果和5万条路径几乎一样。当时我以为是代码写错了,后来才意识到——对于某些简单产品,5万条路径已经足够收敛了。再多跑,边际收益很小。
4.2 中心极限定理:误差到底有多大?
大数定律给了我们信心,但还不够。你想想看,做压力测试时,领导问的不是“你模拟了多少次”,而是“你的结果误差有多大”。
这时候就需要中心极限定理登场了。
中心极限定理告诉我们:无论原始分布是什么样,样本均值的分布都会近似于正态分布。而且这个正态分布的方差,会随着样本量增大而缩小。
具体来说:
- 假设每次模拟结果的方差是 σ²
- 模拟N次后,样本均值的方差是 σ² / N
- 样本均值的标准差是 σ / √N
这意味着什么?意味着我们可以给Monte Carlo结果加上一个置信区间。
实用技巧:我习惯在每次Monte Carlo模拟后,同时输出95%置信区间。比如“预期损失为100万,95%置信区间为[98万, 102万]”。这样领导一看就知道结果的可信度。
4.3 两个定理如何支撑Monte Carlo?
咱们把这两个定理串起来,看看它们到底怎么支撑Monte Carlo方法的:
- 大数定律保证:模拟次数足够多时,结果会收敛到真实值
- 中心极限定理告诉我们:收敛的速度是 1/√N,而且可以量化误差
换句话说:大数定律给了你方向,中心极限定理给了你尺子。
我曾经踩过一个坑。有一次做信用风险压力测试,模拟了1万次,结果看起来挺稳定。但我用中心极限定理算了一下标准误,发现置信区间宽得吓人。后来增加到10万次,结果才真正可靠。嗯,这就是理论指导实践的好处。
4.4 知识体系总览
下面这张图,是我自己梳理的本章知识结构。你看一眼就能明白两个定理在Monte Carlo中的位置:
4.5 实战中的注意事项
理论说完了,咱们聊聊实战中容易踩的坑。
警告:中心极限定理要求样本独立同分布。在Monte Carlo模拟中,如果随机数生成器质量差,或者路径之间存在相关性,结果可能不收敛。
我曾经在做一个多资产组合的压力测试时,用了Excel自带的随机数生成器。结果跑了20万次,置信区间还是不稳定。后来换成Mersenne Twister算法,问题才解决。你想想看,随机数质量直接影响收敛性,这个坑我替你们踩过了。
另外还有一点:不要盲目追求模拟次数。根据中心极限定理,误差和1/√N成正比。想提高10倍精度,需要增加100倍模拟次数。有时候,用方差缩减技术(比如对偶变量法、控制变量法)比单纯增加N更划算。
4.6 一个简单的验证代码
下面这段代码,我经常用来给学生演示大数定律和中心极限定理。你可以跑一下试试:
import numpy as np
import matplotlib.pyplot as plt
# 模拟抛硬币:1表示正面,0表示反面
np.random.seed(42)
N_list = [10, 50, 100, 500, 1000, 5000, 10000]
results = []
for N in N_list:
flips = np.random.binomial(1, 0.5, size=N)
mean = np.mean(flips)
results.append(mean)
# 输出结果
for N, mean in zip(N_list, results):
print(f"N={N:5d}, 正面频率={mean:.4f}, 与0.5的误差={abs(mean-0.5):.4f}")
# 输出示例:
# N= 10, 正面频率=0.6000, 与0.5的误差=0.1000
# N= 100, 正面频率=0.4900, 与0.5的误差=0.0100
# N= 1000, 正面频率=0.4970, 与0.5的误差=0.0030
# N=10000, 正面频率=0.4998, 与0.5的误差=0.0002
看到没?随着N增大,频率越来越接近0.5。这就是大数定律在说话。
再算一下标准误:
# 中心极限定理验证
p = 0.5
for N in [100, 1000, 10000]:
std_error = np.sqrt(p * (1-p) / N)
print(f"N={N:5d}, 理论标准误={std_error:.4f}, 95%置信区间=[{0.5-1.96*std_error:.4f}, {0.5+1.96*std_error:.4f}]")
这个标准误,就是中心极限定理给我们的“尺子”。有了它,你就能自信地说:“我的模拟结果误差在±1.96个标准误之内。”
4.7 小结
大数定律和中心极限定理,是Monte Carlo方法的两个轮子。一个保证方向正确,一个告诉你离目标还有多远。
我个人习惯是:每次写Monte Carlo代码前,先问自己三个问题:
- 我的模拟次数够不够让大数定律生效?
- 我的随机数是否独立同分布?
- 我能不能用中心极限定理给出置信区间?
这三个问题想清楚了,代码写起来心里就有底。否则,跑再多路径也只是在自欺欺人。
公众号:蓝海资料掘金营,微信deep3321