Javascript 求幂集加权子集和的最大值

Javascript 求幂集加权子集和的最大值,javascript,algorithm,optimization,mathematical-optimization,subset-sum,Javascript,Algorithm,Optimization,Mathematical Optimization,Subset Sum,我有一个稀疏的输入功率集(即一些组合已预先排除)。幂集中的每个条目都有一定的分数。我想找到覆盖所有分数并使总分数最大化的组合 例如,假设输入生成如下: function powerset(ary) { var ps = [[]]; for (var i = 0; i < ary.length; i++) { for (var j = 0, len = ps.length; j < len; j++) { ps.push(ps[j].concat(ary[i

我有一个稀疏的输入功率集(即一些组合已预先排除)。幂集中的每个条目都有一定的分数。我想找到覆盖所有分数并使总分数最大化的组合

例如,假设输入生成如下:

function powerset(ary) {
  var ps = [[]];
  for (var i = 0; i < ary.length; i++) {
    for (var j = 0, len = ps.length; j < len; j++) {
      ps.push(ps[j].concat(ary[i]));
    }
  }
  return ps;
}

function generateScores() {
  var sets = powerset([0, 1, 2, 3]);
  sets.pop() //remove the last entry to make it "sparse"
  var scores = {};
  for (var i = 1; i < sets.length; i++) { //skip 0-len
    var set = sets[i];
    var val = 0;
    for (var j = 0; j < set.length; j++) {
      val |= (1 << set[j]);
    }
    scores[val] = ~~Math.pow(((Math.random()+1)*4),set.length);
  }
  return scores;
}
var scores = generateScores();
由于顺序无关紧要,我可以将这些组合转换成一个位掩码并将其用作键。因此,阅读表格:键“3”是
011
是基数2,这意味着链接0-1得到36分,而0单独+1单独得到11分,因此链接
0-1
,大于其各部分的总和
0,1

在这样做的过程中,我将其简化为一个加权子集求和问题,目标是找到每一个求和为15的组合(相当于基数2中的
1111
),然后取最大值。这就是我遇到的问题。我尝试使用动态规划,但由于随机性,我不知道如何减少。例如,
1-2
可能优于
1,2
(在上表中,“3”的得分高于“1”+“2”)。但是
1-3,2
可能比
1-2,3
1-2-3
更好)


如何有效地找到最佳组合?(暴力是不可行的)。对于本例,解决方案将是“11”+“4”,总共515个。

您希望找到总和为15且没有任何重叠位的元素组合,从而最大化所选元素的分数

为此,请定义一个函数
bestSubset(use,valid)
,该函数输入一组需要使用的元素和一组有效但尚未考虑的元素子集。它通过考虑有效集合中的元素
s
进行递归操作,考虑使用
s
的情况或不使用它的情况(如果使用它,则不能再使用任何重叠位的元素)

下面是一个javascript实现:

var scores = {1:7, 2:4, 3:36, 4:5, 5:32, 6:50, 7:84, 8:4, 9:30, 10:50, 11:510, 12:47, 13:73, 14:344};
var S = [];
for (var prop in scores) {
  S.push([parseInt(prop), scores[prop]]);
}

var n = 15;  // Target sum
var k = S.length;  // Number of weights

function bestSubset(use, valid) {
  if (valid.length == 0) {
    var weightSum = 0;
    var scoreSum = 0;
    var weights = [];
    for (var ct=0; ct < use.length; ct++) {
      weightSum += S[use[ct]][0];
      weights.push(S[use[ct]][0]);
      scoreSum += S[use[ct]][1];
    }
    if (weightSum == n) {
      return [weights, scoreSum];
    } else {
      return false;
    }
  }

  // Don't use valid[0]
  var valid1 = [];
  for (ct=1; ct < valid.length; ct++) {
    valid1.push(valid[ct]);
  }
  var opt1 = bestSubset(use, valid1);

  // Use valid[0]
  var use2 = JSON.parse(JSON.stringify(use));
  use2.push(valid[0]);
  var valid2 = [];
  for (ct=1; ct < valid.length; ct++) {
    if ((S[valid[0]][0] & S[valid[ct]][0]) == 0) {
      valid2.push(valid[ct]);
    }
  }
  var opt2 = bestSubset(use2, valid2);

  if (opt1 === false) {
    return opt2;
  } else if (opt2 === false || opt1[1] >= opt2[1]) {
    return opt1;
  } else {
    return opt2;
  }
}

var initValid = [];
for (var ct=0; ct < S.length; ct++) {
  initValid.push(ct);
}
alert(JSON.stringify(bestSubset([], initValid)));
var得分={1:7,2:4,3:36,4:5,5:32,6:50,7:84,8:4,9:30,10:50,11:510,12:47,13:73,14:344};
var S=[];
for(分数中的var道具){
S.push([parseInt(prop),分数[prop]]);
}
变量n=15;//目标金额
var k=S.length;//重量数
函数bestSubset(使用,有效){
if(valid.length==0){
var加权和=0;
var scoreSum=0;
var权重=[];
对于(变量ct=0;ct=opt2[1]){
返回opt1;
}否则{
返回opt2;
}
}
var initValid=[];
对于(变量ct=0;ct
这将返回一组得分为515的
[4,11]
,正如您在原始帖子中标识的那样

从非稀疏情况下的一些计算实验(即带有
d
数字和目标
(2^d)-1
,包括所有数字
1,2,…,(2^d)-1
),我发现这在数字数量上呈指数增长(它在递归函数顶部检查有效性的次数是
O(e^(1.47d))
). 这比您单独考虑包括或不包括每个数字“代码>1, 2,…,(2 ^ D)- 1代码/代码>,在双指数运行时间中运行的速度快得多——<代码> O(2 ^ 2 ^)<代码> > < /P> < P>一种不同的方法(一如既往):

第一个想法: 对于其和小于单个权重的每个值,可以得到一个权重。因此,wa+wb 第二个想法: 为了更好地理解权重,它必须是自然数或整数

第三个想法: 为什么不直接使用数字本身并稍微减少一点,使总和小于单个权重呢

一起: 我把数字和值作为权重。此外,我将其值减少1,因此:

a=1,b=2,c=3瓦+wb wa=0,wb=1,wc=2=>0+1<2

公式为:权重n=n-1

证明: 对于每一个summand,你会得到一个-1的malus。因此,对于更多的求和,得到的数字比原始数字的权重小

另一个例子: 权重15(14)应该大于权重4(3)和权重11(10)之和

数量:14>3+10


我的意思是,这里不需要任何程序代码。

对于那些在谷歌上搜索的人,我使用了@josilber提供的答案,没有递归&有重叠保护(见下文)。因为JS中的递归深度限制为1000,所以我不得不使用循环。不幸的是,对于我的用例,我的内存仍然不足,所以看起来我必须使用一些启发式方法

var scores = {1: 7, 2: 4, 3: 36, 4: 5, 5: 32, 6: 50, 7: 84, 8: 4, 9: 30, 10: 50, 11: 510, 12: 47, 13: 73, 14: 344};
var S = [];
var keys = Object.keys(scores);
for (i = 0; i < keys.length; i++) {
  S.push([parseInt(keys[i]), scores[keys[i]]]);
}

var n = Math.pow(2,range.length) -1;  // Target sum
var k = S.length;  // Number of weights

// best[i, j] is scored in position i*(k+1) + j
var best = [];

// Base case
for (var j = 0; j <= k; j++) {
  best.push([[], 0]);
}

// Main loop
for (var i = 1; i <= n; i++) { 
  best.push(false);  // j=0 case infeasible
  for (j = 1; j <= k; j++) {
    var opt1 = best[i * (k + 1) + j - 1];
    var opt2 = false;
    if (S[j - 1][0] <= i) {
      var parent = best[(i - S[j - 1][0]) * (k + 1) + j - 1];
      if (parent !== false) {
        opt2 = [parent[0].slice(), parent[1]];
        var child = S[j - 1];
        var opt2BitSig = 0;
        for (var m = 0; m < opt2[0].length; m++) {
          opt2BitSig |= opt2[0][m];
        }
        if ((opt2BitSig & child[0])) {
          opt2 = false;
        } else {
          opt2[0].push(child[0]);
          opt2[1] += child[1];
        }
      }
    }
    if (opt1 === false) {
      best.push(opt2);
    } else if (opt2 === false || opt1[1] >= opt2[1]) {
      best.push(opt1);
    } else {
      best.push(opt2);
    }
  }
}

console.log(JSON.stringify(best[n * (k + 1) + k]));
var得分={1:7,2:4,3:36,4:5,5:32,6:50,7:84,8:4,9:30,10:50,11:510,12:47,13:73,14:344};
var S=[];
var keys=Object.keys(分数);
对于(i=0;i对于(var j=0;j)可能更多的例子使问题更清楚。例如,为什么
3
应该返回
36
?我缺少什么
var scores = {1: 7, 2: 4, 3: 36, 4: 5, 5: 32, 6: 50, 7: 84, 8: 4, 9: 30, 10: 50, 11: 510, 12: 47, 13: 73, 14: 344};
var S = [];
var keys = Object.keys(scores);
for (i = 0; i < keys.length; i++) {
  S.push([parseInt(keys[i]), scores[keys[i]]]);
}

var n = Math.pow(2,range.length) -1;  // Target sum
var k = S.length;  // Number of weights

// best[i, j] is scored in position i*(k+1) + j
var best = [];

// Base case
for (var j = 0; j <= k; j++) {
  best.push([[], 0]);
}

// Main loop
for (var i = 1; i <= n; i++) { 
  best.push(false);  // j=0 case infeasible
  for (j = 1; j <= k; j++) {
    var opt1 = best[i * (k + 1) + j - 1];
    var opt2 = false;
    if (S[j - 1][0] <= i) {
      var parent = best[(i - S[j - 1][0]) * (k + 1) + j - 1];
      if (parent !== false) {
        opt2 = [parent[0].slice(), parent[1]];
        var child = S[j - 1];
        var opt2BitSig = 0;
        for (var m = 0; m < opt2[0].length; m++) {
          opt2BitSig |= opt2[0][m];
        }
        if ((opt2BitSig & child[0])) {
          opt2 = false;
        } else {
          opt2[0].push(child[0]);
          opt2[1] += child[1];
        }
      }
    }
    if (opt1 === false) {
      best.push(opt2);
    } else if (opt2 === false || opt1[1] >= opt2[1]) {
      best.push(opt1);
    } else {
      best.push(opt2);
    }
  }
}

console.log(JSON.stringify(best[n * (k + 1) + k]));