2

I have a homework to do, that is:

Pick a random color, from yellow, blue and red, given the probability of: Yellow: 3/7 Blue: 1/7 Red: 3/7


I know that I could work this around by using something like: [yellow, yellow, yellow, blue, red, red, red] But I don't think this would be programatically good, since when I chance the probability, I would have to change the array.

So, I thought I could try something like a weight approach

let yellow_probability = 3/7
let blue_probability = 1/7
let red_probability = 3/7

const colors = ['yellow', 'blue', 'red']

function pickPosition(yellow_probability, blue_probability, red_probability){

    let yellow_weight = Math.random() * yellow_probability
    let blue_weight = Math.random() * blue_probability
    let red_weight = Math.random() * red_probability

    let weights = [yellow_weight, blue_weight, red_weight]

    let max_of_array = Math.max.apply(Math, weights);

    pickedColor = weights.indexOf(max_of_array)

    return pickedColor

}
pickedColorIndex = pickPosition(yellow_probability, blue_probability, red_probability)
pickedColor = colors[pickedColorIndex]
console.log(pickedColor)

I did a test:

let n=1000000; 
let yellow=0, blue=0, red=0; 
for (let i=0; i<n; i++) {

    pickedColorIndex = pickPosition(yellow_probability, blue_probability, red_probability)
    if (pickedColorIndex==0) yellow++
    else if (pickedColorIndex==1) blue++
    else red++;
}
console.log("yellow = " + yellow/n );
console.log("blue = " + blue/n );
console.log("red = " + red/n );

And I would expect this test to output something like:

Yellow = 0.43
Blue = 0.14
Red = 0.43

But I am getting:

Yellow = 0.48
Blue = 0.03
Red = 0.48

It is interesting to point out that the code works when the probabilities are equal (1/3, 1/3, 1/3) or something like (1/2, 1/2, 0)

Can anyone point out what I am doing wrong?

Victor Maricato
  • 672
  • 8
  • 25
  • Possible duplicate of [Generate random integers with probabilities](https://stackoverflow.com/questions/8877249/generate-random-integers-with-probabilities) and [Javascript Get Random result with probability for specific array](https://stackoverflow.com/questions/49164635) – adiga May 09 '19 at 14:15
  • Stackoverflow recommended these posts for me, and the https://stackoverflow.com/questions/49164635 has a quite similar question to mine, but its answers in general present the apporach I discarded. – Victor Maricato May 09 '19 at 14:35

2 Answers2

3

Instead of a single random value, you create as many as different items you have and later take the one with the max value.

This promotes values/items with a higher factor/probability.

Instead of this approach, you could take a single random value and take all probabilities into an array and check in which interval the random value is. Take this item.


EDIT: The code

function getRandomIndex(probabilities) {
    var random = Math.random(),
        i;
        
    for (i = 0; i < probabilities.length; i++) {
        if (random < probabilities[i]) return i;
        random -= probabilities[i];
    }
    return probabilites.length - 1;
}

var probabilities = [3 / 7, 1 / 7, 3 / 7],
    j = 1e6,
    count = [0, 0, 0];

while (j--) count[getRandomIndex(probabilities)]++;

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

This is similar to the approach mentioned in the duplicate. You create an array with ratios same as the probabilities. (Here I'm using 2 decimal places and adding ~100 items to the array. You could add multiply by a bigger number and have .toFixed(3) for better accuracy)

function getRandomWithProbability(array) {
  const filled = array.flatMap(([color, prob]) => {
    const length = prob.toFixed(2) * 100;
    return Array.from({ length }).fill(color)
  });

  const random = Math.floor(Math.random() * filled.length);
  return filled[random]
}

const arr = [["yellow", 3/7], ["blue", 1/7], ["red", 3/7]]

console.log(getRandomWithProbability(arr))
adiga
  • 34,372
  • 9
  • 61
  • 83