壹 ❀ 引
本文属于四年前我入行前端不久的一次思考,当时因为接触到了一到使用随机数的笔试题(问题起源),但在GitHub对于此题的讨论中,我发现大部分人对于随机数API使用不太清晰,使用floor
或是ceil
各执一词,于是让我有了对于随机取整为什么一定要使用floor
的疑虑,所以在当时写下了这篇文章。
最近因为工作繁忙,连迁移文章都变的缓慢了,我觉得这样不行,这篇文章现在来看过于基础了,但我觉得还有点乐趣,如果阅读完能让你有一点点收获,那就更好了。
贰 ❀ round ceil floor有何区别
在弄懂这个问题前,我们先将这三个方法的区别说清楚,它们都是JavaScript
提供的数字取整方法,但却有着本质区别。
贰 ❀ 壹 Math.round()
Math.round()
的含义是将一个数字四舍五入为最接近的整数,四舍五入大家不会陌生,一个数字如果是 4 那就舍弃掉,如果是 5 ,那就进一。
Math.round(0.5); //1
Math.round(1.2); //1
Math.round(2.41); //2
Math.round(-3.55); //-4
贰 ❀ 贰 关于Math.ceil()
Math.ceil()
的含义是向上取整,说直白点,就是得到一个大于等于且最接近自己的整数,不难理解。
Math.ceil(0); //0
Math.ceil(1.2); //2
Math.ceil(2.41); //3
Math.ceil(-3.55); //-3
贰 ❀ 叁 Math.floor()
Math.floor()
刚好与Math.ceil()
相反,这是一对冤家,它表示的是向下取整,也就是找小于等于且最接近自己的整数。
Math.floor(0); //0
Math.floor(1.2); //1
Math.floor(2.41); //2
Math.floor(-3.55); //-4
叁 ❀ 随机整数概率问题
叁 ❀ 壹 使用round的问题
我们知道,当取[0,5]
范围内随机整数时,从概率角度,我们是希望每个随机数出现概率是相同的,那么当我们使用round
方法会有什么问题呢?
round
的作用是四舍五入取整,为了不那么复杂,我们将数字范围划分为0 -- 0.5 -- 1 -- 1.5 -- 2 -- 2.5 -- 3 -- 3.5 -- 4 -- 4.5 -- 5
这几个阶段。
很明显,当范围是0 -- 0.499
之前被四舍五入为0
,而0.5 -- 1
与1 -- 1.4999
可四舍五入为1
,同理,2 阶段,3 阶段,4 阶段都是前后两个范围,4.5 -- 5
可四舍五入为5
。
由此可以统计出1,2,3,4
出现的概率要整整比0,5
两个边界数字出现的概率高百分之50
,我们做个简单的测试:
Math.floor(Math.random() * 6);
这段代码,是取[0,5]
范围的随机整数,我们将floor
改为round
,简单修改逻辑(因为四舍五入,这里改为乘以5
),查看每五次取得数字的概率:
function randomArr() {
let arr = [],
length = 5;
while (length--) {
arr.push(Math.round(Math.random() * 5));
};
console.log(arr);
};
setInterval(function () {
randomArr();
}, 1000);
如上图,一眼扫下来出现0
与5
的概率,普遍比1,2,3,4
要低,很明显这对于0
与5
两个边界数字是不公平的。
叁 ❀ 贰 使用ceil取整的问题
有了上面的分析,ceil
就好理解多了,同样是[0,5]
范围取随机整数,我看同样做范围拆分。
由于是ceil
是向上取整,只有是0
时,才是0
;只要超过0
,0.1
甚至0.0001
都会向上取整变成1
,同理2,3,4,5
概率都会相同,我们可以得出使用ceil
对于0
是极不公平的,我们将上面的代码中的round
改为ceil
:
想要随机取整的数字是0
的概率,简直比中彩票头等奖还低,毕竟0-1
范围内的随机数字组合可以说无数个,外加上还有2,3,4,5
几个数字,这下总知道为啥不能用ceil
了吧。
叁 ❀ 叁 使用floor为什么可以?
floor
因为是向下取整,0-0.999
之前都是0
,1-1.999
之前都是1,每个数字概率相同。
所以我们取[0-5
]范围内整数时,使用的是Math.random() * 6
,得到的范围就是[0-5.999..]
,在通过下下取整,也就达到取[0-5]
的目的了。
叁 ❀ 肆 使用ceil取整后减去1模拟floor?
不对啊,向下取整概率相同,我向上取整不也一样?求[0-5]
范围时,我能不能利用向上取整,求一个[1-6]
再减去1
呢?很明显不行。
0.1-0.999
向上取整是1
,1.1-1.999
向上取整是2
,我们这个基础上减去一个1
,想法是好的,但这个想法忽略了比中彩票还难出现的数字0
,只要0
出现,我们根据通用想法减去1
,那就是-1
了。
Math.ceil(Math.random() * 6) - 1
这段代码貌似能正常取到0-5
范围的任意整数,其实当0
出现时,就BUG了,虽然我们不知道0
什么时候会出现。
肆 ❀ 总
那么文章到这,我们大概知道了这几个知识点:
1.round floor ceil
的作用与区别
2.为什么取范围随机整数使用floor
而不是round
或者ceil
3.利用floor
的思想模拟了ceil
减一的做法,结果行不通。
那么到这,算是给文章开头的问题解答疑惑了,现在你知道为什么不能使用round
或者ceil
取随机整数了吗?