0

I'm trying to find a way to optimize this

Let's say i have this set of chances:

let chances = {
    a: 0.80, 
    b: 0.15, 
    c: 0.05
}

and I want to run these chances X times and store how many of each that was obtained

of course the simplest way is to do an iteration with those chances, for example like this:

let results = { a: 0, b: 0, c: 0 };
for (let i = 0; i < iterations; i++) {
    let rnd = Math.random();
    let chanceSum = 0;
    for (let c in chances) {
        if (rnd <= chances[c] + chanceSum) {
            results[c]++;
        }
        else chanceSum += chances[c];
    }
}

then results would have how many of each was obtained

the problem comes with a BIG number of iterations: clearly doing it like this for thousands of millions of times cause massive performance issues

of course, multiplying the chance with the amount of iterations and then rounding the number is a quick way to solve it, but of course it's not truly random

my idea is to obtain the results without actually doing the iterations, how could i achieve this? My best idea is using the normal distribution somehow but i'm not sure how to apply it

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
loonukir
  • 25
  • 2
  • 1
    keyword: [multinomial distribution](https://en.wikipedia.org/wiki/Multinomial_distribution). you could look at how numpy implements it – Ry- Feb 27 '23 at 23:33
  • Found the solution, i used [this](https://stackoverflow.com/questions/25582882/javascript-math-random-normal-distribution-gaussian-bell-curve) to generate the values! – loonukir Feb 28 '23 at 01:08

1 Answers1

-3

I prefer to solve this with two ways to raise the performance:

  1. Loop the iterations with Array forEach.
  2. Loop the iterations with While Loop.

Both ways will generate a number based on the number of each chance.

let chances = {
    a: 0.80, 
    b: 0.15, 
    c: 0.05
}
let iterations = Math.pow(10,6); // 10^6

// current solution
console.time('current solution performance');
let results = { a: 0, b: 0, c: 0 };
for (let i = 0; i < iterations; i++) {
    let rnd = Math.random();
    let chanceSum = 0;
    for (let c in chances) {
        if (rnd <= chances[c] + chanceSum) {
            results[c]++;
        }
        else chanceSum += chances[c];
    }
}
console.log(results);
console.timeEnd('current solution performance');

// first proposed solution : Use Array forEach
console.time('first proposed solution performance');
results = { a: 0, b: 0, c: 0 };
for (let c in chances) {
    Array(iterations).fill(0).forEach((a) => {
      if (Math.random() <= chances[c]) results[c]++; 
    });
}
console.log(results);
console.timeEnd('first proposed solution performance');

// second proposed solution : Use While Loop
console.time('second proposed solution performance');
results = { a: 0, b: 0, c: 0 };
for (let c in chances) {
    let counter = 0;
    while(counter < iterations) {
      if (Math.random() <= chances[c]) results[c]++; 
      counter++;
    }
}
console.log(results);
console.timeEnd('second proposed solution performance');
Jordy
  • 1,802
  • 2
  • 6
  • 25
  • 1
    Those don’t do the same thing as the original code (or as what the original code probably intended), and there’s no reason to do `Array(iterations).fill(0).forEach` to loop over a range to or expect it to be faster. – Ry- Feb 28 '23 at 04:14