Как рассчитать вероятность выхода из массива в Angular? - PullRequest
0 голосов
/ 28 марта 2019

У меня есть массив с 10 значениями. Каждое из этих значений представляет процент, чтобы выиграть что-то. Например, строка 1 имеет шанс 50% выиграть что-то. Что такое элегантный способ реализовать это?

Я заполняю массив значениями источника данных:

    for (let i = 0; i < 10; i++) {
      this.power[i] = this.dataSource.data[i].power;
    }

Массив мощности представляет вероятности победы каждого человека. Ряд 1: 50% Ряд 2: 10%, ... То, что я хочу в качестве вывода, - это выигрышная строка после вычисления вероятности. Так, например, строка 1 имеет наибольшую вероятность выиграть -> более высокую вероятность того, что я получу 1 в качестве результата.

enter image description here

1 Ответ

1 голос
/ 28 марта 2019

Я интерпретирую ваш вопрос следующим образом: с учетом массива вероятностей, возможно, не нормированных (то есть они могут не прибавить к 1,0), создать случайный индекс в массиве, вероятность появления которого пропорциональна вероятности при этот индекс.

Это означает, что вам нужен генератор случайных чисел где-то. По умолчанию я буду использовать Math.random(), но вы можете заменить его на любой генератор случайных чисел с тем же контрактом: функция без аргументов, которая выдает число, равномерно распределенное между 0 и 1.

Вот как бы я это сделал:

// probabilities: array of (possibly not normalized) probabilities like [4,3,2,1]
// randomGenerator: function producing a random number, uniform distribution from 0≤x<1
function randomIndex(
  probabilities: number[], 
  randomGenerator: () => number = Math.random
): number {

    // get the cumulative distribution function
    let acc: number = 0;
    const cdf = probabilities
        .map(v => acc += v) // running total [4,7,9,10]
        .map(v => v / acc); // normalize to max 1 [0.4,0.7,0.9,1]

    // pick a random number between 0 and 1
    const randomNumber = randomGenerator();

    // find the first index of cdf where it exceeds randomNumber
    // (findIndex() is in ES2015+)
    return cdf.findIndex(p => randomNumber < p);
}

Идея здесь состоит в том, чтобы превратить список вероятностей в нечто, более похожее на совокупное распределение , где элемент в индексе i представляет вероятность того, что случайно выбранный индекс будет меньше или равен i. Итак, если ваши вероятности равны [4, 3, 2, 1], что нормализуется до [0.4, 0.3, 0.2, 0.1], то кумулятивное распределение равно [0.4, 0.7, 0.9, 1.0]. Затем вам нужно найти первый элемент совокупного распределения, который превышает некоторое случайное число от 0 до 1. Используя приведенный выше пример [0.4, 0.7, 0.9, 1.0], если случайное число находится в диапазоне от 0 до 0,4, индекс будет 0. Если оно составляет от 0,4 до 0,7, то индекс будет 1. Если оно находится между 0,7 и 0,9, то индекс будет 2. и если он находится между 0,9 и 1,0, то индекс будет 3. Вы можете видеть, как вероятность каждого создаваемого индекса пропорциональна значению в исходном списке [4, 3, 2, 1].

Давайте попробуем это на вашем примере, выполнив randomIndex() десять тысяч раз и сравнив частоту попаданий для каждого индекса с вероятностью:

const probabilities = [50, 10, 10, 10, 10, 2, 2, 2, 2, 2];
let hits = probabilities.map(x => 0);
const numAttempts = 10000;
for (let k = 0; k < numAttempts; k++) {
    hits[randomIndex(probabilities)]++;
}
for (let i = 0; i < probabilities.length; i++) {
    console.log("" + i + ": prob=" + probabilities[i] + 
      ", freq=" + (100 * hits[i] / numAttempts).toFixed(1));
}

/*
Example of console.log output:
0: prob=50, frequency: 49.4
1: prob=10, freq=10.3
2: prob=10, freq=10.4
3: prob=10, freq=9.9
4: prob=10, freq=10.3
5: prob=2, freq=2.2
6: prob=2, freq=1.8
7: prob=2, freq=1.8
8: prob=2, freq=1.9
9: prob=2, freq=2.1
*/

Выглядит разумно для меня. Хорошо, надеюсь, это поможет. Удачи!

...