如何用均匀分布随机数生成正态分布随机数

article/2025/11/10 23:50:20

文章目录

  • 前言
  • The Box–Muller transform
  • The Ziggurat algorithm(金字形神塔)
  • 附录:Inverse transform sampling直观解释

前言

在Monte Carlo模拟技术中,许多地方都需要用到符合标准正态分布(高斯)的随机数来设计采样方案,因此了解如何用均匀分布随机数(实际上是均匀分布的伪随机数)来生成标准正态分布的随机数十分重要。本文将对这个最基本的问题做讨论,并提供c++11代码。

在讨论更高效的算法之前,我们先来看看能不能基于中心极限定理来设计算法。中心极限定理告诉我们,对于一组 i . i . d i.i.d i.i.d的随机数 { x k } ∼ U ( μ , σ 2 ) \{x_k\}\sim U(\mu,\sigma^2) {xk}U(μ,σ2),有 1 n ∑ i = 1 n x i → N ( μ , σ 2 / n ) \frac{1}{n}\sum_{i=1}^n x_i \rightarrow N(\mu,\sigma^2/n) n1i=1nxiN(μ,σ2/n)。这个算法有两个问题:

  1. 计算量大。生成一个数需要用n个均匀分布随机数。
  2. 无法准确刻画正态分布的末端效应。生成的数均有界,不会是很大的数。

此外,如果用“拒绝采样(rejection sampling)“的思路,在覆盖 f ( x ) = c e − x 2 / 2 f(x)=ce^{-x^2/2} f(x)=cex2/2的矩形内均匀投点,保留曲线下的点,则计算较复杂(exp函数),而且舍弃点多代价大。

我们下面介绍两种更高效的算法:The Box–Muller transform 和 The Ziggurat algorithm。它们一个是对分布函数做了变换,另一个还是使用了“拒绝采样“的思路,但并不是简单的仅用一个大矩形覆盖。

The Box–Muller transform

The Box–Muller transform 把一对均匀分布随机数映射到一对标准正态分布随机数。它有两种形式:

  1. 基本形式:用 ( 0 , 1 ) (0,1) (0,1)均匀分布随机数,需要计算三角函数 sin ⁡ \sin sin cos ⁡ \cos cos
  2. 极坐标形式:用 ( − 1 , 1 ) (-1,1) (1,1)均匀分布随机数,且不需要计算三角函数。

我们希望计算积分 I = ∫ − ∞ ∞ e − x 2 / 2 d x I=\int_{-\infty}^{\infty}e^{-x^2/2}dx I=ex2/2dx,可以先取它的平方并用极坐标表示, I 2 = ∫ − ∞ ∞ ∫ − ∞ ∞ e − ( x 2 + y 2 ) / 2 d x d y = ∫ 0 2 π ∫ 0 ∞ r e r 2 / 2 d r d θ . I^2=\int_{-\infty}^{\infty}\int_{-\infty}^{\infty}e^{-(x^2+y^2)/2}dxdy=\int_{0}^{2\pi}\int_{0}^{\infty}re^{r^2/2}drd\theta. I2=e(x2+y2)/2dxdy=02π0rer2/2drdθ.可以看到极角 θ \theta θ服从 ( 0 , 2 π ) (0,2\pi) (0,2π)均匀分布,径向距离 r r r服从 r e r 2 / 2 re^{r^2/2} rer2/2分布函数(即 r 2 r^2 r2服从 χ 2 分 布 \chi^2分布 χ2)。如果把 r r r的累积分布函数(cumulative distribution function)写出来 F ( x ) = ∫ 0 x r e r 2 / 2 d r = 1 − e − x 2 / 2 , F(x)=\int_0^xre^{r^2/2}dr=1-e^{-x^2/2}, F(x)=0xrer2/2dr=1ex2/2,我们就可以通过 F F F的逆函数 F − 1 ( u ) F^{-1}(u) F1(u),将均匀分布 u ∼ U ( 0 , 1 ) u\sim U(0,1) uU(0,1)映射到目标分布,即 x = − 2 ln ⁡ ( 1 − u ) x=\sqrt{-2\ln (1-u)} x=2ln(1u) ,也等价于 x = − 2 ln ⁡ u x=\sqrt{-2\ln u} x=2lnu (注意到如果 u ∼ U ( 0 , 1 ) u\sim U(0,1) uU(0,1),那么 1 − u 1-u 1u也是)。

