首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >未曾知全貌,我亦知何如(蒙特卡洛算法)

未曾知全貌,我亦知何如(蒙特卡洛算法)

作者头像
云深无际
发布2026-03-03 12:44:17
发布2026-03-03 12:44:17
1330
举报
文章被收录于专栏:云深之无迹云深之无迹

蒙特卡洛就是— 用大量随机试验,去逼近一个本来很难算的结果;它不是“高深算法”,而是一种思想:“既然精确算不出来,那就随机抽样,统计平均。”

最经典的例子:算圆周率 π

有一个正方形,里面画一个内切圆,如果往随机往正方形里撒 100 万个点,然后统计有多少点落在圆里

那么:

圆内点数总点数圆面积正方形面积

于是:

圆内比例

没有算圆面积公式,只是“随机 + 统计”,这就是蒙特卡洛。

到底解决什么问题?

当遇到:积分算不出来,系统误差难以解析,多变量耦合太复杂,概率分布太复杂

蒙特卡洛说:

别推公式了,直接模拟。

为什么它有效?

因为一个很朴素的事实:

如果随机样本足够多,平均值就会接近真实值。

这就是大数定律。

简单说:

10 次试验 → 很不准

1000 次 → 还可以

100 万次 → 非常接近

误差大约按:

误差

所以样本翻 4 倍,误差减半。

深入根基:大数定理

大数定律说的是:

当重复试验次数足够多时,样本平均值会趋近于真实期望值。

数学定义

设:

是独立同分布随机变量。

满足:

样本平均:

大数定律说:

弱大数定律(WLLN)

意思是:偏离真实值的概率 → 0,这是“概率意义下收敛”。

关键是每个随机变量有波动,但独立波动会互相抵消;假设每个变量方差为 ,那么平均值的方差:

于是标准差:

所以:

波动幅度随 1/√n 下降

样本越多,平均值越稳定。

蒙特卡洛本质就是:

期望

用:

去估计,而这个估计能收敛,完全靠大数定律保证。

比如我们测电压:真实值 = 10.000000 V,但是每次测量带有噪声,然后连续测 N 次,取平均;那么测量误差标准差会变成:

这就是为什么:DMM 用积分,ADC 用平均,低频噪声用长时间统计的一点点数学解释。

大数定律 vs 中心极限定理

很多人会混淆。(我也之前没有完全明白)

名称

讲什么

大数定律

平均值会收敛

中心极限定理

平均值的分布会变成正态

大数定理它告诉我们一个哲学事实:

不确定性可以通过重复消除。

这几乎是统计学和工程的根基。

说回蒙特卡洛

比如在金融领域:算复杂期权定价,算极端风险概率(公式算不了,只能随机模拟未来市场);物理领域可以计算粒子碰撞模拟,也可以热噪声统计。

在IC 里面可以做器件误差分析,比如:电阻 ±1%,运放偏置 ±50µV,参考源 ±10ppm,书面的很难推一个复杂误差传播公式;但可以:随机生成一组参数,算一次输出,重复 10 万次,看输出分布,这就是工程里的蒙特卡洛。

另外蒙特卡洛不是乱随机,它是:先建模型,再随机输入,最后统计输出;例如:输出 = f(电阻, 温度, 噪声),把电阻 ~ 正态分布,温度 ~ 均匀分布,噪声 ~ 高斯白噪声,代进去随机跑 10 万次。最后得到:输出的均值,输出的标准差, 输出的概率分布;可以叫做概率建模 + 随机求解。(缺点是收敛慢(1/√N)需要大量计算。)

使用

先把问题改写成“期望”

蒙特卡洛的核心是把想算的量写成:

常见等价形式:

积分:用随机变量 表达成

这就是重要性采样的数学根。

概率

估计器:用样本均值逼近期望

抽样 ,定义:

性质有:

无偏

方差

收敛速率(RMS 误差):

这就是蒙特卡洛的“万能但慢”的本质:维度再高也不怕,但收敛永远是 。

置信区间:把“不确定度”量化出来

样本方差 ,标准误差:

大样本下:

于是 95% 置信区间近似为:

仿真示例

用一个标准例子演示:

令 ,则 。

这里跑了三种方法(同样 N=20000):

Plain MC:SE 约

Antithetic:SE 约

Control variate:SE 约

并且画了三张关键图:

代码语言:javascript
复制
Target I = ∫_0^1 exp(x) dx = e - 1 = 1.718281828459045
Plain      : Ihat=1.72215757, SE=3.466e-03, 95% CI=(np.float64(1.7153641635016394), np.float64(1.7289509773653648))
Antithetic : Ihat=1.71772080, SE=6.245e-04, 95% CI=(np.float64(1.7164966886828101), np.float64(1.7189449125866085))
ControlVar : Ihat=1.71855180, SE=4.446e-04, 95% CI=(np.float64(1.7176804373321641), np.float64(1.7194231571928984)), b≈1.6884

RMS error vs N 的 log-log 图:贴着 下降

的采样分布:近似钟形

标准化误差 :近似

这些就是蒙特卡洛“数学建模 + 仿真验证”的完整闭环。

蒙特卡洛“建模模板”

任何蒙特卡洛任务都可以按这个流程写:

  1. 定义目标:
  2. 选分布:(或重要性采样 )
  3. 估计器: 或
  4. 误差:SE + CI(CLT)

后记

蒙特卡洛这篇文章不是空穴来风,是因为最近在研究传感器,建模了不少的东西,后面在做整体评估的时候就不免的使用蒙特卡洛,然后就萌生这个写作的想法。

