Java中实现正态随机分布

这次内容分为两部分,如果你对算法怎么实现没兴趣的话,看看简单版如何调用库函数就好了,如果你和我一样对算法又有那么点兴趣,那就接着看看进阶版,看不懂也不亏嘛。

 

简单版

如果不想自己写的话,Java util库中早就有提供好的标准正态分布的随机函数:

//标准正态随机分布
public static double StandardNormalDistribution(){
       java.util.Random random = new java.util.Random();
       return random.nextGaussian();
}

库里只有标准正态分布N(0,1),那么想得到满足N(u,v)的随机数怎么办呢,利用N(0,1)有个近似的简单算法:

//普通正态随机分布
//参数 u 均值
//参数 v 方差
public static double NormalDistribution(float u,float v){
       java.util.Random random = new java.util.Random();
       return Math.sqrt(v)*random.nextGaussian()+u;
}

有人可能会怀疑上面   Math.sqrt(v)*random.nextGaussian()+u  这个公式到底靠谱吗?我们先来证明下吧:

N(0,1)-N(\mu,\sigma^2)

P(y)_{N(0,1))}=P(x)_{N(\mu,\sigma^2))}

\frac{1}{\sqrt(2\pi)}\exp^{-\frac{y^2}{2}}=\frac{1}{\sqrt(2\pi)*\sigma}\exp^{-\frac{(x-\mu)^2}{2(\sigma)^2}}

y\approx\frac{x-\mu}{\sigma}

x=\sigma y+\mu

同时,我们在Matlab里面验证一下,附上Matlab代码:

%%%%随机数数量
L=1000000;
x=zeros(1,L);
y=zeros(1,L);

%%%%均值
u=10;
%%%%方差 
v=10;

%%%%normrnd是正态分布函数
for i=1:length(x)
    x(i)=u+sqrt(v)*normrnd(0,1);
    y(i)=normrnd(u,v);
end

figure(1);
hist(x,1000);
title('近似算法');

figure(2);
hist(y,1000);
title('标准算法');

来看结果:

Java中实现正态随机分布_第1张图片可见图像还是非常接近的,说明近似效果还是相当不错的。

 

进阶版

如果你跟我一样很闲,想要自己实现下正态分布的算法,那就接着看吧。我这里主要介绍Box–Muller算法和中心极限定理。

Box–Muller算法

Box–Muller算法是由 George E. P. Box 与 Mervin E. Muller 在1958年提出的,证明过程很复杂这里就不提了,但是公式却很简单,看公式:

Java中实现正态随机分布_第2张图片

感谢网友提供的图。这个公式主要利用了两个均匀分布产生的随机数U1,U2。Z0和Z1实现的效果是一致的,我们在代码中实现一下:

//生成两个(0~1)的随机数 x1,x2;
double x1=Math.Random();
double x2=Math.Random();

double Z0=Math.sqrt(-2*Math.log(x1))*Math.cos(2*Math.PI*x2);
double Z1=Math.sqrt(-2*Math.log(x1))*Math.sin(2*Math.PI*x2);

还是非常简单的。那么真实的效果又如何呢,在Matlab里面验证一下:

Java中实现正态随机分布_第3张图片

用了一百万个随机数据画出的分布直方图,可见还是非常不错的。当然这里实现的还是标准正态分布N(0,1),若要得到一般的正态分布N(u,v),还需要利用之前的公式再转换一下。

 

中心极限定理

中心极限定律很有意思,简而言之呢就是说从各种独立分布中都可以找到一个规律,这个规律近似满足正态分布,具体的推导方面可以自行研究。

假设有一个独立随机的分布 X={x1,x2,x3,...,xn}

可知X的期望 E{X}=u

       X的方差 D{X}=v

Y=\frac{\sum X -n*u}{\sqrt{n*v}}

n越大,Y的分布就越近似于正态分布。而当n接近无穷大时,得到的Y可以看作标准正态分布N(0,1)。

 

那么我们就试着用均匀分布来实现正态分布:

public static float getStandardGaussian(){
        //(0~1)均匀分布均值为0.5
        float u=0.5;
        //(0~1)均匀分布方差为1/12
        float v=1/12;

        //n取100个点
        int n=100;

        float sum=0;
        for(int i=0;i

来吧,看看Matlab的验证效果如何:

Java中实现正态随机分布_第4张图片

还是很不错的。不过有个缺点就是每次计算n 遍,没有Box–Muller算法计算的快。同样的,计算得到的是标准正态随机分布,一般的分布还是需要转换一下才行。

你可能感兴趣的:(算法进阶)