使用 Math.random 来生成随机数在日常操作中是普遍并且是简单的。如下:
let rnd = Math.random() * 10;
上述代码的作用是生成 0~10
的随机数。
那么,这些随机数是出现的概率是均衡的吗?
笔者没办法直接回答这个问题。因为生成的随机数个数理论上是一个无穷大的数集,不过,可以按数值大小范围来估算数值的分布是否平均:
let bucket = new Array(10).fill(0);
let res = new Array(500000);
// 做50万次随机数
for(let i = 0; i < 500000; ++i) {
res[i] = Math.random() * 10;
// 数值大小规类
let num = res[i];
for(let i = 0; i < 10; ++i) {
if(num >= i * .1 && num < (i + 1) * .1) {
++bucket[i];
}
}
}
bucket.forEach(
(count, index) => {
console.log(index * .1 + "~" + (index + 1) * .1 + "的概率:" + count / 500000)
}
)
以下是一次输出结果:
- 0~0.1的概率:0.010244
- 0.1~0.2的概率:0.009868
- 0.2~0.3的概率:0.009744
- 0.3~0.4的概率:0.010024
- 0.4~0.5的概率:0.009818
- 0.5~0.6的概率:0.009814
- 0.6~0.7的概率:0.010048
- 0.7~0.8的概率:0.009834
- 0.8~0.9的概率:0.010154
- 0.9~1的概率:0.009988
结果显示 Math.random() * 10
生成的数值分布是比较均衡的。
但是,如果把「随机数」改成「随机整数」。代码改成:
let rnd = Math.round(Math.random() * 10);
上面代码是生成 0~10
的随机整数。
那么,这些随机整数是出现的概率是均衡的吗?
笔者直观上觉得是随机的,但是实际情况并不是!测试代码如下:
let res = new Array(11).fill(0);
// 做一万次随机数
for(let i = 0; i < 500000; ++i) {
++res[Math.round(Math.random() * 10)];
}
res.forEach((count, index) => console.log(index + "的概率:" + count / 500000))
以下是一次结果:
- 0的概率:0.050028
- 1的概率:0.09957
- 2的概率:0.100616
- 3的概率:0.099684
- 4的概率:0.100672
- 5的概率:0.099588
- 6的概率:0.100446
- 7的概率:0.100276
- 8的概率:0.099664
- 9的概率:0.099628
- 10的概率:0.049828
不难发现,0
和 10
的概率是其它数值的一半左右,而其它数值的概率相差无几。
Math.round 方法是造成「随机整数」不均衡的原因
Math.round/Math.ceil/Math.floor
这三个函数作用是使一定范围内的实数转换成同一个整数。以 Math.round 为例如下:
从上图可以直观地看到,头尾两数(0&10)的取值范围是其它整数的一半!
生成随机均衡整数的一种方案
其实,只需要保证取值范围的长度一致即可以实现随机均衡整数。以下是笔者实现的一种方案:
let res = new Array(11).fill(0);
// 做一万次随机数
for(let i = 0; i < 500000; ++i) {
++res[Math.floor(Math.random() * 11)];
}
res.forEach((count, index) => console.log(index + "的概率:" + count / 500000))
以下是一次输出结果:
- 0的概率:0.090828
- 1的概率:0.090988
- 2的概率:0.09048
- 3的概率:0.08958
- 4的概率:0.091516
- 5的概率:0.090826
- 6的概率:0.09112
- 7的概率:0.091668
- 8的概率:0.090918
- 9的概率:0.090626
- 10的概率:0.09145
从结果上看分布是均衡的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。