我想返回一个数组,该数组具有一组根据自定义频率随机分布的唯一元素。我的真实世界用例是基于对图像流行程度的定性加权重复轮播图像。
E.g。假设我有5个带权重的元素:
A,20% B,50% C,80% D,10%
我想写一个函数,给定一个长度,试图近似一个序列,使得C出现的频率是D的八倍; D的出现次数比B少5次; A的出现次数比C少三倍。
答案 0 :(得分:7)
C的出现次数是D的8倍; D的出现次数比B少5次; A的出现次数比C少三倍。
您可以使用元素的加权数组执行此操作:
var elems = ["A", "B", "C", "D"];
var weights = [2, 5, 8, 1]; // weight of each element above
var totalWeight = weights.reduce(add, 0); // get total weight (in this case, 16)
function add(a, b) { return a + b; } // helper function
var weighedElems = [];
var currentElem = 0;
while (currentElem < elems.length) {
for (i = 0; i < weights[currentElem]; i++)
weighedElems[weighedElems.length] = elems[currentElem];
currentElem++;
}
console.log(weighedElems);
这会生成一个像
这样的数组[&#34; A&#34;,&#34; A&#34;,&#34; B&#34;,&#34; B&#34;,&#34; B&#34;,& #34; B&#34;,&#34; B&#34;,&#34; C&#34;,&#34; C&#34;,&#34; C&#34;,&#34; C&# 34;,&#34; C&#34;,&#34; C&#34;,&#34; C&#34;,&#34; C&#34;,&#34; D&#34;]
所以你可以随意选择
var rnd = Math.floor(Math.random() * totalWeight);
console.log(weighedElems[rnd]);
资源:
答案 1 :(得分:3)
假设您将分配编号作为对象数组,如下所示:
var items = [
{item: "A", weight: 20},
{item: "B", weight: 50},
{item: "C", weight: 80},
{item: "D", weight: 10}
];
这消除了您的权重加起来为100%的任何假设 - 它们可能是点击次数,投票或您喜欢的任何其他值。然后你可以这样做:
function weightedSelect(items) {
// Get the total, and make the weights cummulative
var total = items.reduce(function(sum, item){
item.weight = item.weight + sum;
return item.weight;
},0);
var r = Math.random() * total;
// Can't use .forEach() here because we want early termination
for (var i = 0; i < items.length; i++) {
if (r < items[i].weight)
return items[i].item;
}
}
我不确定这与其他效率实现相比如何,但它很简洁。
答案 2 :(得分:2)
扩展a_gupta的答案:
function pick_bin(binProbabilities){ // e.g. [0.1, 0.3, 0.3, 0.3]
var cumulative = []; // e.g. [0.1, 0.4, 0.7, 1]
var accumulator = 0;
// Iterating over an array with forEach:
binProbabilities.forEach(function(item, index){
var prob = Number(item);
accumulator += prob;
cumulative[index] = accumulator;
})
if(accumulator !== 1){
throw new Error('Sum of binProbabilities must equal 1')
}
var n = binProbabilities.length;
var rFloat = Math.random();
// Iterating over an array with for:
for(var i=0; i<n; i++){
var pcI = cumulative[i]; // cumulative probability of this index
if(pcI >= rFloat){ // Found the first bin fitting the random number
console.log(i);
return i;
}
}
}
pick_bin([1]); // returns 0 every time
pick_bin([.5, .5]) // returns 0,1 50/50
pick_bin([0.1, 0.3, 0.3, 0.3])
跟进你的&gt; 100%示例,您可以重新计算权重,使它们等于1(对于有效概率)
Desired weightings: 20% 50% 80% 10%
Sum these weights: 20 + 50 + 80 + 10 = 160
Divide each by the sum: 2/16, 5/16, 8/16, 1/16
Now they sum to 1
答案 3 :(得分:1)
有一个非常简单的解决方案。 random()方法返回0到1之间的数字。
例如,如果返回的数字是&gt; 0.2,然后输出C(即80%几率)。