抽奖概率性的东西,就要用到随机函数Math.random(),但是只能随机产生0-1范围的随机数。
物品的概率=它的权重/总权重
那么实际操作中,如何实现概率分布,想象一下0-1是一条轴线。
各个商品的概率均<=1,且总和永远为1. 我们把轴线分成几个部分,各个部分分别被这几个商品占据着
0--0.01---0.03----0.15------0.40---------------------------1
function throwError(msg) {
throw new Error(msg);
}
class Roll {
constructor() {
this._ = []; // 存储要roll的列表
}
add(
item = throwError('Item must be required'), // 验证参数
rank = throwError('Rank must be required') // 验证参数
) {
const rankType = typeof rank;
if (rankType !== 'number')
throwError(`Rank must be a Number not ${rankType}`);
if (rank <= 0) throwError('require Rank>0');
this._.push({ item, rank }); // 把要roll的商品添加要列表中
}
roll() {
let totalRank = 0;
const random = Math.random(); // 产生一个随机数
let result = null;
const items = this._.slice().map(item => (totalRank += item.rank) && item); // 计算总权重
let start = 0; // 区间的开始,第一个是为0
while (items.length) {
const item = items.shift(); // 取出第一个商品
const end = start + item.rank / totalRank; // 计算区间的结束
if (random > start && random <= end) { // 如果随机数在这个区间内,说明抽中了该商品,终止循环
result = item;
break;
}
start = end; // 当前区间的结束,作为下一个区间的开始
}
return result ? result.item : null;
}
}
所谓实践才是检验真理的唯一标准,验证硬币正反两面的概率为50%需要无数次的实验。
const data = {};
// 10连抽
for (let i = 0; i <= 10; i++) {
let roller = new Roll();
roller.add('sp', 25);
roller.add('ssr', 100);
roller.add('sr', 2000);
roller.add('r', 7875);
const result = roller.roll();
if (data[result] === void 0) {
data[result] = 0;
} else {
data[result] = data[result] + 1;
}
}
console.log(JSON.stringify(data));
最后的输出结果
{"r":10}
{"r":9,"sr":1}
非常合理..