普通随机数
// 生成[min,max)之间的随机数
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
加权随机数
有时需要随机选取道具,但是只使用普通的Math.random不能完成需求,例如每个道具有不同的权重:
- apple: 50
- banana: 20
- orange: 30
- grape: 30
此时最适合使用加权随机数(总权重不需要保证为100):
function randIndexByWeights(weights) {
let totalWeight = 0;
const prefixSums = weights.map(weight => totalWeight += weight);
const randomNum = Math.random() * totalWeight;
for (let i = 0; i < prefixSums.length; i++) {
if (randomNum < prefixSums[i]) {
return i;
}
}
}
randIndexByWeights([50,20,30,30);
线性同余生成器
虽然编程语言都提供了开箱即用的随机数生成器,例如C语言的rand,js语言的Math.random(),但有时就是需要定制一个更适合业务逻辑的随机数生成器,所以掌握一个可控的伪随机数生成算法是很有必要的,其中最简单的就是线性同余随机数。
公式:
其中:
- m 是一个大于 0 的整数,称为模数。
- a 是一个大于 0 且小于 m 的整数,称为乘数。
- c 是一个大于等于 0 且小于 m 的整数,称为增量。
- 是初始种子值,也是第一个生成的随机数。
C语言的rand用的就是这种算法,一般会选取:
m = 32767
a = 1103515245
c = 12345
示例代码
class Random {
constructor(seed) {
this.seed = seed;
this.xn = seed;
this.m = 32767;
this.a = 1103515245;
this.c = 12345;
}
next() {
const x = (this.a * this.xn + this.c) % this.m;
this.xn = x;
return x;
}
}
function main() {
const r = new Random(0);
for (let i = 0; i < 100; ++i) {
console.log(r.next());
}
}
Fisher-Yates洗牌算法
如果需要打乱一个数组,可以用洗牌算法,其中最经典的就是Fisher-Yates算法:
function fisherYatesShuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
// 生成从0到i的随机数,包括0和i
let j = Math.floor(Math.random() * (i + 1));
// 交换arr[i]和arr[j]
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}