🧩 一、核心思想:Transform Uniform → Normal
几乎所有方法都基于以下逻辑:
如果你能生成 ,那么可以通过某种变换 使得 的分布就是目标分布。
对正态分布 ,有多种实现方式,从理论精确到数值逼近,下面依次介绍。
🧮 方法 1:Inverse Transform Sampling(反变换法)
原理:
设 。
如果 ,则
服从标准正态分布。
因为。
代码:
import numpy as np import scipy.stats as st U = np.random.rand(10000) Z = st.norm.ppf(U)
ppf是 “percent point function” = CDF 的反函数。
- 优点:理论上精确;简单。
- 缺点:需要高精度计算 (\Phi^{-1}),在尾部数值不稳定、计算慢。
应用:
- Stratified Sampling(分层抽样)里使用的就是它!
因为你知道每层的概率区间 ,所以自然用反变换法取样。
🧮 方法 2:Box–Muller Transform(极坐标法)
原理:
使用两个独立的 ,生成两个独立的 :
[
\begin{aligned}
Z_1 &= \sqrt{-2\ln U_1} \cos(2\pi U_2), \
Z_2 &= \sqrt{-2\ln U_1} \sin(2\pi U_2).
\end{aligned}
]
推导思路:
- 设 ()。
- 令 (),
即在极坐标下生成等价分布点。
代码:
U1, U2 = np.random.rand(10000), np.random.rand(10000) Z1 = np.sqrt(-2 * np.log(U1)) * np.cos(2 * np.pi * U2) Z2 = np.sqrt(-2 * np.log(U1)) * np.sin(2 * np.pi * U2)
特点:
- 精确、解析。
- 比
ppf法快得多(不需数值求反函数)。
- 常用于模拟系统底层(例如 C 库里的
randn()就是变体)。
🧮 方法 3:Marsaglia Polar Method(Box–Muller 改进)
思想:
去掉三角函数,避免调用
sin / cos。算法:
- 生成 (U_1, U_2 \sim \text{Uniform}(-1,1)),
计算 (S = U_1^2 + U_2^2)。
- 若 (S \ge 1) 则重新生成。
- 否则令:
[
Z_1 = U_1 \sqrt{\frac{-2\ln S}{S}}, \quad
Z_2 = U_2 \sqrt{\frac{-2\ln S}{S}}.
]
代码:
def marsaglia_normal(n=10000): Z = [] while len(Z) < n: U1, U2 = np.random.uniform(-1, 1, 2) S = U1**2 + U2**2 if S >= 1 or S == 0: continue factor = np.sqrt(-2 * np.log(S) / S) Z += [U1 * factor, U2 * factor] return np.array(Z[:n])
优点:
- 无三角函数,更高效;
- 广泛用于底层库(NumPy、C++ STL、GPU kernels)。
🧮 方法 4:Ziggurat Algorithm(采样分块法)
思想:
把正态密度函数下的区域分为许多矩形层(ziggurats,像阶梯塔)。
随机挑选层和层内点,通过拒绝采样得到正态样本。
特点:
- 极高性能(工业级库,如 NumPy
np.random.randn()、Intel MKL 都使用);
- 理论上属于 accept–reject sampling;
- 细节较复杂,一般不手写。
伪代码概念图:
f(z) │ ___ │ | |______ │ | | |____ │ | | | |__ │ | | | | | └────────────────────────────── z
随机:
- 选层号;
- 在矩形内均匀取样;
- 若点在密度曲线下 → 接受,否则重采样。
🧮 方法 5:CLT Approximation(中心极限定理法)
思想:
如果生成 (U_i \sim [0,1]),则
[
Z = \sqrt{12} \left(\frac{1}{12}\sum_{i=1}^{12} U_i - \frac{1}{2}\right)
]
近似服从 (N(0,1))。
原因:
(\sum U_i) 的分布趋近正态(中心极限定理)。
代码:
U = np.random.rand(12, 10000) Z = np.sqrt(12) * (np.mean(U, axis=0) - 0.5)
特点:
- 概念简单;
- 但精度差(尾部不准确,分布较扁)。
✅ 总结对比表
方法 | 数学原理 | 精度 | 速度 | 是否常用 |
Inverse CDF (PPF) | 反变换法 | ⭐⭐⭐⭐ | ⭐ | 理论上最精确(用于分层采样) |
Box–Muller | 极坐标法 | ⭐⭐⭐⭐ | ⭐⭐⭐ | 常见教学与底层实现 |
Marsaglia Polar | 改进Box–Muller | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 实用高效(NumPy底层) |
Ziggurat | 分层拒绝采样 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 工业级最快算法 |
CLT Approx. | 中心极限定理 | ⭐⭐ | ⭐⭐⭐ | 简单但粗糙 |
📊 可视化小示例
import matplotlib.pyplot as plt Z_ppf = st.norm.ppf(np.random.rand(10000)) Z_box = np.sqrt(-2*np.log(np.random.rand(10000))) * np.cos(2*np.pi*np.random.rand(10000)) Z_clt = np.sqrt(12)*(np.mean(np.random.rand(12,10000),axis=0)-0.5) plt.hist(Z_ppf, bins=40, density=True, alpha=0.6, label="PPF") plt.hist(Z_box, bins=40, density=True, alpha=0.5, label="Box-Muller") plt.hist(Z_clt, bins=40, density=True, alpha=0.4, label="CLT Approx") x = np.linspace(-4,4,400) plt.plot(x, st.norm.pdf(x), 'k-', lw=2, label='True N(0,1)') plt.legend() plt.title("Different methods for sampling Normal from Uniform(0,1)") plt.show()
是否希望我帮你可视化 Ziggurat 与 Box–Muller 的几何原理图(展示采样点是如何被“映射”到正态分布上的)?
那样可以直观看到为什么这两种算法等价于从二维均匀点“拉伸”到正态形状。