29

I'm a bit confused about how to generate integer values with probabilities.

As an example, I have four integers with their probability values: 1|0.4, 2|0.3, 3|0.2, 4|0.1

How can I generate these four numbers taking into account their probabilities?

halfer
  • 19,824
  • 17
  • 99
  • 186
Headshota
  • 21,021
  • 11
  • 61
  • 82
  • If you want to know lots of technical details about how to do this quickly, this is a great resource. For making a weighted choice among 4 choices, it's totally unneeded though. http://www.keithschwarz.com/darts-dice-coins/ – Rob Neuhaus Jan 16 '12 at 17:11
  • See also http://stackoverflow.com/questions/3094873 – Lucas Jan 20 '12 at 16:50

7 Answers7

42

Here's a useful trick :-)

function randomWithProbability() {
  var notRandomNumbers = [1, 1, 1, 1, 2, 2, 2, 3, 3, 4];
  var idx = Math.floor(Math.random() * notRandomNumbers.length);
  return notRandomNumbers[idx];
}
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
40

A simple naive approach can be:

function getRandom(){
  var num=Math.random();
  if(num < 0.3) return 1;  //probability 0.3
  else if(num < 0.6) return 2; // probability 0.3
  else if(num < 0.9) return 3; //probability 0.3
  else return 4;  //probability 0.1
}
bhups
  • 14,345
  • 8
  • 49
  • 57
21

More flexible solution based on @bhups answer. This uses the array of probability values (weights). The sum of 'weights' elements should equal 1.

var weights = [0.3, 0.3, 0.3, 0.1]; // probabilities
var results = [1, 2, 3, 4]; // values to return

function getRandom () {
    var num = Math.random(),
        s = 0,
        lastIndex = weights.length - 1;

    for (var i = 0; i < lastIndex; ++i) {
        s += weights[i];
        if (num < s) {
            return results[i];
        }
    }

    return results[lastIndex];
};
Rom098
  • 2,445
  • 4
  • 35
  • 52
5

I suggest to use a continuous check of the probability and the rest of the random number.

This function sets first the return value to the last possible index and iterates until the rest of the random value is smaller than the actual probability.

The probabilities have to sum to one.

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.4, 0.3, 0.2, 0.09, 0.01 ],
    count = {},
    index;

probabilities.forEach(function (a) { count[a] = 0; });

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

console.log(count);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
3

This is the solution i find the most flexible, for picking within any set of object with probabilities:

// set of object with probabilities:
const set = {1:0.4,2:0.3,3:0.2,4:0.1};

// get probabilities sum:
var sum = 0;
for(let j in set){
    sum += set[j];
}

// choose random integers:
console.log(pick_random());

function pick_random(){
    var pick = Math.random()*sum;
    for(let j in set){
        pick -= set[j];
        if(pick <= 0){
            return j;
        }
    }
}
François Huppé
  • 2,006
  • 1
  • 6
  • 15
0
let cases = {
  10 : 60,// 0-10 : 60  => 10%
  90 : 10,// 10-90 : 10  => 80%
  100 : 70,// 90-100 : 70 => 10%
};
function randomInt(){
  let random = Math.floor(Math.random() * 100);
  for(let prob in cases){
    if(prob>=random){
      return cases[prob];
    }
  }
}
console.log(randomInt())

Rajnish Anand
  • 85
  • 1
  • 6
0

Some variation on Rom098 answer to make it a bit more flexible. Added weights as array of unit instead.

function randomWithProbability(outcomes, weights){
    if(!weights){
        weights=Array(outcomes.length).fill(1);
    }
    let totalWeight=weights.reduce((prev, curr)=>prev+=curr);
    const num=Math.random();
    let sum=0, lastIndex=weights.length-1;
    for(let i=0; i<=lastIndex; i++){
        sum+=weights[i]/totalWeight;
        if(num<sum) return outcomes[i];
    }
    return outcomes[lastIndex];
}

for(let i=0; i<20; i++){
  console.log(randomWithProbability([true, false], [10,1]));
}