如何在阵列中找到尽可能低的键组合

时间:2016-10-25 02:15:55

标签: javascript arrays

我有一个总是有6个键的数组:var ary = [5,10,28,50,56,280]。 我有一个定义为limit的变量我想检查它。

我想找到此数组中limit以上的最低可能组合或键总和。我们称之为result

我正努力解决的一些限制因素:

1 result可以是单个密钥本身: 例如,如果limit = 0最低可能的组合或键的总和应该默认为它可以找到的最低键ary[ 0 ]。在这种情况下或5

2 result可以是任意键的组合: 如果limit = 11result将= ary[ 0 ] + ary[ 1 ](5 + 10)。这将是15

3 最后,result可以超过ary的最大总和: result = 5 + 10 + 28 + 50 + 56 + 280; // equals 429在这种情况下,限制为430

注意:任何密钥都可以在超过result之前重复多次。

我的进展尝试:



function add(a, b) { //add all keys in array
    return a + b;
}

var combine = function(a, min) { //find all combinations of array
    var fn = function(n, src, got, all) {
        if (n == 0) {
            if (got.length > 0) {
                all[all.length] = got;
            }
            return;
        }
        for (var j = 0; j < src.length; j++) {
            fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all);
        }
        return;
    }
    var all = [];
    for (var i = min; i < a.length; i++) {
        fn(i, a, [], all);
    }
    all.push(a);
    return all;
}

var subsets = combine([5,10,28,50,56,280], 1);
var limit = 11;

for( var i = 0; i < subsets.length; i++ ){
  document.write('combination: ' + subsets[ i ] + ' sum: ' + subsets[ i ].reduce(add, 0) + '<br>');
}
&#13;
&#13;
&#13;

4 个答案:

答案 0 :(得分:1)

认为这是有效的。你能提供更多的测试用例吗?答案中的预期429 > 434 应为429 > 430,对吗?

var findLowest = function(arr, limit) {
    if (limit < arr[0]) return arr[0];

    // If there's a number in our arr that's higher than the limit,
    // this is the initial benchmark
    var bestCandidate = Infinity,
        maxIndex = arr.findIndex(v => v > limit);

    if (maxIndex !== -1) {
        bestCandidate = arr[maxIndex] - limit;
        arr = arr.slice(0, maxIndex);
    }

    // Calculate remainders, call this method recursively for all remainders
    var diffs = arr.map(v => limit % v);

    var finalDiffs = diffs.map(v => findLowest(arr, v) - v);
    
    return limit + Math.min(bestCandidate, finalDiffs.sort()[0]);
    
};

var prepareData = function(arr) {
    return arr
        // Remove duplicates of nrs in array
        .reduce((res, v) => {
            var needed = !res.length || res.every(r => v % r);
            return needed ? res.concat(v) : res;
        }, [])

        // Generate each combination (length * length - 1)
        .reduce((res, v, i, all) => {
            return res.concat(v).concat(all.slice(i + 1).map(v2 => v + v2));
        }, [])
        
        // Sort lowest first
        .sort((a, b) => a - b);
}

var data = [5,10,28,50,56,280];
var testCases = [
    [data, 5, 10],
    [data, 11, 15],
    [data, 25, 28],
    [data, 50, 53],
    [data, 55, 56],
    [data, 1, 5],
    [data, 281, 282], // 9 * 28 + 5 * 6
    [[50, 60, 110], 161, 170]
];

testCases.forEach(tc => {
    var prep = prepareData(tc[0]);
    var result = findLowest(prep, tc[1]);

    if (result !== tc[2]) {
      console.log("Limit: ", tc[1]);
      console.log("Expected: ", tc[2]);
      console.log("Result: ", result);
      console.log("----");
    }
});

注意:我当前的尝试是递归的,这可能不太理想......如果它通过了所有测试,我们可以将其重写为不递归。

答案 1 :(得分:0)

所以这是一个使用嵌套forEach循环的解决方案:

var ary = [5, 10, 28, 50, 56, 280];

function lowestSum(limit) {
  var thisArg = {sum: 0};

  ary.forEach(function(ele1, index1) {
    if (ele1 > limit && (ele1 < this.sum || this.sum === 0))
      this.sum = ele1;
    ary.forEach(function(ele2, index2) {
      if (index1 !== index2 && ele1 + ele2 > limit 
          && (ele1 + ele2 < this.sum || this.sum === 0)) {
        this.sum = ele1 + ele2;
      }
    }, this);
  }, thisArg);
  return thisArg.sum;
}

console.log(lowestSum(6));
console.log(lowestSum(11));
console.log(lowestSum(27));
console.log(lowestSum(28));

答案 2 :(得分:0)

如果您有一个小阵列,因此不太关心效率,那么您可以执行以下操作。

  1. 生成候选对
  2. 按其总和
  3. 对候选对进行排序
  4. 遍历已排序的对,直到找到超出限制的对

    var limit = 39;
    var ary = [5,10,28,50,56,280];
    
    function getLowestCombinationOverLimit(ary, limit) {
         var i, j, p;
         var pairs = [];
         for(i = 0; i < ary.length; i++) {
             for(j = i + 1; j < ary.length; j++) {
                 pairs.push({x:ary[i], y:ary[j]});
             }
         }
         pairs.sort(function (a, b) {
             return (a.x + a.y) - (b.x + b.y);
         });
         for (i = 0; i < pairs.length; i++) {
             p = pairs[i];
             if (p.x + p.y > limit) {
                 return p;
             }    
         }
         return null;
     }
    
     console.log(getLowestCombinationOverLimit(ary, limit));
    

答案 3 :(得分:0)

  • 对数组进行排序
  • 首先迭代数组最大值,将最接近的值从限制中减少,直到限制低于0

&#13;
&#13;
function calcSumOfLowestComponents(ary,limit){
  ary.sort(function(a,b) {
    return a - b;
  });

  var safeCount = 0;

  var components = [];
  var remainder = limit + 1;
  while (remainder > 0){
    var found = false;
    for( var i = ary.length - 1 ; i >= 0; i-- ){
      if (remainder > ary[ i ]){
        var closeHigh = i != ary.length - 1 ? ary[i + 1] : ary[i];
        var closeLow = ary[i];
        if (closeHigh - remainder > remainder - closeLow){
          components.push(closeLow);
          remainder -= closeLow;  
        }
        else{
          components.push(closeHigh);
          remainder -= closeHigh;  
        }
        found = true;
      }
    }
    if (!found && remainder > 0){
      components.push(ary[ 0 ]); 
      remainder -= ary[ 0 ];
    }
    
    if (safeCount > 1000){
      break;
    }
    safeCount++;
  }
  return { total : limit + 1 - remainder, components : components};
}

function dumpData(array,value){
  var result = calcSumOfLowestComponents(array,value);
  document.write("Value = " + value + " Total = " + result.total + " Components " + result.components + "</br>");
}

var array = [5,10,28,50,56,280];
dumpData(array,2);
dumpData(array,5);
dumpData(array,11);
dumpData(array,25);
dumpData(array,28);
dumpData(array,50);
dumpData(array,55);
dumpData(array,281);
dumpData(array,429);
&#13;
&#13;
&#13;

结果

Value = 2 Total = 5 Components 5
Value = 5 Total = 10 Components 5,5
Value = 11 Total = 15 Components 10,5
Value = 25 Total = 28 Components 28
Value = 28 Total = 33 Components 28,5
Value = 50 Total = 55 Components 50,5
Value = 55 Total = 56 Components 56
Value = 281 Total = 285 Components 280,5
Value = 429 Total = 430 Components 280,56,56,28,10