如何将均匀分布(大多数随机数生成器产生的分布,例如在0.0和1.0之间)转换为正态分布?如果我想要选择自己的均值和标准差怎么办?
如何将均匀分布(大多数随机数生成器产生的分布,例如在0.0和1.0之间)转换为正态分布?如果我想要选择自己的均值和标准差怎么办?
有很多方法:
Ziggurat算法对此非常有效,尽管Box-Muller变换更容易从头开始实现(而且不会非常缓慢)。
将任何函数的分布转换为另一个函数的分布,需要使用您想要的函数的反函数。
换句话说,如果您想要特定的概率函数 p(x),则通过对其积分-> d(x) = integral(p(x))并使用其反函数:Inv(d(x))来获得分布。现在使用随机概率函数(具有均匀分布),并将结果值通过函数 Inv(d(x)) 转换。您应该得到根据您选择的函数分布投掷的随机值。
这是一种通用的数学方法 - 使用它,您现在可以选择您拥有的任何概率或分布函数,只要其具有反函数或良好的反函数逼近。
希望这有所帮助,并感谢关于使用分布而不是概率本身的小提醒。
这是使用 Box-Muller 转换的极坐标形式的 JavaScript 实现。
/*
* Returns member of set with a given mean and standard deviation
* mean: mean
* standard deviation: std_dev
*/
function createMemberInNormalDistribution(mean,std_dev){
return mean + (gaussRandom()*std_dev);
}
/*
* Returns random number in normal distribution centering on 0.
* ~95% of numbers returned should fall between -2 and 2
* ie within two standard deviations
*/
function gaussRandom() {
var u = 2*Math.random()-1;
var v = 2*Math.random()-1;
var r = u*u + v*v;
/*if outside interval [0,1] start over*/
if(r == 0 || r >= 1) return gaussRandom();
var c = Math.sqrt(-2*Math.log(r)/r);
return u*c;
/* todo: optimize this algorithm by caching (v*c)
* and returning next time gaussRandom() is called.
* left out for simplicity */
}
其中R1,R2是随机均匀数:
正态分布,标准差为1:
sqrt(-2*log(R1))*cos(2*pi*R2)
这就是精确的方法...不需要进行那些缓慢的循环!
生成 n 个均匀分布的数字,将它们相加,减去 n*0.5,得到的结果近似为正态分布,其平均值为0,方差为(1/12) * (1/sqrt(N))
。(参见维基百科上的均匀分布)
n=10 可以快速得到半可靠的结果。如果想要更好的结果,可以选择泰勒的解决方案(如维基百科上的正态分布条目所述)。
八年后我仍能为Java添加一些内容,特别是关于Random.nextGaussian()方法,该方法可为您生成均值为0.0、标准差为1.0的高斯分布。
只需进行简单的加法和乘法运算即可将其平均值和标准差更改为您所需的数值。
randn_box_muller.m
:function [values] = randn_box_muller(n, mean, std_dev)
if nargin == 1
mean = 0;
std_dev = 1;
end
r = gaussRandomN(n);
values = r.*std_dev - mean;
end
function [values] = gaussRandomN(n)
[u, v, r] = gaussRandomNValid(n);
c = sqrt(-2*log(r)./r);
values = u.*c;
end
function [u, v, r] = gaussRandomNValid(n)
r = zeros(n, 1);
u = zeros(n, 1);
v = zeros(n, 1);
filter = r==0 | r>=1;
% if outside interval [0,1] start over
while n ~= 0
u(filter) = 2*rand(n, 1)-1;
v(filter) = 2*rand(n, 1)-1;
r(filter) = u(filter).*u(filter) + v(filter).*v(filter);
filter = r==0 | r>=1;
n = size(r(filter),1);
end
end
调用histfit(randn_box_muller(10000000),100);
的结果如下图所示:
很明显,与Matlab内置函数randn相比,这种方法效率非常低。
标准的Python库模块random拥有您所需的功能:
normalvariate(mu, sigma)
正态分布。mu是平均值,sigma是标准差。
关于算法本身,请查看Python库中random.py中的函数。