蒙特卡洛方法听起来很高深,但核心思想一句话就能说清:当你算不出解析解,或者解析解太贵时,就用大量随机采样去逼近答案。
它不是魔法,而是大数定律的工程化应用。这篇文章从最经典的"投点估 π"讲起,把误差来源和收敛速度说清楚,最后给你一段能直接跑的 Python。
本文所有结论都用最基础的独立同分布随机抽样,不涉及马尔可夫链蒙特卡洛(MCMC)等进阶变体。目的是建立直觉,不是覆盖全貌。
一、最经典的例子:单位圆投点估 π
想象一个边长为 2 的正方形,中心在原点,里面内切一个半径为 1 的单位圆。在正方形内均匀随机地投 N 个点,落进圆里的点数记为 M。
因为点在正方形内均匀分布,落在圆内的概率就等于面积之比:
P(落在圆内) = 圆的面积 / 正方形面积 = π·1² / 2² = π / 4
根据大数定律,当 N 足够大时:
M / N ≈ π / 4 ⇒ π ≈ 4 · M / N
这就是蒙特卡洛方法最朴素的形态——用一个容易生成的随机实验,去估计一个常数。
二、误差从哪来:√N 收敛
很多人以为"样本越多越准",但到底多准?关键在于收敛速度。
对投点估 π,单次实验是伯努利变量,方差有限。由中心极限定理,估计值的标准误差满足:
SE(π̂) ≈ σ / √N ∝ 1 / √N
这意味着:
- 想把误差缩小到原来的 1/10,样本量要增大到 100 倍;
- 蒙特卡洛的收敛速度是 O(1/√N),与维度无关——这正是它在高维问题(如金融定价、粒子模拟)里吊打网格法的原因。
换句话说,蒙特卡洛慢,但"不挑维度"。这是它最宝贵的特性。
三、一个能直接跑的 Python 示例
下面这段代码不仅估 π,还顺手给出 95% 置信区间:
import random, math
def estimate_pi(n: int):
inside = 0
for _ in range(n):
x, y = random.random()*2 - 1, random.random()*2 - 1
if x*x + y*y <= 1:
inside += 1
pi_hat = 4 * inside / n
se = 4 * math.sqrt((inside/n)*(1-inside/n) / n) # 伯努利标准误
return pi_hat, pi_hat - 1.96*se, pi_hat + 1.96*se
for n in (1_000, 100_000, 10_000_000):
pi_hat, lo, hi = estimate_pi(n)
print(f"N={n:>10,} π̂≈{pi_hat:.5f} 95% CI [{lo:.5f}, {hi:.5f}]")
注意 N=10⁷ 时置信区间已经能稳稳包住真值 3.14159——这就是 √N 收敛的直观体现。
四、什么时候该想到蒙特卡洛
道理讲完,落到"我什么时候该用它"。三条经验法则:
- 没有闭式解:积分算不出、期望列不出解析式时,先想蒙特卡洛;
- 维度很高:网格法点数随维度指数爆炸,而蒙特卡洛的 √N 收敛"不挑维度",高维问题里反而最划算;
- 系统本身随机:抽卡、排队、粒子碰撞——过程本就是随机的,直接模拟常常比推导更省事。
说到底,蒙特卡洛不是更聪明的公式,而是"用算力换洞察"。像本站之前聊过的抽卡概率的数学,里面"期望 167 抽"的结论,最朴素的验证方式就是写个模拟跑一万次,看经验均值落不落得进理论值——这恰好是蒙特卡洛最舒服的用武之地。
小结
- 蒙特卡洛 = 用随机采样逼近难算的量;
- 误差 ∝ 1/√N,收敛慢但不随维度爆炸;
- 它能独立求解,也能反过来验证解析解。
下次遇到"这积分/这期望根本算不出来"的情况,别硬刚公式——写个循环,让随机数替你干活。
