最近在琢磨内置对象Math
的时候,参考了很多网上资料,不过我在Google中搜索js 随机整数
,出来很多博客文章,很遗憾,在我看来排名靠前的这些文章都是错误的。接下来我将会论证我这一观点,同时把我所理解的Math.random()
方法跟你分享。
获取端点问题;
先看这一篇,Javascript实现随机整数。
在这篇文章中,作者给出了如下方法,根据他的文章描述,这个算法能产生start
到end
之间的随机整数。
javascript
function rnd(start, end){ return Math.floor(Math.random() * (end - start) + start); }
一个需要确认的问题
在说我的观点之前,我希望先明确一件事情,设想别人跟你提了一个需求:
需要一个生成
a
到b
之间随机整数的方法。
这时候我觉得你至少需要跟对方确认一个问题:包不包括两个端点,即a
和b
?
(其实还有一些问题需要考虑的,比如传入的a
和b
如果不是数字类型?如果是小数?如果是负数?如果a
比b
大?为了专注本文要讨论的内容,我们假定传入的a
和b
都是合法的正整数,且a
<b
。)
如果对方给你确认了,那是坠吼的;但是如果对方没有确认,如何理解这一需求?我认为在对上述需求没有更多说明的情况下,以下两个是靠谱的理解:
- 既要包括端点
a
,也要包括端点b
。- 既不包括端点
a
,也不包括端点b
。
所以,如果你实现的方法能获取的只包含其中一个端点,我觉得这种实现是不太理想的,你再琢磨这句话:a
到b
之间的随机整数。
如果你也认同我的这段论述,接下来回到开头那个方法,看他能不能实现获取从start
到end
之间的随机整数;方法主体就一个运算表达式:
javascript
return Math.floor(Math.random() * (end - start) + start);
逐步分析:
javascript
Math.random(); //[0,1) Math.random()*(end-start); //[0,end-start); Math.random()*(end-start)+start; //[start,end); Math.floor(Math.random()*(end-start)+start); //{ x | x>=start,x<end,x∈N}
第一行,Math.random()
方法返回的是0
到1
之间的浮点数,注意,__包括0
,不包括1
!__ECMA语言标准里明确规定了:
Returns a Number value with positive sign, greater than or equal to 0 but less than 1
所以,这篇博客,和这篇博客是不正确的;由于这两篇占据结果第一页的博文比较古老,我甚至在想是不是ECMA标准一开始不是这样子的,然后查了一下,ECMA标准从第一个版本对random
方法的规定就是现在这个样子。
第二行,Math.random()*(end-start);
则返回0
到end-start
之间的浮点数,包含0
,不包含end-start
。
第三行, Math.random()*(end-start)+start;
返回start
到end
之间的浮点数,包含start
,不包含end
。
第四行,Math.floor(Math.random()*(end-start)+start);
。这是最关键的一行,对上一行产生的浮点数进行向下取整,既然是向下取整,结果可能取到start
,却永远取不到end
。
根据前述理解,这样的实现我认为是不合要求的。所以,这篇文章也是不正确的。
另外,这几篇几个不错的JavaScript随机...、js生成随机数采用parseInt
对获取的浮点数进行取整操作,也是同样的问题,能取到左端点,却无法取到右端点。parseInt
操作浮点数的效果相当于Math.floor()
。
勘误:
之前写的上面这句话“
parseInt
操作浮点数的效果相当于Math.floor()
”,我在看了这篇文章之后发现这句话是不对的,抱歉。但我的结论是不变的:Math.random()
的结果范围包括0
,因此parseInt(Math.random())
也可能取到0
而这一篇JavaScript random方法得到随机整数,采用的是Math.ceil()
方法进行向上取整,
javascript
Math.ceil(Math.random()*3);//得到1-3的整数
这样确实既能取到右端点,也能取到左端点,但是作者的左端点标错了,这个是有可能能取到0的,尽管取到0的概率是无限趋近于0;
这样引伸出另一个问题:取得的随机整数范围里,各整数出现的概率是否一致?
获取概率问题
一个概率不均等的方法
在之前有位老师给出的获取随机数方法是下面这样(要求了必须包括端点);
javascript
function getRandom(n,m){ //省略特殊情形下的处理过程,比如n>m,或者n、m之一无法转化为有效数字; return Math.round(Math.random()*(m-n)+n); }
一个获取随机整数的方法,如果不格外的声明,我觉得有一个默认的条件应该是:
生成每个整数的概率应当是均等的;
根据第一部分的分析,这个方法能获取的浮点数的范围是n
到m
之间,包括n
,不包括m
;而这个方法中取整是采用Math.round()
四舍五入;这样的话:
- 当取出的结果
x∈[n,n+0.5)
时,会四舍五入为n;区间范围为0.5,概率为0.5/(m-n)*100%;- 当取出的结果
x∈[n+0.5,n+1.5)
时,会四舍五入为n+1;区间范围为1,概率为1/(m-n)*100%;- 当取出的结果
x∈[n+1.5,n+2.5)
时,会四舍五入为n+2;区间范围为1,概率为1/(m-n)*100%;- ......
- 当取出的结果
x∈[m-0.5,m)
时,会四舍五入为m;区间范围为0.5,概率为0.5/(m-n)*100%;
通过以上分析,该算法确实可以达到目标,取到n
到m
之间的随机整数,但是并不是每个整数出现的概率都是相等的,具体来说,取得两边端点的整数概率是其他数字的一半。所以,我认为这也是不够完美的。
这样的话,在写一个获得概率均等的随机整数方法的时候,相信你知道应该注意些什么问题了吧。
总结
总结一下,当我们在获取随机整数的时候,有两个问题需要特别注意:
- 一定要仔细检查两边端点是否是否能取到?是否与需求一致?
- 获取到的每个整数的概率是否均等?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。