Javascript 随机数函数 学习之二:产生服从正态分布随机数

一、为什么需要服从正态分布的随机函数

一般我们经常使用的随机数函数 Math.random() 产生的是服从均匀分布的随机数,能够模拟等概率出现的情况,例如 扔一个骰子,1到6点的概率应该相等,但现实生活中更多的随机现象是符合正态分布的,例如20岁成年人的体重分布等。

假如我们在制作一个游戏,要随机设定许许多多 NPC 的身高,如果还用Math.random(),生成从140 到 220 之间的数字,就会发现每个身高段的人数是一样多的,这是比较无趣的,这样的世界也与我们习惯不同,现实应该是特别高和特别矮的都很少,处于中间的人数最多,这就要求随机函数符合正态分布。

二、正态分布复习

图片来自:http://zh.wikipedia.org/zh-cn/%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83

具体性质也请查阅上面链接,描述正态分布的主要特征是均值和方差,如上图,最左的倒钟形图的均值为-2, 其余为0 ;

方差越大,钟形越扁平,方差越小越陡;

  • 密度函数图像关于均值对称。
  • 在x=μ±σ处,曲线有拐点。
  • 函数曲线下68.26%的面积在平均数左右的一个标准差σ的区间内。
  • 95.44%的面积在平均数左右两个标准差2σ的区间内。
  • 99.74%的面积在平均数左右三个标准差3σ的区间内。

当均值为0, 方差为 1 时称为标准正态分布;

三、由均匀分布经 “Box-Muller法” 转换为正态分布

通过查阅文献可知(请参见:http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform),有一个称为 Box-Muller (1958) 转换的算法能够将两个在区间(0,1] 的均匀分布转化为标准正态分布,其公式为:

y1 = sqrt( - 2 ln(u) ) cos( 2 pi v )

y2 = sqrt( - 2 ln(u) ) sin( 2 pi v )

因为三角函数计算较慢,我们可以通过上述公式的一个 polar form(极坐标形式)能够简化计算,

算法描述如下:

function getNumberInNormalDistribution(mean,std_dev){
    return mean+(randomNormalDistribution()*std_dev);
}

function randomNormalDistribution(){
    var u=0.0, v=0.0, w=0.0, c=0.0;
    do{
        //获得两个(-1,1)的独立随机变量
        u=Math.random()*2-1.0;
        v=Math.random()*2-1.0;
        w=u*u+v*v;
    }while(w==0.0||w>=1.0)
    //这里就是 Box-Muller转换
    c=Math.sqrt((-2*Math.log(w))/w);
    //返回2个标准正态分布的随机数,封装进一个数组返回
    //当然,因为这个函数运行较快,也可以扔掉一个
    //return [u*c,v*c];
    return u*c;
}

因此,假如我们要获得均值为180,要68.26%左右的NPC身高都在[170,190]之内,即1个标准差范围内,因此标准差为10, 可以通过getNumberInNormalDistribution(180,10) 调用,我们实验1000000词,得到结果如下:

// 身高:频率
128:1
132:1
133:1
134:1
135:1
136:2
137:4
138:8
139:11
140:14
141:19
142:28
143:41
144:54
145:80
146:133
147:153
148:235
149:333
150:429
151:598
152:764
153:1059
154:1314
155:1776
156:2290
157:2835
158:3503
159:4373
160:5513
161:6475
162:7809
163:9437
164:11189
165:13282
166:15020
167:17239
168:19215
169:21597
170:24336
171:26684
172:29000
173:31413
174:33179
175:35027
176:37084
177:38047
178:38968
179:39635
180:39700
181:39548
182:38960
183:38674
184:36948
185:35220
186:33224
187:31038
188:29198
189:26668
190:23893
191:21662
192:19476
193:16898
194:15056
195:13046
196:10971
197:9456
198:7928
199:6697
200:5370
201:4334
202:3548
203:2810
204:2330
205:1765
206:1350
207:1093
208:797
209:595
210:371
211:328
212:255
213:165
214:121
215:91
216:71
217:29
218:32
219:28
220:20
221:6
222:7
223:7
224:3
225:2
228:1

绘制成柱状图如下:

可见,这是有着非常明显的正态分布图像特征。

四、由均匀分布叠加获得正态分布

我们需要祭出万能的中心极限定理。

根据独立同分布的中心极限定理:设随机变量X1,X2,...Xn,...相互独立,服从同一分布,且数学期望为μ,标准差为σ (σ>0),则随机变量之和的标准化变量:

Y=((X1+X2+...+Xn)-nμ)/(sqrt(n)*sqrt(σ)) 近似服从标准正态分布 N(0,1)

如果我们将足够多个均匀分布随机变量相加,相加之和将服从正态分布。但是,我们需要累加多少个均匀分布才能较好低近似正态分布呢?

由于 X~U(0, 1) , 可得 μ=1/2, σ=sqrt(1/12),代入上面的式子即可近似模拟随机变量之和的概率密度函数(p.d.f).

下图是由2个服从 U(0,1) 分布的随机变量相加得到的 p.d.f 图像:

如果我们增加累加的均匀分布的数量会怎样呢?

