服从正态分布随机数的生成
- 生成单变量正态分布随机数
- Box-Muller 算法
- Accept/Reject 算法(接受/拒绝算法)
- 附录
- Box-Muller 算法的简要证明
- 参考
生成单变量正态分布随机数
Box-Muller 算法
Box-Muller算法是利用两个i.i.d. (independent identical distribution)的 ((0, 1) 区间的均匀分布)来生成两个i.i.d. 的标准正态分布()的算法。 其具体步骤如下。
1. 首先我们生成两个服从的i.i.d.变量,假设为。
2. 计算,
3. 于是即为服从的两个独立的变量。
我们看到Box-Muller算法实际上是生成了两个服从正态分布的独立变量。当然如果我们只需要生成一个变量,我们取即可。利用Python,Box-Muller算法的代码如下:
import numpy as np
from scipy.stats import norm
from scipy.stats import uniform
import matplotlib.pyplot as plt
import pandas as pd
class Box_Muller:
def __init__(self, n: int):
"""
Suppose we want to generate n i.i.d. N(0, 1) random variables
"""
self.N = n
def generate_Box_Muller(self) -> 'list(float)':
"""
Use Box-Muller algorithm to generate self.N number of i.i.d. N(0, 1) random variables.
"""
res = []
for i in range(n):
U1, U2 = np.random.uniform(), np.random.uniform()
X1, X2 = np.cos(2 * np.pi * U1) * np.sqrt(-2 * np.log(U2)), \
np.sin(2 * np.pi * U1) * np.sqrt(-2 * np.log(U2))
res.append(X1)
return res
如果我们生成个服从标准正态分布的随机数,其统计分布直方图与理论的pdf (probability density function,即) 比较如下:
n = 10 ** 5
a = Box_Muller(n)
x = a.generate_Box_Muller()
plt.figure(figsize=(8, 6), dpi=100)
plt.hist(x, bins = 100, density=True)
x_forplot = np.linspace(-5, 5, 1000)
plt.plot(x_forplot, norm.pdf(x_forplot), linewidth = 3)
plt.xlabel("random variable values", fontsize=20)
plt.ylabel("histogram frequency", fontsize=20)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.legend(['theoretical pdf', 'Bos-Muller sample frequencies'], fontsize=10)
我们可以看到统计分布直方图与理论非常接近。关于Box-Muller算法的简要证明可见附录。
Accept/Reject 算法(接受/拒绝算法)
下面我们来看如何用 Accept/Reject 算法来产生服从正态分布的随机数。Accept/Reject算法是一个产生随机数的通用的算法。我们不仅可以用Accept/Reject算法产生正态分布的随机数,也可以产生服从其他分布的随机数。Accept/Reject算法的具体步骤(理论依据)如下 [1]:
假设随机变量 , , 即 的 pdf 为,的 pdf 为。 与 的定义域相同。并且对任意 ,存在 ,使得 。于是,为了生成服从 的随机变量 ,我们可以采用如下步骤:
a. 生成两个独立的随机变量 和 ,,即;。
b. 如果 ,我们取 ;否则,返回步骤 a。
下面我们证明通过步骤 a 和 b 生成的随机变量 的 pdf 是。
证明的思路是计算概率。如果我们能得到,那我们就证明了随机变量的pdf是。
于是我们就证明了Accept/Reject 算法。
有了Accept/Reject 算法,我们来看怎么生成标准态分布的随机变量。
我们选取 , 。对于指数分布,我们有。首先我们须要验证 是否成立。 我们有。于是我们可以取的值为。
从以上分析看到对于服从指数分布的随机变量,我们都可以用与均匀分布相结合来生成服从标准正态分布的随机数。那么,具体取为什么值才能使我们的Accept/Reject算法最高效呢?这里我们取使得最小的。这是为了使我们的Accept/Reject算法效率最高。即我们能够accept的次数最多 (Reject 的次数最少)。通过对求导我们可知使得最小的为1。从而我们选取。
有了 与 ,我们可以根据上述 Accept/Reject 算法的步骤,来生成服从的随机变量,具体步骤如下:
a. 生成独立的两个随机变量和,;;
b. 如果,则取;反之则返回步骤a。注意到指数分布生成的随机变量只能为正,但是我们的正态分布的随机数可正可负。所以我们用一个均匀分布随机数来决定的符号。即如果随机数小于0.5,则取为正;反之则取为负。
根据以上Accept/Reject算法的Python代码如下:
class Accept_Reject:
def __init__(self, n: int):
"""
Suppose we want to generate n i.i.d. N(0, 1) random variables
"""
self.N = n
def generate_accept_reject(self) -> 'list(float)':
"""
Use accept/reject algorithm to generate self.N random numbers that follows
N(0, 1) distribution.
"""
standNorm = []
M = 1 / np.sqrt(np.pi * 2) * np.e ** (0.5)
while len(standNorm) < self.N:
U = np.random.uniform()
W = np.random.uniform()
V = -np.log(W)
if U < np.e ** (-0.5 * (V - 1) ** 2):
if np.random.uniform() <= 0.5:
standNorm.append(V)
else:
standNorm.append(-V)
return standNorm
如果我们用Accept/Reject生成个服从标准正态分布的随机数,其统计分布直方图与理论的 pdf 比较如下:
附录
Box-Muller 算法的简要证明
对于Box-Muller算法的证明,我们利用两个变量的概率密度函数变换 [1]。我们已知,所以。我们要求出经过变换,
之后, 新的变量的joint distribution。根据两个变量的概率密度函数变换公式,我们有。 是这个变换的Jacobian,即。
为了方便计算,我们用。
经过计算,我们有。而把与相加,我们有 (这里可以看出这个变换是一一对应的)。代入,我们有。从而我们就证明了是独立的且服从标准正态分布。
参考
[1] George Casella, Roger L. Berger, Statistical inference, Chapter 4.3