我将您的问题解释为:给定一个可能未归一化的概率数组(即,它们可能不会增加到 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);
}
这里的想法是将list of probabilities 变成更像cumulative distribution 的东西,其中索引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
*/
在我看来是合理的。好的,希望有帮助。祝你好运!