服从正态分布随机数的生成

  • 生成单变量正态分布随机数
  • Box-Muller 算法
  • Accept/Reject 算法(接受/拒绝算法)
  • 附录
  • Box-Muller 算法的简要证明
  • 参考


生成单变量正态分布随机数

Box-Muller 算法

Box-Muller算法是利用两个i.i.d. (independent identical distribution)的服从正态分布python 服从正态分布N(0,1)_python ((0, 1) 区间的均匀分布)来生成两个i.i.d. 的标准正态分布(服从正态分布python 服从正态分布N(0,1)_python_02)的算法。 其具体步骤如下。

1. 首先我们生成两个服从服从正态分布python 服从正态分布N(0,1)_随机数_03的i.i.d.变量,假设为服从正态分布python 服从正态分布N(0,1)_python_04
2. 计算服从正态分布python 服从正态分布N(0,1)_正态分布_05,
服从正态分布python 服从正态分布N(0,1)_正态分布_06
3. 于是服从正态分布python 服从正态分布N(0,1)_python_07即为服从服从正态分布python 服从正态分布N(0,1)_python_08的两个独立的变量。

我们看到Box-Muller算法实际上是生成了两个服从正态分布的独立变量。当然如果我们只需要生成一个变量,我们取服从正态分布python 服从正态分布N(0,1)_随机数_09即可。利用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

如果我们生成服从正态分布python 服从正态分布N(0,1)_随机数_10个服从标准正态分布的随机数,其统计分布直方图与理论的pdf (probability density function,即服从正态分布python 服从正态分布N(0,1)_随机数_11) 比较如下:

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)

服从正态分布python 服从正态分布N(0,1)_服从正态分布python_12


我们可以看到统计分布直方图与理论服从正态分布python 服从正态分布N(0,1)_python_13非常接近。关于Box-Muller算法的简要证明可见附录。

Accept/Reject 算法(接受/拒绝算法)

下面我们来看如何用 Accept/Reject 算法来产生服从正态分布的随机数。Accept/Reject算法是一个产生随机数的通用的算法。我们不仅可以用Accept/Reject算法产生正态分布的随机数,也可以产生服从其他分布的随机数。Accept/Reject算法的具体步骤(理论依据)如下 [1]:
假设随机变量 服从正态分布python 服从正态分布N(0,1)_python_14, 服从正态分布python 服从正态分布N(0,1)_服从正态分布python_15, 即 服从正态分布python 服从正态分布N(0,1)_正态分布_16的 pdf 为服从正态分布python 服从正态分布N(0,1)_服从正态分布python_17服从正态分布python 服从正态分布N(0,1)_python_18的 pdf 为服从正态分布python 服从正态分布N(0,1)_服从正态分布python_19服从正态分布python 服从正态分布N(0,1)_服从正态分布python_17服从正态分布python 服从正态分布N(0,1)_服从正态分布python_19 的定义域相同。并且对任意 服从正态分布python 服从正态分布N(0,1)_统计学_22,存在 服从正态分布python 服从正态分布N(0,1)_python_23,使得 服从正态分布python 服从正态分布N(0,1)_正态分布_24。于是,为了生成服从 服从正态分布python 服从正态分布N(0,1)_随机数_25的随机变量 服从正态分布python 服从正态分布N(0,1)_正态分布_16,我们可以采用如下步骤:
a. 生成两个独立的随机变量 服从正态分布python 服从正态分布N(0,1)_服从正态分布python_27服从正态分布python 服从正态分布N(0,1)_python_18服从正态分布python 服从正态分布N(0,1)_服从正态分布python_29,即服从正态分布python 服从正态分布N(0,1)_统计学_30服从正态分布python 服从正态分布N(0,1)_服从正态分布python_31
b. 如果 服从正态分布python 服从正态分布N(0,1)_服从正态分布python_32,我们取 服从正态分布python 服从正态分布N(0,1)_python_33;否则,返回步骤 a。

下面我们证明通过步骤 a 和 b 生成的随机变量 服从正态分布python 服从正态分布N(0,1)_统计学_34 的 pdf 是服从正态分布python 服从正态分布N(0,1)_统计学_35

证明的思路是计算概率服从正态分布python 服从正态分布N(0,1)_python_36。如果我们能得到服从正态分布python 服从正态分布N(0,1)_统计学_37,那我们就证明了随机变量服从正态分布python 服从正态分布N(0,1)_统计学_34的pdf是服从正态分布python 服从正态分布N(0,1)_统计学_35
服从正态分布python 服从正态分布N(0,1)_服从正态分布python_40 服从正态分布python 服从正态分布N(0,1)_服从正态分布python_41

于是我们就证明了Accept/Reject 算法。

有了Accept/Reject 算法,我们来看怎么生成标准态分布服从正态分布python 服从正态分布N(0,1)_python_02的随机变量。