上图是 n=3 时的图像,可以看到正态分布的形状出来了,但顶端还略为平缓。

特别低,当n=12时 (随机变量(X1+X2+...+Xn)的均值为6,方差为1)  这时有一个很好的特点,公式 Y=((X1+X2+...+Xn)-nμ)/(sqrt(n)*sqrt(σ)) 的分母正好为1,因此简化成了 Y=((X1+X2+...+Xn)-nμ),非常便于编程计算,并且已经非常接近于标准正态分布,请见下图:

也就是说均值为μ,标准差为σ 的独立同分布变量 X1,X2, ..., Xn 的算数平均数  T=(X1+X2+ ...+ Xn)/n,当n充分大时,近似地服从均值为μ,方差为σ*σ/n 的正态分布。

最后,代码如下:

function getNumberInNormalDistribution(mean,std_dev){
    return mean+(uniform2NormalDistribution()*std_dev);
}

function uniform2NormalDistribution(){
    var sum=0.0;
    for(var i=0; i<12; i++){
        sum=sum+Math.random();
    }
    return sum-6.0;
}

同样,将产生100万个随机数按频率画出直方图如下:

如何产生服从均匀分布的随机数呢?

请见上篇:

Javascript 随机数函数 学习之一:产生服从均匀分布随机数

时间: 2024-12-16 04:52:57

Javascript 随机数函数 学习之二:产生服从正态分布随机数的相关文章

记JavaScript的入门学习(二)

2016年11月25号,利用上午时间学习了JavaScript的数据类型和变量,下午就该去图书馆泡书了. 看完变量的本章节,发现我可能不能一天结束,那我就利用上午和晚上九点回来的时间完成吧.把心态调整好,不求速度,只求质量,学会即可,也许我就是下一个Js大神/偷笑/偷笑/偷笑 2.1    并得到如下小实例: 代码解释: 2.1.1  var myFirstVariable;此时变量的值是undefined,对于计算机而言,变量声明仅仅表明了变量的存在,并没有在其中保存任何实际的数据,但是und

Python Random函数学习(二)

一.Python Random模块常用方法和实例 random.random 描述:random.random()用于生成一个0到1到随机浮点数:0<=n<1.0 random.uniform 描述:random.uniform(a,b),用于生成一个指定范围内到浮点数,两个参数其中一个是上限,一个是下限.如果a>b,则生成到随机是n:a<=n<=b.如果a<b,则b<=n<=a 代码如下: print random.uniform(10,20) # 12.

MySQL函数学习笔记二:字符函数

1. 计算字符串字符数和字符串长度 - CHAR_LENGTH(s) CHAR_LENGTH(str): 返回str所包含的字符个数. mysql> select CHAR_LENGTH('MySQL'); +----------------------+ | CHAR_LENGTH('MySQL') | +----------------------+ |                    5 | +----------------------+ 2. 合并字符 - CONCAT(s1,s

excel函数学习系列二

按键F9可以查询函数中逻辑表达式的值 1.now() 返回电脑当前的日期时间,日期格式取绝于单元格的日期格式设定例:=NOW() 返回2015-7-31 8:34 2.day(日期)返回日期中的"日"例:=day(now()) 返回31=day(日期单元格) 返回日 3.month(日期函数或单元格引用) 返回月份=month(now()) 返回7 4.weekday(日期函数或单元格引用,一周从哪天开始) 返回星期几 一周从哪天开始:   1:返回1代表星期日,返回7代表星期六  

关于JavaScript设计模式的学习(二)

第二部分来了,是关于结构型的,同样的,还是在简书中,GitHub上也有代码示例和详细注释 简书:http://www.jianshu.com/p/face1be4b846 github:https://github.com/Stevenzwzhai/javascript-design-pattern

AWK指定范围随机数函数

AWK是一种优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一.rand 是awk内置的随机数函数但只能生成 0-1 之间的随机数且无法定义生成范围,为解决 AWK指定范围随机数 的问题,定义了下面的 irand 的随机数函数. cat infile | awk -F " " ' function irand(min, max){     max= max - min + 1;     num= rand() * 1000000000;     retur

javascript基础学习(二)

javascript的数据类型 学习要点: typeof操作符 五种简单数据类型:Undefined.String.Number.Null.Boolean 引用数据类型:数组和对象 一.typeof操作符 typeof操作符用来检测变量的数据类型,操作符可以操作变量也可以操作字面量. 对变量或值运用typeof操作符得到如下值: undefined----如果变量是Undefined类型: boolean-------如果变量是Boolean类型: number-------如果变量是Numbe

Javascript常用方法函数收集(二)

Javascript常用方法函数收集(二) 31.判断是否Touch屏幕 function isTouchScreen(){ return (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch); } 32.判断是否打开视窗 function isViewportOpen() { return !!document.getElementById('wixMobileV

JavaScript 基础学习(二)

JavaScript 基础学习(二) instanceof方法: var s = "hello"; var i = 8; //typeof 只能判断基本数据类型 alert(typeof(s)); alert(typeof (i)); //对于引用数据类型,用instanceof var s2=new String("hello2") alert(typeof(s2)); alert(s2 instanceof String);//true var n = new