4. 蒙特卡洛仿真方法
蒙特卡洛仿真,说白了就是「用随机数解决问题」。
我最早接触这个方法,是在做无线信道建模的时候。当时要评估一个接收机在不同信噪比下的性能,理论公式推导到一半就卡住了——信道太复杂,多径、衰落、干扰搅在一起,根本算不出闭式解。后来前辈丢给我一句话:「算不出来?那就扔骰子呗。」
嗯,这就是蒙特卡洛的核心思想。
4.1 蒙特卡洛方法原理
蒙特卡洛方法的名字,来自摩纳哥那个赌城。你想想看,赌场里玩轮盘、掷骰子,靠的就是随机性。这个方法也一样——用大量随机样本来逼近真实结果。
数学上,它的依据是大数定律:
当样本数 N → ∞ 时,样本均值依概率收敛于期望值
举个例子。你想求一个圆的面积,但不会算积分。怎么办?
往一个正方形里随机撒点,统计落在圆内的点数比例。这个比例乘以正方形面积,就是圆的近似面积。撒的点越多,结果越准。
这就是蒙特卡洛的典型应用——用频率代替概率,用统计平均代替解析计算。
核心公式:
误码率估计值 P̂e = (错误比特数) / (总发送比特数)
当总比特数 N → ∞ 时,P̂e → 真实误码率 Pe
4.2 在误码率仿真中的应用
在通信系统仿真里,蒙特卡洛方法用得最多的就是误码率估计。
流程其实很简单:
- 生成发送信号——比如随机产生 0 和 1 的比特流
- 模拟信道传输——加上高斯白噪声,或者更复杂的衰落
- 接收端判决——根据接收信号判断发送的是 0 还是 1
- 统计错误——对比发送和接收,数一数错了多少比特
- 计算误码率——错误比特数除以总比特数
我习惯用 Python 快速验证。下面是一个 BPSK 在 AWGN 信道下的蒙特卡洛仿真代码:
import numpy as np
import matplotlib.pyplot as plt
def bpsk_monte_carlo(EbN0_dB, num_bits=100000):
# 生成随机比特
tx_bits = np.random.randint(0, 2, num_bits)
# BPSK调制:0→-1, 1→+1
tx_symbols = 2 * tx_bits - 1
# 计算噪声标准差
EbN0_lin = 10 ** (EbN0_dB / 10)
noise_std = np.sqrt(1 / (2 * EbN0_lin))
# 加噪声
noise = noise_std * np.random.randn(num_bits)
rx_symbols = tx_symbols + noise
# 判决:大于0判为1,否则判为0
rx_bits = (rx_symbols > 0).astype(int)
# 统计错误
errors = np.sum(tx_bits != rx_bits)
ber = errors / num_bits
return ber
# 测试不同信噪比
EbN0_dB_range = np.arange(0, 11, 2)
ber_results = []
for EbN0_dB in EbN0_dB_range:
ber = bpsk_monte_carlo(EbN0_dB, num_bits=100000)
ber_results.append(ber)
print(f'Eb/N0 = {EbN0_dB} dB, BER = {ber:.2e}')
这段代码我用了很多年。每次做新项目,第一件事就是跑一遍这个基础仿真,确认信道模型和调制方式没问题。
小技巧:
仿真时建议先跑少量比特(比如 1000 个)快速验证代码逻辑,确认无误后再加大样本量。我曾经因为调制映射写反了,跑了 100 万比特才发现结果完全不对,白白浪费了计算时间。
4.3 仿真精度与样本数的关系
这里有个关键问题:到底需要多少样本,才能得到可信的误码率?
我踩过这个坑。有一次仿真一个低误码率场景,目标 BER 是 10-6,我只跑了 10 万个比特。结果算出来 BER 是 0——因为一个错误都没发生。这显然不对。
为什么会这样?
蒙特卡洛估计的精度,跟样本数 N 和真实误码率 Pe 都有关系。统计上,误码率估计的方差为:
Var(P̂e) = Pe(1 - Pe) / N
标准差就是:
σ = sqrt(Pe(1 - Pe) / N)
你看,要得到可靠的估计,至少需要观察到几十个错误比特。也就是说:
N ≥ 10 / Pe (经验法则)
比如你要测 BER = 10-5,至少需要 100 万个比特。想测到 10-6?那就得跑 1000 万个比特。
| 目标误码率 | 最少样本数(经验值) | 预计错误比特数 |
|---|---|---|
| 10-3 | 10,000 | ~10 |
| 10-4 | 100,000 | ~10 |
| 10-5 | 1,000,000 | ~10 |
| 10-6 | 10,000,000 | ~10 |
注意:
样本数不是越多越好。当 N 超过一定量级后,仿真时间会急剧增加,但精度提升越来越慢。我建议在工程中,先跑一个粗略的曲线(比如每点 10 万比特),找到感兴趣的信噪比范围,再针对关键点加大样本量。
另外,还有一种加速技巧叫「重要性采样」——说白了,就是人为让错误更容易发生,然后修正统计结果。这个方法在低误码率场景下特别有用,但实现起来稍微复杂一些。后面讲到高级仿真技术时,我会详细展开。
最后,我用一张流程图总结蒙特卡洛仿真的核心逻辑:
这张图我每次讲课都会放。你仔细看那个虚线反馈回路——它代表「样本不够,继续跑」。实际仿真中,我经常写一个循环,每跑完一批样本就检查一下错误数,如果少于 10 个就继续加样本,直到满足精度要求为止。
嗯,蒙特卡洛方法就讲到这里。方法本身不复杂,但用得好不好,差别很大。我见过有人跑了一整天仿真,结果因为样本数不够,曲线在低误码率区域全是毛刺,根本没法用。记住那个经验法则:至少观察到 10 个错误,你的结果才可信。
公众号:蓝海资料掘金营,微信deep3321