我比较推荐的是这本书,但是这本书里面并没有写相关的内容
我比较推荐的是这本书,但是这本书里面并没有写相关的内容

我比较推荐的是这本书,但是这本书里面并没有写相关的内容

不过它整体的内容非常好,尤其感兴趣的数值积分部分,写了不少,看的我是满心欢喜。

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt

# =========================
# Monte Carlo: mathematical modeling + simulation demo (fast version)
# =========================
# Target: I = ∫_0^1 exp(x) dx = e - 1 = E[exp(U)], U ~ Unif(0,1)
# Show:
#   - Estimator + 95% CI
#   - Convergence ~ 1/sqrt(N)
#   - Variance reduction: antithetic + control variate
#   - CLT intuition via standardized errors
# =========================

TRUE_I = np.e - 1.0

def mc_plain(N, rng):
    u = rng.random(N)
    y = np.exp(u)
    Ihat = y.mean()
    se = y.std(ddof=1) / np.sqrt(N)
    return Ihat, se

def mc_antithetic(N_total, rng):
    # Use N_total samples by pairing => N_pairs = N_total/2
    N_pairs = N_total // 2
    u = rng.random(N_pairs)
    z = 0.5 * (np.exp(u) + np.exp(1.0 - u))
    Ihat = z.mean()
    se = z.std(ddof=1) / np.sqrt(N_pairs)
    return Ihat, se

def mc_control(N, rng):
    u = rng.random(N)
    x = np.exp(u)
    c = u
    c0 = 0.5
    cov = np.cov(x, c, ddof=1)[0, 1]
    varc = np.var(c, ddof=1)
    b = cov / varc
    z = x - b * (c - c0)
    Ihat = z.mean()
    se = z.std(ddof=1) / np.sqrt(N)
    return Ihat, se, b

def ci95(Ihat, se):
    return (Ihat - 1.96*se, Ihat + 1.96*se)

# ---- Single run summary
rng = np.random.default_rng(0)
N = 20000
I1, se1 = mc_plain(N, rng)
I2, se2 = mc_antithetic(N, rng)
I3, se3, b3 = mc_control(N, rng)

print("Target I = ∫_0^1 exp(x) dx = e - 1 =", TRUE_I)
print(f"Plain      : Ihat={I1:.8f}, SE={se1:.3e}, 95% CI={ci95(I1,se1)}")
print(f"Antithetic : Ihat={I2:.8f}, SE={se2:.3e}, 95% CI={ci95(I2,se2)}")
print(f"ControlVar : Ihat={I3:.8f}, SE={se3:.3e}, 95% CI={ci95(I3,se3)}, b≈{b3:.4f}")

# ---- Convergence study (reduced)
Ns = np.unique(np.logspace(2, 5, 16).astype(int))  # 1e2..1e5, 16 points
n_rep = 60  # repetitions per N

def rms_error(method, N, seed_base=123):
    errs = np.empty(n_rep)
    for k in range(n_rep):
        rg = np.random.default_rng(seed_base + 10000*k + N)
        if method == "plain":
            Ihat, _ = mc_plain(N, rg)
        elif method == "antithetic":
            Ihat, _ = mc_antithetic(N, rg)
        elif method == "control":
            Ihat, _, _ = mc_control(N, rg)
        else:
            raise ValueError
        errs[k] = Ihat - TRUE_I
    return np.sqrt(np.mean(errs**2))

rms_plain = np.array([rms_error("plain", int(n)) for n in Ns])
rms_anti  = np.array([rms_error("antithetic", int(n)) for n in Ns])
rms_ctrl  = np.array([rms_error("control", int(n)) for n in Ns])

ref = rms_plain[0] * np.sqrt(Ns[0] / Ns)

plt.figure()
plt.loglog(Ns, rms_plain, marker="o", label="Plain MC")
plt.loglog(Ns, rms_anti,  marker="o", label="Antithetic")
plt.loglog(Ns, rms_ctrl,  marker="o", label="Control variate")
plt.loglog(Ns, ref, linestyle="--", label="~ 1/sqrt(N)")
plt.xlabel("N samples")
plt.ylabel("RMS error")
plt.title("Monte Carlo convergence (RMS error vs N)")
plt.grid(True, which="both")
plt.legend()
plt.show()

# ---- CLT intuition: standardized errors (reduced)
N_clt = 2000
reps = 1500
Ihats = np.empty(reps)
ses = np.empty(reps)
for k in range(reps):
    rg = np.random.default_rng(7 + k)
    Ihat, se = mc_plain(N_clt, rg)
    Ihats[k] = Ihat
    ses[k] = se
z = (Ihats - TRUE_I) / ses

plt.figure()
plt.hist(Ihats, bins=50)
plt.xlabel("Ihat")
plt.ylabel("Count")
plt.title(f"Sampling distribution of Ihat (plain), N={N_clt}, reps={reps}")
plt.grid(True)
plt.show()

plt.figure()
plt.hist(z, bins=50)
plt.xlabel("z = (Ihat - I)/SE")
plt.ylabel("Count")
plt.title("Standardized errors (approximately N(0,1) by CLT)")
plt.grid(True)
plt.show()
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-02-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 最经典的例子:算圆周率 π
  • 到底解决什么问题?
  • 为什么它有效?
  • 深入根基:大数定理
  • 数学定义
    • 弱大数定律(WLLN)
    • 大数定律 vs 中心极限定理
  • 说回蒙特卡洛
  • 使用
  • 先把问题改写成“期望”
  • 估计器:用样本均值逼近期望
  • 置信区间:把“不确定度”量化出来
  • 仿真示例
  • 蒙特卡洛“建模模板”
  • 后记
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档