【问题标题】:All distributions of items between buckets桶之间的所有项目分布
【发布时间】:2017-11-22 00:03:43
【问题描述】:

我最近遇到了一个问题,我需要弄清楚如何将项目分配到存储桶中,但我需要找到所有分配它们的方法。

输入以整数数组的形式出现,告诉您每列可以容纳的最大值,并且数组中必须有 N 个项目。

例如:

maxItems = 3
maximums = [4,2,1] # The order of maximums DOES matter meaning
# This means that the results of maximums = [2,4,1] are different from maximums = [1,2,4]
outputs = [[3,0,0],[2,1,0],[1,1,1],[2,0,1],[0,2,1]] # results are in no particular order
# notice how the sum of each result is equal to maxItems and each value in each of the rows are less than the value inside of maximums

我试图在 javascript 中解决这个问题,但我无法弄清楚如何解决这个问题。我想从用尽可能多的数字填充第一列开始,然后开始向右移动,但是随着最大值数组变大,这种方法变得越来越不准确,我根本不知道如何处理它。

如果您还有其他问题,请随时询问您是否不了解问题。

我开始使用 javascript 的代码是

var all_combinations = function(N, maximums){
    var empty = maximums.map(function(){return 0;}); // create empty array size of maximums filled with 0s
    var s = 0;
    for (var i = 0; i < empty.length && s < N;){
        if (empty[i] >= maximums[i]){i++;continue;}
        empty[i]++;
        s++;
    } // fill the left side with as many items as possible

    // Then i would proceed to move one item at a time to the right side but some how i would need to do it for the whole array and this is where I get stuck.
};

我尝试搜索此问题,但我从未发现如何按照此处设置的方式进行操作。我试图找到类似的问题,但它们总是与此无关。也许我正在寻找错误的问题。如果有人可以链接一个有用的资源,那就太好了。

如果您有任何问题,请向他们提问。我会尽我所能回答。

【问题讨论】:

  • 我是否遗漏了一些信息以使这个问题成为可接受的问题?已经一票接近,甚至不到 5 分钟
  • 不,你不是。我个人不同意投票结束这个问题,这只是一个有点计算机科学的问题,而且关于 SO 的大多数问题都是简单的“请帮助修复我的程序”之类的交易,所以这个问题需要更长的时间来回答。不过不用担心,您来对地方了,我认为这是一个经过深思熟虑的问题。
  • 如果我理解你的话,你想在maximums 定义的边界内找到所有可能的排列,并按maxItemsN 定义的大小对它们进行分组。我说的对吗?
  • 是的,没错。
  • @Xiaoy312 我相信每个排列中所有值的总和必须等于maxItems / N

标签: javascript arrays algorithm


【解决方案1】:

您可以使用递归方法检查约束的所有部分。

它使用一个索引和一个临时数组来保持项目的数量。

开始时,索引为零,数组为空。通过调用fork,第一个退出选项被检查,这意味着约束被检查,如果大于或等于计数,则递归停止。

第二个退出选项是当项的总和达到想要的计数时,然后将临时数组推送到结果集并结束递归。

在所有其他情况下,fork 会再次调用

  • 相同的索引i 和索引处临时数组的递增值,或
  • 增加的索引和实际的临时数组。

function getCombination(max, count) {

    function fork(index, temp) {
        var sum = temp.reduce((a, b) => a + b, 0);
        if (max.some((a, i) => (temp[i] || 0) > a) || index === max.length || sum > count) {
            return;
        }
        if (sum === count) {
            result.push(temp);
            return;
        }
        fork(index, max.map((a, i) => (temp[i] || 0) + (i === index)));
        fork(index + 1, temp);
    }

    var result = [];
    fork(0, []);
    return result;
}

