Javascript 随机数函数 学习之一:产生服从均匀分布随机数
大家都知道Math.random是 javascript 中返回伪随机数的函数,但查看 MDN,
The Math.random()
function returns a floating-point, pseudo-random number in the range [0, 1)
that is, from 0 (inclusive) up to but not including 1 (exclusive)
Returns a Number value with positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy.
以上都是认为能够取到0,
但是在js权威书籍 Professional javascript for web developers 3rd edition 中,描述如下:
The Math.random() method returns a random number between the 0 and the 1, not including either 0, or 1.
明确指出不包括 0, 或 1,
到底怎么回事呢,那么我们来做一个实验:
var count=0; for(var i=0;i<100000000;i++){ if(Math.random()===0.0){ count++; } } console.log("count:"+count);
我们循环产生1亿个随机数,看能否得到0.0, 结果输出“count:0”,即不会得到0.0,
我们再来看能否得到1.0,
var count=0; for(var i=0;i<100000000;i++){ if(Math.random()===1.0){ count++; } } console.log("count:"+count);
结果输出“count:0”,即没有得到1.0,
接着我们把循环增加到10亿,结果是没有得到0.0,也没有得到1.0
把循环增加到100亿,结果是得到1次0.0, 但没有得到1.0
但实际上使用上,我们几乎不太可能去产生100亿个随机数,也不会取判断单独取0.0的概率,因此,从理论上说,Math.random()产生 [0,1)区间的数,但实际使用上能否取到0.0对我们的实用意义不大,可以看成是(0,1)开区间。
第一:因为对一个连续型随机变量来说,取具体一个点的概率是0,
第二:我们使用Math.random()时我们大多数应用无非是产生一个数字,或者是产生一组数字,
当需要产生一个数字时候,我们是不需要利用 “这个数字的出现概率” 这一信息的,仅使用这个数字而已。
当需要产生一组数字的时候,这时需要用到:“这个数字的出现概率”,这实际是要计算累积概率密度,我们知道在计算连续型随机变量落在某一个区间内的概率时是不需要区分该区间是开区间,闭区间或者是半闭区间,也就是说,取0.0或者不取0.0不影响我们结果。
例如,我们希望以相等的概率随机出现0,或者1,我们实际上可以通过计算落在(0, 0.5),(0.5, 1)这两个区间内的累积概率分别代表0,和1,当然这里的区间也可以是闭区间[0, 0.5] ... 所以取不取0.0对我们的结果没有影响。
因此,当我们需要获得指定区间的随机数时,我们可以在 Math.random()上乘以一定的数值,把整个区间范围拉长放大,
例如,我们需要返回 [1,10] (注意是闭区间,包括1和10),我们可以用 Math.floor(Math.random()*10+1),获得,
这里使用 floor 函数向下取整,而不能使用 round 函数四舍五入取整,round 取整后得到的是非均匀分布。
因为我们要每个数字出现的概率相等,就要保证每个区间长度相等,使用 round 取整后, 映射到1的区间为(1,1.5) 映射到2的区间为(1.5, 2.5)...... (9.5,10.5),(10.5,11) 可见其区间长度是不同的,即1和11出现的概率为其他数字出现概率的一半。
实验代码:
var temp; var arr=[0,0,0,0,0,0,0,0,0,0]; for(var i=0;i<1000000000;i++){ temp=Math.floor(Math.random()*10+1); arr[temp]=arr[temp]+1; } for (var j = 0; j < arr.length; j++) { console.log(j + ":" + arr[j]); }
结果为:
得到1的概率:0.099990183 得到2的概率:0.100023429 得到3的概率:0.100037064 得到4的概率:0.099983931 得到5的概率:0.100004498 得到6的概率:0.09999009 得到7的概率:0.099987366 得到8的概率:0.100001322 得到9的概率:0.099990864 得到10的概率:0.099991253
可以看到出现各个值的概率是基本相等的。
如果是使用 round,
var temp; var arr=[0,0,0,0,0,0,0,0,0,0,0,0]; for(var i=0;i<1000000000;i++){ temp=Math.round(Math.random()*10+1); arr[temp]=arr[temp]+1; } for (var j = 0; j < arr.length; j++) { console.log("得到"+j+"的概率:" + arr[j]/1000000000); }
得到:
得到1的概率:0.049978171 得到2的概率:0.100013093 得到3的概率:0.10004957 得到4的概率:0.099999108 得到5的概率:0.099998023 得到6的概率:0.099991361 得到7的概率:0.10000203 得到8的概率:0.09999017 得到9的概率:0.099980942 得到10的概率:0.100001593 得到11的概率:0.049995939
得到1和11的概率为其他数字的一半。
所以,Math.random 服从 [0,1) 区间的均匀分布,因为均匀分布是连续性分布,我们也可以说是 服从 (0,1)区间的均匀分布,这不影响我们使用累计概率密度。
因此,如果想获得 [min, max], 可以使用 Math.floor(Math.random() * (max - min + 1)) + min;
如果想获得 [min, max), 可以使用 Math.floor(Math.random() * (max - min )) + min;
如果想获得 (min, max], 可以使用 Math.ceil(Math.random() * (max - min )) + min;
我们再看一个应用,是 百度 2014年秋校园招聘 中,web前端开发岗位 的一道题,要求生成随机颜色,实现代码如下:
function randomColor(){ var rc=function(){ return Math.floor(Math.random()*256); } return "rgb("+rc()+","+rc()+","+rc()+")"; }
注意,rgb颜色区间为[0,255], 0和255是能够取到的。
再继续,如何产生服从正态分布的随机数呢?
请见下篇: