Я интерпретирую ваш вопрос следующим образом: с учетом массива вероятностей, возможно, не нормированных (то есть они могут не прибавить к 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
*/
Выглядит разумно для меня. Хорошо, надеюсь, это поможет. Удачи!