这是一种递归方法,它也仅适用于非负数组元素:
function subset_sum( list, upper_bound )
{
if( list.length == 1 ) return list[0] < upper_bound ? [list] : [];
var new_list = list.slice(0); // copy list
var elem = new_list.pop();
var combo = elem < upper_bound ? [[elem]] : []; // A
if( combo.length )
{
var lists = subset_sum( new_list, upper_bound-elem ); // B
combo = combo.concat( lists.map(function(a) { return a.concat(elem); }) );
}
return combo.concat(subset_sum( new_list, upper_bound )); // C
}
var arr = [2, 25, 37, 54, 54, 76, 88, 91, 99];
var combos = subset_sum(arr,100);
这是 jfiddle:http://jsfiddle.net/bceHr/4/
基本情况是单元素列表,当且仅当元素小于上限时,答案就是它本身。
递归步骤分为3个互斥且完整的情况,上面标记为A、B、C:
- (A) 最后一个元素是单例集当且仅当它小于上界。
- (B) 包括最后一个元素的所有其他子集都被计算在内,方法是将函数递归地应用到省略该元素的列表中,并通过该元素减少一个新的上界。
- (C) 所有其他排除最后一个元素的子集都被计算在内,方法是使用相同的上界将函数递归地应用于列表。
最后,有 26 种这样的组合。由于 54 包含了两次,因此它也在输出中重复:
[[99],[91],[2,91],[88],[2,88],[76],[2,76],[54],[37,54],[2 ,37,54],[25,54],[2,25,54],[2,54],[54],[37,54],[2,37,54],[25,54], [2,25,54],[2,54],[37],[25,37],[2,25,37],[2,37],[25],[2,25],[2 ]]