【架构师重学前端】Math.floor才是Math.random的绝佳搭配

420 阅读3分钟

前言

Math.random函数用于生成一个[0,1)范围的随机数

但如需生成(0,5)(2,3]之间的整数随机数,还需要与Math.floorMath.ceilMath.round函数的配合,但是对于三种函数的选用,真是任意选其一都可以吗?

显然是不行的,毕竟三种函数的功能都不一致,怎么可能任意选其一都可以

测试

下面以生成[0,5]之间的整数随机数为例,讲解三种函数不同的效果

Math.round生成随机数

Math.round函数把一个数字四舍五入后最接近的整数

先使用运算表达式

Math.round(Math.random() * 5);  

首先来看看生成100000次之后[0,5]每个数字的概率吧

let countObj = {  
   0: 0,  
   1: 0,  
   2: 0,  
   3: 0,  
   4: 0,  
   5: 0,  
};

const randomCount = 100000;

for (let index = 0; index < 100000; index++) {  
   let num = Math.round(Math.random() * 5);  
   countObj[num]++;  
}

let proObj = {};  
Object.entries(countObj).forEach(([key, value]) => {  
   proObj[key] = (value / randomCount) * 100 + "%";  
});  

image-20240622182427823

明显05的生成概率只有其他数值的一半,用图分析一下

未命名文件 (2)

生成0的范围只有数值[0,5),生成5的范围也只有[4.5,5)范围

这里可以改一下生成规则,Math.random() * 6 - 0.5生成数值范围是[-0.5,5.499999),再执行Math.round

Math.round(Math.random() * 6 - 0.5)  

image-20240622191543915

这里不是万事大吉哦!!!

但是还是要你提醒你Math.random()生成的数值范围是[0,1),当Math.random()生成0时,Math.random() * 6 - 0.5的值就是**-0.5**,再执行Math.round之后值就是**-1了,但是-1**不在[0,5]的范围哦,BUG不就出来了

未命名文件 (6)

Math.ceil

Math.ceil总是向上舍入,并返回大于等于给定数字的最小整数

生成表达式

   Math.ceil(Math.random() * 5);

看看100000次的生成效果吧

let countObj = {  
   0: 0,  
   1: 0,  
   2: 0,  
   3: 0,  
   4: 0,  
   5: 0,  
};

const randomCount = 100000;

for (let index = 0; index < 100000; index++) {  
   let num = Math.ceil(Math.random() * 5);  
   countObj[num]++;  
}

let proObj = {};  
Object.entries(countObj).forEach(([key, value]) => {  
   proObj[key] = (value / randomCount) * 100 + "%";  
});  

image-20240622184003887

0几乎永远取不到,你可能会疑惑,上面都是*0%*了,为什么还说是几乎喃?

不要忘了Math.random()生成的数值范围是[0,1)0是有很小的概率取到的哦

未命名文件 (5)

这里可能你会发现除0以外的其他数字概率几乎都是相同的呀,我们能不能改一下表达式

Math.ceil(Math.random() * 6) - 1  

Math.ceil(Math.random() * 5)可以生成*[1,5]的数值,那么Math.ceil(Math.random() * 6)就能生成[1,6]数值,这里只需要将结果-1*,不是*[1,5]*,属实是曲线救国了

但是还是要你提醒你Math.random()生成的数值范围是[0,1),当Math.random()生成0时,那么Math.ceil(Math.random() * 6)的值就是**-1**,这不就是BUG嘛!!!

未命名文件 (6)

Math.floor

Math.floor()返回小于等于一个给定数字的最大整数

生成随机数表达式

Math.floor(Math.random() * 5)  

还是生成100000看看效果吧

let countObj = {  
   0: 0,  
   1: 0,  
   2: 0,  
   3: 0,  
   4: 0,  
   5: 0,  
};

const randomCount = 100000;

for (let index = 0; index < 100000; index++) {  
   let num = Math.floor(Math.random() * 6);  
   countObj[num]++;  
}

let proObj = {};  
Object.entries(countObj).forEach(([key, value]) => {  
   proObj[key] = (value / randomCount) * 100 + "%";  
});  

image-20240622192445008

未命名文件 (7)

这里需要注意Math.floor向下取整,Math.random()生成值的范围[0,1)*5的只能取到[0,4],那么可以**6*

Math.floor(Math.random() * 6)  

image-20240622192812871

未命名文件 (8)

Math.ceilMath.round都会在Math.random()生成0时卡壳,但是可以看出Math.floor并不会

总结

生成随机数整数时一定要用Math.floor,完整的表达式是

Math.floor(Math.random() * ((maxValue - minValue) + 1)) + minValue