我试图找出一种方法从数组中选择一个随机对象,基于它的权重属性。这是一个示例数组:
var item = [{
verDiv: 'div-gpt-ad-1553003087342-0',
verKv: 'version1',
verSize: [300, 250],
weight: 10 //should be chosen in 10% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-1',
verKv: 'version2',
verSize: [300, 250],
weight: 25 //should be chosen in 25% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-2',
verKv: 'version3',
verSize: [160, 600],
weight: 25 //should be chosen in 25% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-3',
verKv: 'version4',
verSize: [728, 90],
weight: 40 //should be chosen in 40% of cases
}];
我想要做的是通过使用函数选择四个对象中的一个,该函数考虑了它们的权重属性,因此我可以在需要时调用其他属性。
console.log([item[weightFunction()].verDiv]);
console.log([item[weightFunction()].verKv]);
console.log([item[weightFunction()].verSize]);
编辑:以上只是一个建议,我相信有更好的方法来做到这一点。
假设所有权重之和恰好为100(否则计算它并用作cumul
初始值和随机乘数:
function weightFunction(items) {
var cumul = 100
var random = Math.floor(Math.random() * 100)
for(var i = 0; i < items.length; i++) {
cumul -= items[i].weight
if (random >= cumul) {
return items[i]
}
}
}
您可以使用所有权重对权重数组进行闭包,并返回一个函数,该函数根据所有权重的总和得到索引。
function getWeightedDistribution(weights) {
return function () {
var random = Math.random(),
sum = 0;
return weights.findIndex(w => random < (sum += w));
};
}
var weights = [0.1, 0.25, 0.25, 0.4], // all values have to sum to 1
i;
weightFunction = getWeightedDistribution(weights),
counts = [0, 0, 0, 0];
for (i = 0; i < 1e6; i++) counts[weightFunction()]++;
console.log(...counts);
与您的代码一起
function getWeightedDistribution(weights) { // weights sums up to 1
return function () {
var random = Math.random(),
sum = 0;
return weights.findIndex(w => random < (sum += w));
};
}
var item = [{ verDiv: 'div-gpt-ad-1553003087342-0', verKv: 'version1', verSize: [300, 250], weight: 10 }, { verDiv: 'div-gpt-ad-1553003087342-1', verKv: 'version2', verSize: [300, 250], weight: 25 }, { verDiv: 'div-gpt-ad-1553003087342-2', verKv: 'version3', verSize: [160, 600], weight: 25 }, { verDiv: 'div-gpt-ad-1553003087342-3', verKv: 'version4', verSize: [728, 90], weight: 40 }],
weightFunction = getWeightedDistribution(item.map(({ weight }) => weight / 100));
console.log(item[weightFunction()].verDiv);
console.log(item[weightFunction()].verKv);
console.log(item[weightFunction()].verSize);
这是一个更抽象的问题方法,允许总权重大于100,您可以定义如何检索每个元素的权重属性。
这种方法的工作方式是为每个值创建一个范围映射,并返回其范围“捕获”随机数的第一个元素。
var item = [{
verDiv: 'div-gpt-ad-1553003087342-0',
verKv: 'version1',
verSize: [300, 250],
weight: 10 //should be chosen in 10% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-1',
verKv: 'version2',
verSize: [300, 250],
weight: 25 //should be chosen in 25% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-2',
verKv: 'version3',
verSize: [160, 600],
weight: 25 //should be chosen in 25% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-3',
verKv: 'version4',
verSize: [728, 90],
weight: 40 //should be chosen in 40% of cases
}
];
function weightFunction(list, getWeight) {
var total = 0; // Faster than doing another loop with reduce
var map = list.reduce(function(result, value, index) {
var currentWeight = getWeight(value, index);
total += currentWeight;
result[total] = value;
return result;
}, {});
var random = Math.random() * total;
return map[Object.keys(map).find(function(index) {
return index >= random;
})];
}
console.log(weightFunction(item, x => x.weight).verDiv);
console.log(weightFunction(item, x => x.weight).verKv);
console.log(weightFunction(item, x => x.weight).verSize);
stat_map
的数组,最终将具有sum of all weights
的大小0
(第一个项目的索引),25个1
(第二个项目的索引),25个2
(第三个项目的索引),40个3
(第四个项目的索引)const item = [{
verDiv: 'div-gpt-ad-1553003087342-0',
verKv: 'version1',
verSize: [300, 250],
weight: 10 //should be chosen in 10% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-1',
verKv: 'version2',
verSize: [300, 250],
weight: 25 //should be chosen in 25% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-2',
verKv: 'version3',
verSize: [160, 600],
weight: 25 //should be chosen in 25% of cases
},
{
verDiv: 'div-gpt-ad-1553003087342-3',
verKv: 'version4',
verSize: [728, 90],
weight: 40 //should be chosen in 40% of cases
}];
const randomItem = (item) => {
const stat_map = []
item.map((v, i) => stat_map.push(...new Array(v.weight).fill(i)))
const rand = Math.floor(Math.random() * stat_map.length)
return item[stat_map[rand]]
}
console.log(randomItem(item))