Случайное число с процентами - PullRequest
0 голосов
/ 07 июня 2018

У меня есть это:

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 должен быть в 2 раза больше, чем 50, и в 5 раз выше, чем 20.

Я рад за каждый ответ и, пожалуйста, не отмечайте это как дубликат для процентов игровых автоматов, это НЕ то, что мне нужно.

Ответы [ 4 ]

0 голосов
/ 07 июня 2018

В качестве альтернативы вероятностным решениям вы также можете создать массив с дублирующимися пользователями, чтобы их количество соответствовало проценту.Затем требуется, чтобы ваши проценты были положительными целыми числами, а не дробями.

Примерно так:

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.

0 голосов
/ 07 июня 2018

Преобразуйте отношения в проценты.Видимо, их сумма должна составлять 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);
0 голосов
/ 07 июня 2018

Вы можете использовать массив с вероятностями, а также проверять и считать случайное значение.

Эта функция сначала устанавливает возвращаемое значение в последний возможный индекс, а затем перебирает до тех пор, покаслучайное значение меньше фактической вероятности .Также сумма всех вероятностей должна быть равна единице.

Для реализации вам просто нужно взять функцию для получения индекса для массива пользователей.

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; }
0 голосов
/ 07 июня 2018

    //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!
    }
...