基本形式:
假设 U 1 U_1 U1 U 2 U_2 U2是区间 ( 0 , 1 ) (0,1) (0,1)上的均匀分布随机数,令
Z 1 = R cos ⁡ ( θ ) = − 2 ln ⁡ U 1 cos ⁡ ( 2 π U 2 ) Z_1=R\cos(\theta)=\sqrt{-2\ln U_1}\cos(2\pi U_2) Z1=Rcos(θ)=2lnU1 cos(2πU2)
Z 2 = R sin ⁡ ( θ ) = − 2 ln ⁡ U 1 sin ⁡ ( 2 π U 2 ) Z_2=R\sin(\theta)=\sqrt{-2\ln U_1}\sin(2\pi U_2) Z2=Rsin(θ)=2lnU1 sin(2πU2)
Z 1 Z_1 Z1 Z 2 Z_2 Z2是两个独立的标准正态分布随机变量。

极坐标形式:
转换到极坐标的方法叫做Marsaglia polar method。该方法对水平和竖直方向 ( − 1 , 1 ) (-1,1) (1,1)正方形区域内均匀投点,把落在单位圆内的点 ( x , y ) (x,y) (x,y)通过 s = x 2 + y 2 s=x^2+y^2 s=x2+y2映射到单位圆周上,即 ( x s , y s ) (\frac{x}{\sqrt{s}},\frac{y}{\sqrt{s}}) (s x,s y),这样就可以不用计算三角函数。由于 s ≡ r 2 s\equiv r^2 sr2独立于极角,且在区间 ( 0 , 1 ) (0,1) (0,1)上均匀分布(因为 ∫ ∫ d x d y = ∫ ∫ r d r d θ = ∫ ∫ 1 2 d r 2 d θ \int\int dxdy=\int\int rdrd\theta=\int\int \frac{1}{2}dr^2d\theta dxdy=rdrdθ=21dr2dθ),因此可以用这个 s s s继续得到径向距离 − 2 ln ⁡ ( s ) \sqrt{-2\ln(s)} 2ln(s) 。于是我们有:
Z 1 = − 2 ln ⁡ U 1 cos ⁡ ( 2 π U 2 ) = − 2 ln ⁡ s ( u s ) = u ⋅ − 2 ln ⁡ s s , Z_1=\sqrt{-2\ln U_1}\cos(2\pi U_2)= \sqrt{-2 \ln s} \left(\frac{u}{\sqrt{s}}\right) = u \cdot \sqrt{\frac{-2 \ln s}{s}}, Z1=2lnU1 cos(2πU2)=2lns (s u)=us2lns ,
Z 2 = − 2 ln ⁡ U 1 sin ⁡ ( 2 π U 2 ) = − 2 ln ⁡ s ( v s ) = v ⋅ − 2 ln ⁡ s s . Z_2=\sqrt{-2\ln U_1}\sin(2\pi U_2)= \sqrt{-2 \ln s}\left( \frac{v}{\sqrt{s}}\right) = v \cdot \sqrt{\frac{-2 \ln s}{s}}. Z2=2lnU1 sin(2πU2)=2lns (s v)=vs2lns .
极坐标形式对应的c++代码如下(顺带说一下,c++标准函数库libstdc++里std::normal_distribution用的就是Marsaglia polar method,见这里):

//
// Marsaglia polar method. C++11 代码
// 由于生成的是一对i.i.d.高斯变量,每次生成的两个样本都可以利用起来!
//
bool _M_saved_available=false;
if (_M_saved_available) //输出下面暂时储存的x*mult
{_M_saved_available = false;return = _M_saved;
}
else
{double x, y, r2;do{x = 2.0 * rand() - 1.0;y = 2.0 * rand() - 1.0;r2 = x * x + y * y;}while (r2 > 1.0 || r2 == 0.0);const double mult = std::sqrt(-2 * std::log(r2) / r2);_M_saved = x * mult;_M_saved_available = true; //暂时储存x*mult,输出y*multreturn = y * mult;
}

算法局限性: 尾部截断(Tails truncation)
由于计算机有数值精度限制,其表示的数字不可能无线接近0。比如,使用32bit精度,那么最接近0的数字是 2 − 32 2^{-32} 232。因此,当 u 1 u_1 u1 u 2 u_2 u2都取 2 − 32 2^{-32} 232时, x x x的最大值是 − 2 ln ⁡ ( 2 − 32 ) cos ⁡ ( 2 − 32 ) ≈ 6.6 \sqrt{-2\ln(2^{-32})}\cos(2^{-32})\approx6.6 2ln(232) cos(232)6.6,即产生的随机数最大不会超过 6.6 6.6 6.6个标准差,这意味着会有 2 × 1 0 − 11 2×10^{-11} 2×1011的比例(尾部)会因为计算机精度问题无法采样到,这在大规模生成高斯随机数和研究rare events的时候要特别注意。

