结论:
在网上找怎么写,找到个很好的例子,这个逻辑很有启发,但是写完调试发现逻辑是有问题的,然后进行了修正,希望大家用时候注意下。
找到的百度文章 点击查看
过程:
诉求是控制中奖的概率,比如随机给用户积分,需要控制每种积分概率,最低是1分。同样的这个可以类比奖品和未中奖。给的不同积分就是不同等级的奖品,1分就是未中奖
| 积分 | 6 | 5 | 4 | 3 | 2 | 1 |
|---|---|---|---|---|---|---|
| 概率 | 0.5 | 2.5 | 5 | 10 | 15 | 67 |
根据网上例子写出来代码是
const priceArr = [6, 5, 4, 3, 2, 1]; //奖品数组,从大到小
const priceWeight = [0.5, 2.5, 5, 10, 15, 67]; //对应位置的概率
randomPrize(prizeArr, weightArr) {
let weightSum = weightArr.reduce((a, b) => {
return a + b;
}, 0);
const random = Math.random() * weightSum;
const concatWeightArr = weightArr.concat(random);
const sortedWeightArr = concatWeightArr.sort((a, b) => {
return a - b;
});
let randomIndex = sortedWeightArr.indexOf(random);
randomIndex = Math.min(randomIndex, prizeArr.length - 1);
return prizeArr[randomIndex];
}
思路:
1、权重求和
2、随机从0到权重和之间的数字
3、把随机的数字放到权重数组中
4、新生成的权重数字从小到大排序
5、从新权重数组中找到随机数的位置(防止位置超过奖品数组的大小,超过就设置最后一个)
6、最后拿位置去奖品数组中获取奖品
用例结果
执行1000次
| 积分 | 6 | 5 | 4 | 3 | 2 | 1 |
|---|---|---|---|---|---|---|
| 期望(%) | 0.5 | 2.5 | 5 | 10 | 15 | 67 |
| 实际次数(次) | 7 | 27 | 31 | 52 | 45 | 838 |
| 实际概率(%) | 0.7 | 2.7 | 3.1 | 5.2 | 4.5 | 83.8 |
执行10000次
| 积分 | 6 | 5 | 4 | 3 | 2 | 1 |
|---|---|---|---|---|---|---|
| 期望(%) | 0.5 | 2.5 | 5 | 10 | 15 | 67 |
| 实际次数(次) | 48 | 191 | 279 | 503 | 506 | 8473 |
| 实际概率(%) | 0.48 | 1.91 | 2.79 | 5.03 | 5.06 | 84.73 |
执行100000次
| 积分 | 6 | 5 | 4 | 3 | 2 | 1 |
|---|---|---|---|---|---|---|
| 期望(%) | 0.5 | 2.5 | 5 | 10 | 15 | 67 |
| 实际次数(次) | 511 | 2037 | 2499 | 5033 | 5016 | 84904 |
| 实际概率(%) | 0.511 | 2.037 | 2.499 | 5.033 | 5.016 | 84.904 |
分析:
1、结果看其实就第一个基本接近,其他都是不准的。
2、代码里weightSum咋算都是100。本来就是按照100分配的。
优化代码如下:
const priceArr = [6, 5, 4, 3, 2, 1]; //奖品数组,从大到小
const priceWeight = [0.5, 2.5, 5, 10, 15, 67]; //对应位置的概率
randomPrize(prizeArr, weightArr) {
const newWeightArr = []; //新排序数组
//转化实际位置的数字
weightArr.reduce((a, b) => {
newWeightArr.push(a + b);
return a + b;
}, 0);
const weightNum = 100;
const random = Math.random() * weightNum;
const concatWeightArr = newWeightArr.concat(random);
const sortedWeightArr = concatWeightArr.sort((a, b) => {
return a - b;
});
let randomIndex = sortedWeightArr.indexOf(random);
//去除 randomIndex = Math.min(randomIndex, prizeArr.length - 1);
return prizeArr[randomIndex];
}
用例结果
执行1000次
| 积分 | 6 | 5 | 4 | 3 | 2 | 1 |
|---|---|---|---|---|---|---|
| 期望(%) | 0.5 | 2.5 | 5 | 10 | 15 | 67 |
| 实际次数(次) | 5 | 23 | 48 | 105 | 127 | 692 |
| 实际概率(%) | 0.5 | 2.3 | 4.8 | 10.5 | 12.7 | 69.2 |
执行10000次
| 积分 | 6 | 5 | 4 | 3 | 2 | 1 |
|---|---|---|---|---|---|---|
| 期望(%) | 0.5 | 2.5 | 5 | 10 | 15 | 67 |
| 实际次数(次) | 37 | 239 | 535 | 987 | 1512 | 6690 |
| 实际概率(%) | 0.37 | 2.39 | 5.35 | 9.87 | 15.12 | 66.9 |
执行100000次
| 积分 | 6 | 5 | 4 | 3 | 2 | 1 |
|---|---|---|---|---|---|---|
| 期望(%) | 0.5 | 2.5 | 5 | 10 | 15 | 67 |
| 实际次数(次) | 505 | 2453 | 4999 | 9936 | 15031 | 67076 |
| 实际概率(%) | 0.505 | 2.453 | 4.999 | 9.936 | 15.031 | 67.076 |
分析:
基本符合预期的。
总结:
现成的是方便,但是要用前要思考,结合实际,弄懂原理。