console.log(getCombination([4, 2, 1], 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }

一种迭代方法,之前检查总和加值是否小于或等于所需计数。

function getCombination(max, count) {

    function iter(index, sum, temp) {
        var i;
        if (count === sum) {
            result.push(temp);
            return;
        }
        for (i = max[index]; i >= 0; i--) {
            if (sum + i <= count) {
                iter(index + 1, sum + i, temp.concat(i));
            }
        }
    }

    var result = [];
    iter(0, 0, []);
    return result;
}

console.log(getCombination([4, 2, 1], 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 答案确实符合我的要求。但它是如何工作的?我不清楚其中的一些功能。我想我会在文档中弄清楚。
  • 可以用一些解释。 "Try this" answers have zero educational value 和非描述性的 var 名称也无助于理解算法。
  • 例如我怀疑它会尝试所有组合并过滤掉那些超出限制的组合,即大量不必要的工作。
【解决方案2】:

使用 ECMA 6 生成器的易于理解的递归解决方案:

对于每个 i,将 i 项目放入第一个插槽(如果它们适合),然后将其他项目分配给其余的。

function* bucket_distributions(capacities,nItems){
    if (capacities.length==1) {
      if (capacities[0] >= nItems)
        yield [nItems];
    }
    else if (capacities.length>1) {
      for (var i=Math.min(capacities[0],nItems);i>=0;i--) {
        for (subdist of
            bucket_distributions(capacities.slice(1),nItems-i))
          yield [i].concat(subdist);
      }
    }
}

console.log(Array.from(bucket_distributions([4,2,1],3)))

【讨论】:

  • 这是一个非常简洁的解决方案。我还没有想到 javascript for..of 还 XD
  • 我最终使用了您的解决方案,因为它很容易转换为 python。
  • @ahitt6345 由于我有 Python 背景,我实际上将其转换为相反的答案:-)
  • 你不应该嵌套 if 语句。
  • @Arrow else if 仅与外部if 相关,与内部无关,所以我没有其他(明显的)选择,只能嵌套它们。这个想法是让生成器在其他情况下不返回任何内容,从而使 for..of 循环成为无操作。
【解决方案3】:

这里有一个带有交互式演示的注释良好的迭代解决方案:

// reducer function for calculating sum of array
function sum(prev, next) {
  return prev + next;
}

// returns the contextual constraints of a bucket
function bucketMinMax(maxItems, otherItems, bucketMax) {
  return {
    // minimum values in bucket to meet maxItems
    min: Math.max(0, maxItems - otherItems),
    // maximum values in bucket to meet maxItems
    max: Math.min(maxItems, bucketMax),
  };
}

// takes an incomplete combination and expands it with the next bucket 
// starting from the left
function expandCombination(maxItems, maximums, combinations) {
  // get next combo group to expand
  var comboGroup = combinations.shift();
  // get index of expansion bucket
  var index = comboGroup.length;
  // calculate maximum possible otherItems
  var otherItems = maximums.slice(index + 1).reduce(sum, 0);

  // removes already used spaces from maxItems in combination group being expanded
  maxItems -= comboGroup.reduce(sum, 0);

  // get constraints for expansion bucket
  var {min, max} = bucketMinMax(maxItems, otherItems, maximums[index]);

  for (var i = min; i <= max; i++) {
    // add combo group expansions to output
    combinations.push(comboGroup.concat([i]));
  }
}

// main function
function allCombinations(maxItems, maximums) {
  // will eventually contain all combinations
  var output = [[]];

  // loops through array of combinations, expanding each one iteratively
  while (output.length > 0 && output[0].length < maximums.length) {
    // takes incomplete combination group and expands it with possible values
    // for next bucket starting from the left
    expandCombination(maxItems, maximums, output);
  }

  return output;
}

document.addEventListener('change', () => {
  var maxes = JSON.parse(maximums.value);
  var items = JSON.parse(maxItems.value);
  
  console.log(JSON.stringify(allCombinations(items, maxes)));
});

document.dispatchEvent(new Event('change'));
<label>maxItems
<input id="maxItems" value="3">
</label>
<label>maximums
<input id="maximums" value="[4,2,1]">
</label>

【讨论】:

    猜你喜欢
    • 2017-02-04
    • 2019-12-31
    • 2017-05-28
    • 1970-01-01
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多