The Ziggurat algorithm(金字形神塔)

The Ziggurat algorithm 属于rejection sampling的一种。这个方法适用于分布函数是单调递减的情况,而正态分布的正半轴就属于这种情况。

课件截图

附录:Inverse transform sampling直观解释

数学解释: 假设 F ( x ) = P r [ X &lt; x ] F(x)=Pr[X&lt;x] F(x)=Pr[X<x]是CDF,并假设 u ∼ U ( 0 , 1 ) u\sim U(0,1) uU(0,1),定义映射 X = f ( x ) X=f(x) X=f(x),这个映射是 [ 0 , 1 ] → R [0,1]\rightarrow R [0,1]R。我们希望找到一个映射使得 X X X恰好服从 F ( x ) F(x) F(x)的CDF分布,即 P r [ f ( U ) &lt; x ] = F ( x ) Pr[f(U)&lt;x]=F(x) Pr[f(U)<x]=F(x)。显然, f = F − 1 f=F^{-1} f=F1,因为 P r [ X &lt; x ] = P r [ f ( u ) &lt; x ] = P r [ u &lt; F ( x ) ] = F ( x ) Pr[X&lt;x]=Pr[f(u)&lt;x]=Pr[u&lt;F(x)]=F(x) Pr[X<x]=Pr[f(u)<x]=Pr[u<F(x)]=F(x)

图像解释: 我们假设随机变量取值是离散的,比如一共有5种取值。右边是CDF,相当于把左边的概率“盒子“逐渐堆积起来,直到最后一列对应的 F F F正好等于1。这时候对 F ( x ) F(x) F(x)均匀采样的话,相当于在最后一列按照各个盒子面积的相对大小(各部分的概率大小)来采样, F ( x ) F(x) F(x)的逆函数只是用来回溯到对应的 x x x而已。随机变量取连续情况同样适用上述论证过程。
这里写图片描述


[1]:
[2]: https://github.com/jmcmanus/pagedown-extra “Pagedown Extra”
[3]: http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference
[4]: http://bramp.github.io/js-sequence-diagrams/
[5]: http://adrai.github.io/flowchart.js/
[6]: https://github.com/benweet/stackedit


http://chatgpt.dhexx.cn/article/wDFExsp8.shtml

相关文章

常见的概率分布并生成随机数

一、均匀分布&#xff08;Uniform Distribution&#xff09; 在相同长度间隔的分布概率是等可能的。 1.概率密度函数&#xff1a; 2.分布函数&#xff1a; 3.期望和方差&#xff1a; 4.生成随机数 import numpy as np #生成从0-1的均匀分布 np.random.rand(10)#生成十个均匀…

生成特定分布随机数的方法

生成随机数是程序设计里常见的需求。一般的编程语言都会自带一个随机数生成函数&#xff0c;用于生成服从均匀分布的随机数。不过有时需要生成服从其它分布的随机数&#xff0c;例如高斯分布或指数分布等。有些编程语言已经有比较完善的实现&#xff0c;例如Python的NumPy。这篇…

推导:通过均匀分布来产生任意分布随机数

最近想用C语言写一个产生服从指数分布的随机数的程序。从网上找了找&#xff0c;发现可以通过均匀分布来产生服从任意分布的随机数。然而&#xff0c;网上的推导不是很完善&#xff0c;我把自己的理解写在这里&#xff0c;有不严谨的地方请大家指正。 命题1&#xff1a;对一连续…

随机数生成(一):均匀分布

引言 许多应用中都需要用到随机数&#xff0c;如物理仿真、统计采样、密码学、博彩等。随机数一般可以通过两种方法得到。一种是基于物理现象由硬件产生。由此得到的随机数&#xff0c;在产生之前是不可预知的&#xff0c;因此&#xff0c;是真正的随机数。另一种是通过计算机算…

如何产生指定分布的随机数?

参考&#xff1a;https://www.cnblogs.com/xingshansi/p/6539319.html&#xff1b;    https://www.jianshu.com/p/3d30070932a8&#xff1b;    https://blog.csdn.net/pipisorry/article/details/50615652&#xff1b;    https://cosx.org/2015/06/generating-n…

一、三大基础随机分布与数学特征

一、三大基础随机分布 均匀、指数、正态 1、均匀分布 表示在相同长度间隔的分布概率是等可能的 其概率密度、均值、方差 2、指数分布 事件以恒定平均速度连续且独立地发生的过程(泊松过程中的事件之间的时间的概率分布) 其概率密度、均值、方差 3、正态分布 常见的连续概…

AttributeError:Can only use .str accessor with string values!

