0

This is part of a bigger problem I try to solve in an exercise. It looks like this:

  • x is 10 times more likely to appear than y.

  • z appears 2x less often than y.

I solved this by calculating a single unit like this:

const x = 100;
const y = 10;
const z = 5;
const unit = 100 / (x + y + z);

unit equals 0.87 So when I do (0.87) + (0.87 * 10) + (0.87 * 5) I get 100%(almost)

Then I generate a random number between 0 and 1.

const randomNumber = Math.random();
function getValue() {
    if (randomNumber <= 0.87) {
        console.log('x');
    } else if (randomNumber > 0.87 && randomNumber < 95.7) {
        console.log('y');
    } else console.log('z');
}

getValue();

If value<0.87 then I log out x, if value < 0.87+(0.087*10) I log y etc

Can anyone recommend a more logical and elegant way than this?

Király Roland
  • 141
  • 2
  • 9

2 Answers2

0

Your way looks clean for me except the fact that randomNumber > 0.87 is redundant.

if you store the value x, y and z in an array, you can probably write some cleaner code for example:

let prob = [100, 10, 5];
let sum = prob.reduce((a, b) => a + b, 0);
let normalizedProb = prob.map(p => p / sum);
let cummulativeProb = normalizedProb.map((cummulative => p => cummulative += p)(0));
for (let i = 0; i <= 50; i++) {
  let r = Math.random();
  console.log(cummulativeProb.filter(p => r >= p).length);
}

Also, you may want to read this post for faster implementation (in python though). However, the code will be more complicated for sure.

TYeung
  • 2,579
  • 2
  • 15
  • 30
0

Since the weights are small integers, you can duplicate the x, y and z in an array, and just pick one random cell of the array:

let choices = "zyyxxxxxxxxxxxxxxxxxxxx";
console.log(choices[Math.floor(Math.random() * 23)]);
Here the magic number 23 is the number of choices, 1+2+20; and Math.floor(Math.random() * 23) is a random integer uniformly at random in range [0, 22] (both bounds included). See also:
Stef
  • 13,242
  • 2
  • 17
  • 28