本文正在参加「金石计划 . 瓜分6万现金大奖」
前言
日常开发中大家经常使用 Math.random
来实现随机的效果,但是你知道 Math.random
实际上是“伪随机”吗?除了 Math.random
外,有什么方式可以实现真随机的?大家可以带着这些疑问继续阅读。
Math.random
介绍
Math.random()
函数返回一个浮点数,伪随机数在范围从0 到小于1,也就是说,从 0(包括 0)往上,但是不包括 1(排除 1),然后您可以缩放到所需的范围。
语法
Math.random()
返回值
返回一个浮点型伪随机数字,在0
(包括 0)和1
(不包括)之间。
Mat为.random 真的随机吗
我们可以简单测试一下 Math.random
生成数字的分布概率。这里我们可以使用一个数组来进行统计:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const locationMap = new Array(10).fill(0);
const shuffle = (nums) => {
const lens = nums.length;
nums.sort(() => Math.random() - 0.5);
locationMap[nums[lens - 1] - 1]++;
}
for (let i = 0; i < 100000; ++i) {
shuffle(arr.slice());
}
console.log(locationMap);
log:
[
10199, 2629, 8036,
7046, 9260, 12724,
16780, 9567, 10967,
12792
]
我们可以看出元素在各个位置出现的概率差距有可能是巨大的。因此通过 Math.random
打乱的数组并不是真正的随机。
我们还可以从这引申出另一个问题:很多面试题合集里会有打乱数组这么一道题,大部分题解用的都是 nums.sort(() => Math.random() - 0.5)
这个手段去实现“随机”打乱的,如果面试官好说话倒还好,如果面试官有意刁难,小伙伴们有办法写出真正随机的打乱数组方法吗?
为了做到真随机打乱数组,我们首先要做到元素在各个位置的出现的概率是一样或者近似的。
洗牌算法
洗牌算法的步骤就是将数组分为 未排序区域 和 已排序区域,不断的从未排序区域中 随机 出一个下标来,和未排序区域的最后一个元素 交换 ,进行 n 轮交换后就实现了等概率的洗牌算法了。
const shuffle = (arr) => {
const lens = arr.length;
for (let i = 0; i < lens; ++i) {
const idx = Math.floor(Math.random() * (lens - i));
[arr[idx], arr[lens - i - 1]] = [arr[lens - i - 1], arr[idx]];
}
return arr;
}
const arr = [6, 10, 4, 2, 7];
console.log(shuffle(arr));
由于洗牌算法不是本文的重点,这里就简单介绍一下。
crypto.getRandomValues
介绍
crypto.getRandomValues
方法让你可以获取 符合密码学要求的安全的随机值 。传入参数的数组被随机值填充。
语法
cryptoObj.getRandomValues(typedArray);
示例
/* 假设 window.crypto.getRandomValues 可用 */
var array = new Uint32Array(1);
window.crypto.getRandomValues(array);
我们看看输出:
封装函数
接下来我们封装一个获取随机数的方法:
const myRandom = function (){
const array = new Uint32Array(1);
const randomNum = crypto.getRandomValues(array)[0] / 0xffffffff;
return `${randomNum}`[2];
}
结束语
如果小伙伴们有别的想法,欢迎留言,让我们共同学习进步💪💪。
如果文中有不对的地方,或是大家有不同的见解,欢迎指出🙏🙏。
如果大家觉得所有收获,欢迎一键三连💕💕。