存储桶之间的所有项目分布

时间:2017-06-19 17:04:50

标签: javascript arrays algorithm

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

输入以整数数组形式出现,告诉您每列可以容纳的最大值,并且数组中必须有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.
};

我试着搜索这个问题,但我从来没有发现如何按照这里设置的方式进行搜索。我试图找到类似的问题,但他们总是与此无关。也许我正在寻找错误的问题。如果有人可以链接一个很有用的有用资源。

如果您有任何疑问,请咨询他们。我会尽我所能回答。

3 个答案:

答案 0 :(得分:4)

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

它适用于索引和临时数组,用于保存项目的计数。

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

第二个退出选项是当项目总和达到所需计数时,临时数组将被推送到结果集并且递归结束。

在所有其他情况下,使用

再次调用fork
  • 相同的索引i以及索引处的临时数组的递增值,或
  • 递增索引和实际临时数组。

&#13;
&#13;
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));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

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

&#13;
&#13;
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));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 1 :(得分:1)

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

对于每个i,如果它们适合,则将i个项目放入第一个广告位,然后将其他广告分配给其他广告。

&#13;
&#13;
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)))
&#13;
&#13;
&#13;

答案 2 :(得分:0)

这是一个评论很好的迭代解决方案,带有一个交互式演示:

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