内容
Box-Muller 变换是一种从均匀分布的随机变量生成正态分布随机变量的方法。
具体来说,选取两个在区间 (0,1) 上均匀分布的随机变量 与 。按如下方式构造随机变量 与 :
此时, 与 服从均值为 0、方差为 1 的正态分布。
推导
设 与 是相互独立、均值为 0、方差为 1 的正态分布随机变量。记 与 分别为其概率密度函数。于是有:
由于 与 相互独立,它们的联合概率密度函数为:
现在进行坐标变换,定义:
于是,在极坐标下,联合分布变为:
由此,可推导出 与 的分布函数,分别记为 与 :
显然, 在 上服从均匀分布。设
其反函数为:
当 在 上服从均匀分布时, 的分布函数为 。因此,我们可以选取两个在 上均匀分布的随机变量 与 ,使得:
将上述各式代入前面的表达式中,可得:
由此恢复了 与 的原始表达式,它们服从均值为 0、方差为 1 的正态分布。
验证
为了生成服从标准正态分布的随机变量 ,我们注意到误差函数 的反函数不是初等函数。用级数展开来近似这个反函数会引入较大的计算误差。
另一方面,初等函数具有成熟的高精度算法。因此,我们希望用初等函数从均匀分布随机变量构造正态分布随机变量。
设 为均匀分布的随机变量。考虑如下变换:
接下来,我们验证 与 相互独立且服从标准正态分布。
定义:
注意到 ,。下面证明在极坐标变换下, 与 相互独立,并且它们产生标准正态随机变量。
考虑其中一个变量固定时另一个变量的概率密度函数。 的概率密度函数为:
这很直接。对于 ,其累积分布函数为:
因此, 的概率密度函数为:
因此, 与 的联合概率密度为:
由于极坐标变换给出:
我们得到 与 的联合概率密度:
这正是两个相互独立、服从标准正态分布的随机变量 与 的概率密度函数。
代码实现
function randInterval(): number {
return Math.random();
}
function boxMuller(mu: number, sigma: number): [number, number] {
const u = randInterval();
const v = randInterval();
const x = Math.cos(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
const y = Math.sin(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
return [x * sigma + mu, y * sigma + mu];
}
// 使用示例
const [value1, value2] = boxMuller(0, 1);
console.log(value1, value2);
// 输出两个服从正态分布的随机数
为了验证 boxMuller 函数生成的数服从正态分布,可以生成大量的随机数并绘制直方图与标准正态分布进行比较。你可以使用 plotly 等库将直方图可视化并评估输出。
下面是一个测试程序,它使用 boxMuller 函数生成数据集,进行基本的统计检验,并绘制直方图。假设你在 Node.js 环境中运行,可以使用 plotly-nodejs,或直接在浏览器中使用 plotly.js 生成图形。
首先,安装所需的依赖:
npm i plotly.js-dist
然后,执行以下代码:
import * as fs from "fs";
import * as plotly from "plotly.js-dist";
// 使用 Box-Muller 生成服从正态分布的随机数
function randInterval(): number {
return Math.random();
}
function boxMuller(mu: number, sigma: number): [number, number] {
const u = randInterval();
const v = randInterval();
const x = Math.cos(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
const y = Math.sin(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
return [x * sigma + mu, y * sigma + mu];
}
// 生成 n 个服从正态分布的随机数
function generateNormalDistribution(
mu: number,
sigma: number,
n: number
): number[] {
const values: number[] = [];
for (let i = 0; i < n / 2; i++) {
const [value1, value2] = boxMuller(mu, sigma);
values.push(value1, value2);
}
return values;
}
// 生成数据
const mu = 0;
const sigma = 1;
const sampleSize = 10000; // 样本大小
const values = generateNormalDistribution(mu, sigma, sampleSize);
// 计算生成的随机数的均值与方差
const mean = values.reduce((acc, val) => acc + val, 0) / values.length;
const variance =
values.reduce((acc, val) => acc + (val - mean) ** 2, 0) / values.length;
console.log(`均值:${mean}`);
console.log(`方差:${variance}`);
// 绘制直方图
const trace = {
x: values,
type: "histogram",
xbins: {
size: 0.1, // 直方图的组距
},
marker: {
color: "blue",
},
opacity: 0.7,
name: "Box-Muller 分布",
};
const data = [trace];
const layout = {
title: "生成的正态分布",
xaxis: { title: "数值" },
yaxis: { title: "频数" },
bargap: 0.05,
};
const graphOptions = {
filename: "box-muller-distribution",
fileopt: "overwrite",
};
fs.writeFileSync("box-muller.html", plotly.plot(data, layout, graphOptions));
console.log("Box-Muller 分布数据已保存为 box-muller.html。");
该程序使用 boxMuller 函数生成 10,000 个服从正态分布的随机数,并计算所生成数据集的均值与方差。随机数通过直方图可视化,便于与标准正态分布直接比较。直方图保存为名为 box-muller.html 的文件,可在浏览器中打开查看。