带百分比的随机数

Random number with percentage

我有这个:

var users = [user1, user2, user3];
var chances = [20, 20, 60];
var total = 100;
var random = Math.floor((Math.random() * total) + 1);

if (random > 40 && random < 100) {
    console.log('winner:', users[2]);
}

if (random > 20 && random < 40) {
    console.log('winner:', users[1]);
}

if (random > 0 && random < 20) {
    console.log('winner:', users[0]);
}

这将使每个用户都有公平的获胜机会。 (60 个有 60% 的几率,20 个有 20% 的几率)。

但我真正需要的是,这是每个(或任何其他)功能的动态。

我的想法示例:

 chances.forEach(function(entry) {
     if (unknownValue > unkownValue2 && unknownValue3 < unknownValue4) {
         console.log('winner:', unknownUser);
     };
 });

所以基本上,如果机会数组的值为 50、100 和 20,则数字 100 获胜的机会必须比 50 高 2 倍,比 20 高 5 倍。

我对每个答案都很满意,请不要将此标记为老虎机百分比的重复项,这不是我需要的。

    //put chances and user object in the same object, in an array
    let userChances = [
        {userObj: 'user1', chance: 20},
        {userObj: 'user2', chance: 40},
        {userObj: 'user2', chance: 60}
    ];
    //we loop the items and turn the chance into a running total...
    for (let i = 0; i < userChances.length; i++) {
        if (i > 0) {
            userChances[i] = {
                userObj: userChances[i].userObj,
                chance: userChances[i].chance + userChances[i - 1].chance
            };
        }
    }
    //now data looks like this:
    //let userChances = [
    //    {userObj: user1, chance: 20},
    //    {userObj: user2, chance: 60},
    //    {userObj: user2, chance: 120}
    //];
    //we get the total available chance, which is now the value of the chance property of the last element in the array
    let totalChances = userChances[userChances.length - 1].chance;
    let yourChance = Math.floor((Math.random() * totalChances) + 1);
    //loop one last time
    for (let i= 0; i < userChances.length; i ++) {
        //if our number is less than the current user chance, we found the user! return it! otherwise, proceed to check the next user...
        if (yourChance <= userChances[i].chance) {
            console.log('Winner', userChances[i].userObj);
            break;
        }
        //please note that if this does not return or break the loop, every user after this will be logged as a winner too!
    }

将比率转换为百分比。显然,他们的总和应该占1,每个的值是val/total:

function transformRatiosToAccPercentages(ratios) {
  const total = ratios.reduce((sum, el) => sum += el, 0);
  let acc = 0;
  const accPercentages = ratios.map(rat => acc += rat / total);
  return accPercentages;
}

function chooseBiasedRandom(accPercentages) {
  const random = Math.random();
  const index = accPercentages.findIndex(acc => random < acc);
  return index;
}

// And that's how it can be used:

const users = {
  Alpha: 50,
  Bravo: 100,
  Charlie: 10
};

const userNames = Object.keys(users); 
const ratios = Object.values(users);

const attempts = 1E6;
const counter = Array(userNames.length).fill(0);

const accPercentages = transformRatiosToAccPercentages(ratios);

for (let i = 1; i <= attempts; i++) {
  const index = chooseBiasedRandom(accPercentages);
  counter[index]++;
  // console.log(`Attempt ${i}: user ${userNames[index]} wins!`);
}

console.log(counter);

您可以使用带有概率的数组,并对照随机值进行检查和计数。

此函数首先将return值设置为最后一个可能的索引,然后迭代直到其余随机值小于实际概率。另外,所有概率的总和应该等于一。

实现时,只需要获取用户数组索引的函数即可。

var users = ['user1', 'user2', 'user3'],
    probabilities = [0.2, 0.2, 0.6],
    selectedUser = users[getRandomIndexByProbability(probabilities)];

然后,代码显示了索引的分布。

function getRandomIndexByProbability(probabilities) {
    var r = Math.random(),
        index = probabilities.length - 1;

    probabilities.some(function (probability, i) {
        if (r < probability) {
            index = i;
            return true;
        }
        r -= probability;
    });
    return index;
}

var i,
    probabilities = [0.2, 0.2, 0.6],
    count = {},
    index;

probabilities.forEach(function (_, i) { count[i] = 0; });

for (i = 0; i < 1e6; i++) {
    index = getRandomIndexByProbability(probabilities);
    count[index]++;
}

console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }

作为概率解决方案的替代方案,您还可以创建一个包含重复用户的数组,以便他们的计数对应于关联的百分比。然后要求你的百分比是正整数,而不是分数。

像这样:

const users = [
  {name: "Jim",   pct: 20},
  {name: "Helen", pct: 20},
  {name: "Anna",  pct: 60}
];

const expanded = users.flatMap(user => Array(user.pct).fill(user));
const winner = expanded[Math.floor(Math.random() * expanded.length)];
console.log("winner: " + winner.name);

这些数字不一定是百分比。如果您需要更高的精度,只需使用更大的数字,加起来可能达到 1000 或 10000。