我们选取 服从正态分布python 服从正态分布N(0,1)_服从正态分布python_43, 服从正态分布python 服从正态分布N(0,1)_python_44。对于指数分布,我们有服从正态分布python 服从正态分布N(0,1)_随机数_45。首先我们须要验证服从正态分布python 服从正态分布N(0,1)_服从正态分布python_46 是否成立。 我们有服从正态分布python 服从正态分布N(0,1)_python_47。于是我们可以取服从正态分布python 服从正态分布N(0,1)_随机数_48的值为服从正态分布python 服从正态分布N(0,1)_服从正态分布python_49

从以上分析看到对于服从指数分布的随机变量服从正态分布python 服从正态分布N(0,1)_python_44,我们都可以用服从正态分布python 服从正态分布N(0,1)_python_51与均匀分布服从正态分布python 服从正态分布N(0,1)_统计学_52相结合来生成服从标准正态分布的随机数。那么,具体取服从正态分布python 服从正态分布N(0,1)_统计学_53为什么值才能使我们的Accept/Reject算法最高效呢?这里我们取使得服从正态分布python 服从正态分布N(0,1)_统计学_54最小的服从正态分布python 服从正态分布N(0,1)_统计学_53。这是为了使我们的Accept/Reject算法效率最高。即我们能够accept的次数最多 (Reject 的次数最少)。通过对服从正态分布python 服从正态分布N(0,1)_统计学_56求导我们可知使得服从正态分布python 服从正态分布N(0,1)_统计学_56最小的服从正态分布python 服从正态分布N(0,1)_统计学_53为1。从而我们选取服从正态分布python 服从正态分布N(0,1)_随机数_59

有了 服从正态分布python 服从正态分布N(0,1)_统计学_52服从正态分布python 服从正态分布N(0,1)_python_51,我们可以根据上述 Accept/Reject 算法的步骤,来生成服从服从正态分布python 服从正态分布N(0,1)_python_02的随机变量服从正态分布python 服从正态分布N(0,1)_统计学_34,具体步骤如下:
a. 生成独立的两个随机变量服从正态分布python 服从正态分布N(0,1)_统计学_52服从正态分布python 服从正态分布N(0,1)_python_51服从正态分布python 服从正态分布N(0,1)_服从正态分布python_43服从正态分布python 服从正态分布N(0,1)_随机数_59
b. 如果服从正态分布python 服从正态分布N(0,1)_随机数_68,则取服从正态分布python 服从正态分布N(0,1)_正态分布_69;反之则返回步骤a。注意到指数分布生成的随机变量只能为正,但是我们的正态分布的随机数可正可负。所以我们用一个服从正态分布python 服从正态分布N(0,1)_统计学_70均匀分布随机数来决定服从正态分布python 服从正态分布N(0,1)_python_51的符号。即如果随机数小于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生成服从正态分布python 服从正态分布N(0,1)_随机数_10个服从标准正态分布的随机数,其统计分布直方图与理论的 pdf 比较如下:

服从正态分布python 服从正态分布N(0,1)_统计学_73

附录

Box-Muller 算法的简要证明

对于Box-Muller算法的证明,我们利用两个变量的概率密度函数变换 [1]。我们已知服从正态分布python 服从正态分布N(0,1)_统计学_74,所以服从正态分布python 服从正态分布N(0,1)_统计学_75。我们要求出经过变换服从正态分布python 服从正态分布N(0,1)_服从正态分布python_76,
服从正态分布python 服从正态分布N(0,1)_正态分布_77之后, 新的变量服从正态分布python 服从正态分布N(0,1)_服从正态分布python_78的joint distribution。根据两个变量的概率密度函数变换公式,我们有服从正态分布python 服从正态分布N(0,1)_随机数_79服从正态分布python 服从正态分布N(0,1)_随机数_80 是这个变换的Jacobian,即服从正态分布python 服从正态分布N(0,1)_服从正态分布python_81

为了方便计算,我们用服从正态分布python 服从正态分布N(0,1)_正态分布_82

经过计算,我们有服从正态分布python 服从正态分布N(0,1)_随机数_83。而把服从正态分布python 服从正态分布N(0,1)_随机数_84服从正态分布python 服从正态分布N(0,1)_统计学_85相加,我们有服从正态分布python 服从正态分布N(0,1)_随机数_86 (这里可以看出这个变换是一一对应的)。代入服从正态分布python 服从正态分布N(0,1)_随机数_79,我们有服从正态分布python 服从正态分布N(0,1)_服从正态分布python_88。从而我们就证明了服从正态分布python 服从正态分布N(0,1)_服从正态分布python_78是独立的且服从标准正态分布。

参考

[1] George Casella, Roger L. Berger, Statistical inference, Chapter 4.3