由一个产生随机数函数的前端面试题引发的思考

702 阅读2分钟

参加前端群答疑live时,有小伙伴提出了这样一个问题:

粗略一看,这个创造随机数的函数似乎没什么问题,Math.ceil(Math.random()*(max-min))看上去是在获取[0,max-min]区间上随机的一个整数,再加上min的值,区间就刚好就变成了[min,max]。但是细细一想,很快就能发现问题的所在了--Math.ceil()执行的是向上舍入,即它总是将数值向上舍入为最接近的整数。那么问题就产生了,只有Math.random()值为0时,min + Math.ceil(Math.random()*(max-min))才能取得最小值min,很显然这是不符合随机数要求的。

那该如何改进呢?JS内置的Math对象,舍入方法除了有向上舍入的ceil(),还有向下舍入的floor()以及标准舍入(即将数值四舍五入为最接近的整数)的round()。而我们恰恰也可以利用floor()来产成真正指定范围的随机整数数

function random(min,max) {
    return min + Math.floor(Math.random()*(max - min + 1))
}

因为Math.random()**(max - min + 1)介于[0,max-min+1)之间,而floor()由于向下取整特性,所以Math.floor(Math.random()*(max - min + 1))落到了[0,max-min],而且不存在必须满足某种特殊条件才能取到某某特殊值的情况,所以用floor()改写过的random()是能满足面试题要求的。

既然用到了floor(),可能有人就会好奇了,先前提到的第三种舍入方法round()是不是同样也可以产生随机数呢。根据题设要求,我们很快能写出以下代码:

function random(min,max) {
    return min + Math.round(Math.random()*(max-min))
}

同样的,我们很快也能发现问题,那就是取值区间的边界值,也就是min和max,相对于取值区间内其他整数,能取到的概率大大变小。我们以min=0、max=2为例,根据使用round()编写的随机数函数,产生随机数0的区间为[0,0.5),产生随机数1的区间为[0.5,1.5),产生随机数2的区间为[1.5,2)。很明显,边界值概率只有取值区间内其他整数值的1/2,所以用round()也是无法生成真正的随机数的。

如何这篇文章对你有帮助,可以的话,点个喜欢呢