修改之前&#xff1a; 出现错误&#xff1a;意为matches不是字符串则使用此方法错误 修改方法&#xff1a; 将最后两行代码改为&#xff1a;

“ Can only use .str accessor with string values!”

“ Can only use .str accessor with string values&#xff01;” 出现错误 原代码 解决办法&#xff1a;

vue3报错‘get‘ on proxy: property ‘__accessor__‘ is a read-only and non-configurable data property on t

在使用arcgis地图时候 我把map对象存进了store里面共享数据 结果其他页面使用时候 给我甩了这样一个错误 get on proxy: property __accessor__ is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expe…

Access数据库是什么

数据是当今社会的命脉&#xff0c;因此自然而然地&#xff0c;很多注意力都集中在不同的数据库工具上。毕竟&#xff0c;如果用户有合适的工具&#xff0c;用户就有最有效的方法来处理当前的海量数据过剩问题&#xff0c;或许还能让整个过程变得更易于管理。为此&#xff0c;本…

【ERROR Error: No value accessor for form control with unspecified name attribute】

遇到问题&#xff1a; 控制台报错如下ERROR Error: No value accessor for form control with unspecified name attribute 解决&#xff1a; [(ngModel)]不能直接加在某些标签中 需要同时加ngDefaultControl

access是干什么的软件

Access是一款数据库应用开发工具软件&#xff0c;中文名:微软办公软件-关系数据库管理系统。 access安装包 Access是微软公司于1994年发布的微机数据库管理系统。作为一种功能强大的MIS系统开发工具&#xff0c;它具有界面友好、易学易用、开发简单、界面灵活等特点&#xff0…

Access数据库有什么用?该数据库有什么功能?

对于那些想寻找一个简单的数据库管理系统的用户来说&#xff0c;微软旗下的Access数据库也许是可以让其眼前一亮的工具。 数据库基本定义 Access是Microsoft 365套件工具随附的强大生产力工具&#xff0c;该工具允许用户创建以有组织的结构存储信息的自定义数据库&#xff0c;还…

@Accessors

Accessors 作用&#xff1a;存取器&#xff0c;用于配置getter和setter方法的生成结果 三个属性&#xff1a;fluent、chain、prefix 1、fluent&#xff1a;流畅的&#xff0c;设置为true&#xff0c;getter和setter方法的方法名都是基础属性名&#xff0c;且setter方法返回当前…

使用pandas对数据提取时报错,AttributeError: Can only use .str accessor with string values!

from pandas import DataFrame from pandas import read_excel df read_excel(ri_nuc.xls,sheet_nameSheet4) df.head() 运行结果如下图 df.电话.head().str.strip() 结果出现报错 AttributeError: Can only use .str accessor with string values! 这句话翻译成&#xf…

@Accessors 注解详解

??前言1. Accessors 源码2. Accessors 属性详解2.1 fluent 属性2.2 chain 属性2.3 prefix 属性 前言 在你的工作中&#xff0c;有时候可能会看到Accessors(chain true)这样的注解&#xff0c;他是 lombok 插件包中的一个注解&#xff0c;那么它是什么意思呢&#xff1f; 1.…

lombok里的@Accessors注解

lombok里的Accessors注解 Accessor的中文含义是存取器&#xff0c;Accessors用于配置getter和setter方法的生成结果。 有三个属性 fluent–一个布尔值。如果属实&#xff0c;对于吸气pepper只是pepper()&#xff0c;并且设置器pepper(T newValue)。此外&#xff0c;除非指定…

Linux系统函数之lseek函数

Linux系统函数之lseek函数 fseek与lseek的对比文件存放位置:示例代码:使用lseek函数拓展文件的长度 fseek与lseek的对比 标准C库的函数#include <stdio.h>int fseek(FILE *stream, long offset, int whence);Linux系统函数#include <sys/types.h>#include <uni…

六、Linux文件 - lseek函数

目录 1.lseek函数 2.lseek函数实战 2.1宏SEEK_CUR的用法 2.2宏SEEK_END的用法 3.Open函数实战 - O_APPEND的用法 4.Linux在库函数中寻找相应的宏定义 1.lseek函数 off_t lseek(int fd,off_t offset,int whence);光标的偏移量 fd:文件描述符offset:偏移量whence: SEEK_…

【Linux系统IO函数】lseek函数

Linux系统IO函数—lseek函数 1.1 lseek函数与标准C库的fseek函数 lseek函数对应标准C库中的fseek函数 查看标准C库中的fseek函数使用说明&#xff1a; &#xff08;shell输入&#xff09; man 3 fseekfseek函数&#xff1a; #include <stdio.h> int fseek(FILE *st…