【问题标题】:pick item from list at random, each has different chance从列表中随机选择项目,每个项目都有不同的机会
【发布时间】:2017-11-10 06:59:33
【问题描述】:

我想写一个简单的函数,允许从列表中滚动随机项目,我用这个代码做到了:

this.resources = [false, 'nitrogen', 'silicon', 'cobalt', 'magnesium'];

this.assign_resource = function() {
    var index = tools.rnd(0, this.resources.length - 1);
    return this.resources[index];
};

但它玩得不好,所以我想把它改成不同的系统,允许一个项目列表(包括空的),它随机选择一个,但每个都有不同的机会(例如这个有 10 %,这个有 20%)。也许有人可以帮助我完成这种功能。

已编辑 -----

例如,这可能是新列表:

this.resources = [
    { type: 'empty', chance: 30 },
    { type: 'nitrogen', chance: 10 },
    { type: 'silicon', chance: 20 },
    { type: 'cobalt', chance: 30 },
    { type: 'magnesium', chance: 10 }
];

现在如何使用才能让它正常发生?

已编辑 2 -----

我正在尝试使用数学找出出色的编程解决方案,而不是简单地复制数组中的项目,this 主题中提供的答案只是解决问题的方法。

【问题讨论】:

  • 我不知道你在输入百分比时能做到多好,但你可以多次创建一个具有相同值的数组,例如[1,1,2] 会给1 一个 2/ 3 概率或基于随机索引号返回。
  • 你在哪里指定机会?像这样? this.resources = [false, ['nitrogen', 0.5], ['silicon', 0.2], ['cobalt', 0.2], ['magnesium', 0.1]]; 另外,false 是否适用于未分配资源的情况?
  • 添加到我的评论中,你会想要this
  • 我更新了我的问题,指出特定项目发生的可能性

标签: javascript arrays random


【解决方案1】:

我会通过让一个有机会成为结果的对象数组来解决它,总计1.0,然后选择一个介于 0 和 1 之间的随机数,然后遍历资源并检查是否将其添加到累计总数包括您的随机数。

var resources = [
  { resource: false, chance: 0.2 },
  { resource: 'nitrogen', chance: 0.1 },
  { resource: 'silicon', chance: 0.2 },
  { resource: 'cobalt', chance: 0.45 },
  { resource: 'mangesium', chance: 0.05 }    
];

function get_result(resouceList) {
  //get our random from 0 to 1
  var rnd = Math.random();
  
  //initialise our cumulative percentage
  var cumulativeChance = 0;
  //iterate over our resources
  for (var i = 0; i < resouceList.length; i++) {
    
    //include current resource
    cumulativeChance += resouceList[i].chance;
    
    if (rnd < cumulativeChance)
      return resouceList[i].resource;
  }
  
  return false;  
}

//test
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));

【讨论】:

    【解决方案2】:

    我会设置它,以便只有实际资源在您的数组中,如果随机滚动超出这些资源,则会发生“空”。

    this.resources = [
        { type: 'nitrogen', chance: 10 },
        { type: 'silicon', chance: 20 },
        { type: 'cobalt', chance: 30 },
        { type: 'magnesium', chance: 10 }
    ];
    
    this.assign_resource = function() {
      var rnd = Math.random();
      var acc = 0;
      for (var i=0, r; r = this.resources[i]; i++) {
        acc += r.chance / 100;
        if (rnd < acc) return r.type;
      }
      // rnd wasn't less than acc, so no resource was found
      return 'empty';
    }
    

    【讨论】:

    • 我会先随机订购阵列。因为从逻辑上讲,如果您将机会设置为 100,您会期望该元素返回,除非有另一个设置为 100。也许这就是为什么它需要计算的不是超过 100,而是超过机会的总和。
    • @s.alem 是的,如果您不使用百分比机会,那是一个有效的选择。我喜欢这种技术,因为这并不意味着你必须维护一个“空”元素,也意味着如果你向表中添加一个新项目,你不必手动调整所有其他机会,只要一切是一个合理的百分比,总数不超过 100。
    【解决方案3】:

    你可以这样做。

    多次创建具有相同值的数组使其被选中的机会更高。

    var resources = [{
        type: 'empty',
        chance: 30
      },
      {
        type: 'nitrogen',
        chance: 10
      },
      {
        type: 'silicon',
        chance: 20
      },
      {
        type: 'cobalt',
        chance: 30
      },
      {
        type: 'magnesium',
        chance: 10
      }
    ];
    
    
    function GetRandom(list) {
      var array = [];
      for (var i = 0; i < list.length; i++) {
        var item = list[i];
        var chance = item.chance / 10;
        for (var j = 0; j < chance; j++) {
          array.push(item.type);
        }
      }
      var idx = Math.floor(Math.random() * array.length);
      return array[idx];
    
    }
    
    console.log(GetRandom(resources))
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

      【解决方案4】:

      这就是我实施解决方案的方式。 第一步:积累所有可能的机会 第 2 步:选择与总机会成比例的随机值 第 3 步:遍历资源,查看随机值属于哪个部分。

      var resources = [
          { type: 'empty', chance: 30 },
          { type: 'nitrogen', chance: 10 },
          { type: 'silicon', chance: 20 },
          { type: 'cobalt', chance: 30 },
          { type: 'magnesium', chance: 10 }
      ];
      
      function solution(resources) {
        let chanceTotal = resources.reduce((acc, val) => { acc += val.chance ; return acc;}, 0);
        let randomVal = parseInt(Math.random() * chanceTotal);
        let chanceAcc = 0;
        let ans;
        resources.forEach(r => {
          chanceAcc += r.chance;
          if (chanceAcc > randomVal && !ans) {
            ans = r;
          }
        });
        return ans;
      }
      
      console.log(solution(resources));

      【讨论】:

        【解决方案5】:

        这是另一个实现。

        var res = [
            ["empty", 3],
            ["nitrogen", 1],
            ["silicon", 2],
            ["cobalt", 3],
            ["magnesium", 1]
        ];
        var flat = [];
        var item;
        for(var i = 0, l = res.length; i < l; i++) {
            item = Array(res[i][1]+1).join(i+",");
            item = item.substr(0, item.length-1);
            flat.push(item);
        }
        flat = flat.join(",").split(",");
        
        function get_random_item() {
            var ridx = Math.floor(Math.random() * (flat.length));
            return res[flat[ridx]][0];
        }
        
        var pick;
        for(var p = 0; p < 50; p++) {
            pick = get_random_item();
            console.log(p, pick);
        }

        【讨论】:

        • @Shafizadeh,我希望答案有足够的优点来支持投票;)(我知道,只是在开玩笑)。您可以在我的个人资料中找到我的联系信息。
        猜你喜欢
        • 1970-01-01
        • 2016-06-17
        • 2016-10-31
        • 2012-09-11
        • 1970-01-01
        • 1970-01-01
        • 2017-12-23
        • 2021-11-17
        • 1970-01-01
        相关资源
        最近更新 更多