4. 蒙特卡洛仿真方法

蒙特卡洛仿真,说白了就是「用随机数解决问题」。

我最早接触这个方法,是在做无线信道建模的时候。当时要评估一个接收机在不同信噪比下的性能,理论公式推导到一半就卡住了——信道太复杂,多径、衰落、干扰搅在一起,根本算不出闭式解。后来前辈丢给我一句话:「算不出来?那就扔骰子呗。」

嗯,这就是蒙特卡洛的核心思想。

4.1 蒙特卡洛方法原理

蒙特卡洛方法的名字,来自摩纳哥那个赌城。你想想看,赌场里玩轮盘、掷骰子,靠的就是随机性。这个方法也一样——用大量随机样本来逼近真实结果。

数学上,它的依据是大数定律:

当样本数 N → ∞ 时,样本均值依概率收敛于期望值

举个例子。你想求一个圆的面积,但不会算积分。怎么办?

往一个正方形里随机撒点,统计落在圆内的点数比例。这个比例乘以正方形面积,就是圆的近似面积。撒的点越多,结果越准。

这就是蒙特卡洛的典型应用——用频率代替概率,用统计平均代替解析计算。

核心公式:

误码率估计值 P̂e = (错误比特数) / (总发送比特数)

当总比特数 N → ∞ 时,P̂e → 真实误码率 Pe

4.2 在误码率仿真中的应用

在通信系统仿真里,蒙特卡洛方法用得最多的就是误码率估计。

流程其实很简单:

  1. 生成发送信号——比如随机产生 0 和 1 的比特流
  2. 模拟信道传输——加上高斯白噪声,或者更复杂的衰落
  3. 接收端判决——根据接收信号判断发送的是 0 还是 1
  4. 统计错误——对比发送和接收,数一数错了多少比特
  5. 计算误码率——错误比特数除以总比特数

我习惯用 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 万比特),找到感兴趣的信噪比范围,再针对关键点加大样本量。

另外,还有一种加速技巧叫「重要性采样」——说白了,就是人为让错误更容易发生,然后修正统计结果。这个方法在低误码率场景下特别有用,但实现起来稍微复杂一些。后面讲到高级仿真技术时,我会详细展开。

最后,我用一张流程图总结蒙特卡洛仿真的核心逻辑:

蒙特卡洛误码率仿真流程 生成随机比特流 调制(如BPSK) 模拟信道加噪声 接收端判决 统计错误比特数 增加样本数,重复仿真 计算误码率

这张图我每次讲课都会放。你仔细看那个虚线反馈回路——它代表「样本不够,继续跑」。实际仿真中,我经常写一个循环,每跑完一批样本就检查一下错误数,如果少于 10 个就继续加样本,直到满足精度要求为止。

嗯,蒙特卡洛方法就讲到这里。方法本身不复杂,但用得好不好,差别很大。我见过有人跑了一整天仿真,结果因为样本数不够,曲线在低误码率区域全是毛刺,根本没法用。记住那个经验法则:至少观察到 10 个错误,你的结果才可信。


公众号:蓝海资料掘金营,微信deep3321