要编程得到服从均匀分布的伪随机数是容易的。C语言、Java语言等都提供了相应的函数。但是要想生成服从正态分布的随机数就没那么容易了。
得到服从正态分布的随机数的基本思想是先得到服从均匀分布的随机数,再将服从均匀分布的随机数转变为服从正态分布。接下来就先分析三个从均匀分布到正态分布转变的方法。然后编程实现其中的两个方法并对程序实现运作的效果进行统计分析。
1、 方法分析
(1) 利用分布函数的反函数
若要得到分布函数为F(x)的随机变量Y。
可令
, 其中u是服从均匀分布的随机变量,有
因而,对于任意的分布函数,只要求出它的反函数,就可以由服从均匀分布的随机变量实例来生成服从该分布函数的随机变量实例。
现在来看正态分布的分布函数,对于
,其分布函数为:
显然,要想求其反函数是相当困难的,同时要想编程实现也很复杂。可见,用此种方法来生成服从正态分布的随机变量实例并不可取。
(2) 利用中心极限定理
第二种方法利用林德伯格—莱维(Lindeberg—Levi)中心极限定理:如果随机变量序列
独立同分布,并且具有有限的数学期望和方差
则对一切x∈R有
因此,对于服从均匀分布的随机变量Xi,只要n充分大,随机变量
为正态分布。我们将实现这一方法。这里 Ri为[0,1]之间均匀分布的随机数
package cn.tan.basicmath;
/**
*
* @author Özil ισνΞ
*
*/
public class RanZT {
/**
* 这里是我上一篇日记里面产生[0,1]随机数的算法
* @param r 将变量的地址传进函数中,以便每次调用后更新随机种子的值,否则将得到完全一样的数据从而失去随机性
* 这里r我们初始化为5
* @return
*/
static double seftRandom(double[] r){
double base,u,v,p,temp1,temp2,temp3;
//基数
base = 256.0;
//两个常数 uv;
u = 17.0;
v = 139.0;
//计算总值
temp1 = u*(r[0])+v;
//计算商
temp2 = (int)(temp1/base);
//计算余数,1到base的余数
temp3 = temp1 - temp2*base;
//更新随机种子,为下一次使用
r[0] = temp3;
//随机数赋值 ,获取[0,1]的随机数
p = r[0]/base;
return p;
}
/**
* 正态分布随机数生成法
* @param u 正态分布的均值
* @param t 正态分布的方差0
* @param r 随机种子
* @param n 正态分布公式的n
* @return
*/
static double randZT(double u,double t,double[]r,double n){
int i;
double total = 0.0;
double result;
for(i = 0;i<n;i++){
//累加
total += seftRandom(r);
}
//得到的随机数
result = u+t*((total-n/2.0)/Math.sqrt(n/12));
return result;
}
public static void main(String[] args){
int i;
double u,t,n;
double[] r = {5.0};
//初始化正态分布的均值和方差
u = 2.0;
t = 3.5;
n = 12.0;
System.out.println("产生十个正态分布的随机数:");
//循环调用
for(i = 0;i<10;i++){
System.out.printf("%10.5f\n",randZT(u,t,r,n));
}
System.out.println();
}
}