【问题标题】:Choosing Random Weighted Object from an Array从数组中选择随机加权对象
【发布时间】:2019-03-20 09:05:01
【问题描述】:

我正在尝试找出一种方法来根据它的权重属性从数组中选择一个随机对象。这是一个示例数组:

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]);

编辑:以上只是一个建议,我相信还有更好的方法。

【问题讨论】:

  • weightFunction(item) 会更有意义

标签: javascript arrays object weighted


【解决方案1】:

假设所有权重之和正好是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]
    }
  }
}

【讨论】:

  • 非常感谢。像魅力一样工作:)
  • 编辑:当我开始调用不同的属性 weightFunction(item).verDiv weightFunction(item).verKv 它返回来自不同对象的值:C
  • weightFunction 每次调用都会返回随机项。 weightFunction(item).verDivweightFunction(item).verKv 是两个电话。您必须像 var choosen = weightFunction(item) 一样存储它并将其用作 choosen.verDivchoosen.verKv
  • 是的,正如 undefined 所说。如果每次都返回相同的,则它不是随机的;)
【解决方案2】:

您可以对具有所有权重的权重数组进行闭包并返回一个函数,该函数根据所有权重的总和获取索引。

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);

【讨论】:

    【解决方案3】:

    这是解决问题的一种更抽象的方法,它允许总权重超过 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);

    【讨论】:

      【解决方案4】:
      • 定义一个名为 stat_map 的数组,其大小最终为 sum of all weights
      • 使用项目索引填充 stat_map,以便 stat_map 包含与其权重一样多的项目索引。
      • 现在 stat_map 包含 10 0(第一项索引),25 1(第二项索引),25 2(第三项索引),40 3(第四项索引)
      • 如果你从 stat_map 中选择随机元素,它将是所选项目的索引,很明显,项目将根据其重量进行选择。

      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))

      【讨论】:

        猜你喜欢
        • 2011-05-26
        • 2021-03-17
        • 2022-01-08
        • 2015-07-05
        • 2010-09-08
        相关资源
        最近更新 更多