var a = [1,3,6,10,-1];
function combinations(array, n) {
}
combinations(a, 9) // should return...
[[1], [3], [6], [-1], [1,3], [1,6], [1,-1], [3,6], [3,-1], [6, -1], [10, -1], [1,3,-1], [3,6,-1], [1,6,-1], [1,3,6,-1]]
也许我错过了一些正确的答案,但你明白了。真的很想知道如何解决这个问题!
答案 0 :(得分:8)
我想说这里的问题是取一个数组的幂集,并将其过滤到只有总和大于一定数的元素。
集合的幂集是该集合的所有子集的集合。 (说快五倍,你将成为一名数学家)
例如,[1]
的幂集为[[], [1]]
,[1, 2]
的幂集为[[], [1], [2], [1, 2]]
。
首先,我要定义一个powerSet
函数,如下所示:
var powerSet = function (arr) {
// the power set of [] is [[]]
if(arr.length === 0) {
return [[]];
}
// remove and remember the last element of the array
var lastElement = arr.pop();
// take the powerset of the rest of the array
var restPowerset = powerSet(arr);
// for each set in the power set of arr minus its last element,
// include that set in the powerset of arr both with and without
// the last element of arr
var powerset = [];
for(var i = 0; i < restPowerset.length; i++) {
var set = restPowerset[i];
// without last element
powerset.push(set);
// with last element
set = set.slice(); // create a new array that's a copy of set
set.push(lastElement);
powerset.push(set);
}
return powerset;
};
然后我将定义一个函数,它接受数组的幂集,并且只包含总和小于或等于某个量的元素:
var subsetsLessThan = function (arr, number) {
// all subsets of arr
var powerset = powerSet(arr);
// subsets summing less than or equal to number
var subsets = [];
for(var i = 0; i < powerset.length; i++) {
var subset = powerset[i];
var sum = 0;
for(var j = 0; j < subset.length; j++) {
sum += subset[j];
}
if(sum <= number) {
subsets.push(subset);
}
}
return subsets;
};
这在大型阵列上可能不会很快,但它适用于小型阵列。
看起来它为console.log(subsetsLessThan([1,3,6,10,-1], 9));
编辑:关于此处实施的电源设置功能的更多信息
[]
的唯一子集是[]
,因此[]
的幂集是仅包含[]
的集合。那将是[[]]
。
如果您传入if
,powerSet
函数中的初始[[]]
语句会立即返回[]
。
var powerSet = function (arr) {
if(arr.length === 0) {
return [[]];
}
如果传入一个至少包含一个元素的集合,powerSet
函数首先删除最后一个元素。例如,如果您在powerSet
上致电[1, 2]
,则变量lastElement
将设置为2
,arr
将设置为[1]
。
var lastElement = arr.pop();
然后powerSet
函数递归调用自身以获得列表“休息”的幂集。如果您已通过[1, 2]
,则会将restPowerset
分配给powerSet([1])
[[], [1]]
。
var restPowerset = powerSet(arr);
我们定义一个变量,该变量将保存传入的内容的幂集,[1, 2]
var powerset = [];
我们遍历restPowerset
中的每一组。
for(var i = 0; i < restPowerset.length; i++) {
var set = restPowerset[i];
[1]
的任何子集也是[1, 2]
的子集,因此我们将其添加到列表中。也就是说,[]
和[1]
都是[1, 2]
的子集。
powerset.push(set);
如果您将元素2
添加到[1]
的任何子集,这也是[1, 2]
的子集,那么我们将其添加到列表中。 [2]
和[1, 2]
都是[1, 2]
的子集。
set = set.slice(); // copy the array
set.push(lastElement); // add the element
powerset.push(set);
这就是全部。此时,变量powerset
为[[], [2], [1], [1, 2]]
。退货吧!
}
return powerset;
};
答案 1 :(得分:3)
暴力O(N * 2 N )解决方案,其中N = a.length < 31
。
这使用索引i
作为filter
的位字段,在每次迭代中将a
的元素放入子列表中。
var a = [1,3,6,10,-1];
function combinations(array, n) {
var lists = [], M = 1<<array.length;
for( var i = 1 ; i < M ; ++i ) {
var sublist = array.filter(function(c,k){return i>>k & 1});
if( sublist.reduce(function(p,c){return p+c},0) <= n )
lists.push(sublist);
}
return lists;
}
console.log(JSON.stringify(combinations(a,9)));
[[1],[3],[1,3],[6],[1,6],[3,6],[ - 1],[1,-1],[3, - 1],[1,3,-1],[6,-1],[1,6,-1],[3,6,-1],[1,3,6,-1],[10 ,-1]]
答案 2 :(得分:3)
与Matt的答案类似,但使用Array.filter()和Array.reduce()打包。在该示例中,变量mask
从1增加到32-1(因为阵列长度是5并且count
= 1 <&lt; 5,这是32)。对每个掩码增量过滤数组,产生一个新的数组或排列,其中只包含某些值。
如果掩码向右偏移值的索引是奇数,则在置换中包含一个值。在这里考虑二进制,因为一个值应该在排列中或者不是(0或1)并且因为掩码将遍历所有可能的数字,所以所有可能的排列都直接在数字中被表示为二进制:
var a = [1,3,6,10,-1];
function combinations(array, n) {
var mask, len = array.length, count = 1 << len, permutations = [];
var indexVisible = function(v, i) { return ((mask >> i) & 1) == 1 }
var sum = function(a, b) { return a + b }
for (mask = 1; mask < count; ++mask) {
permutations.push(array.filter(indexVisible))
}
return permutations.filter(function(p) { return p.reduce(sum) <= n })
}
console.log(JSON.stringify(combinations(a, 9)));
函数indexVisible()
用于过滤原始数组并返回与掩码匹配的排列。
函数sum()
用于将每个排列减少到其值的总和,如果该总和小于或等于n
,则它包含在最终结果中并从combinations()
以下是排列:
[[1],[3],[1,3],[6],[1,6],[3,6],[1,3,6],[10],[1,10],[3,10],[1,3,10],[6,10],[1,6,10],[3,6,10],[1,3,6,10],[-1],[1,-1],[3,-1],[1,3,-1],[6,-1],[1,6,-1],[3,6,-1],[1,3,6,-1],[10,-1],[1,10,-1],[3,10,-1],[1,3,10,-1],[6,10,-1],[1,6,10,-1],[3,6,10,-1],[1,3,6,10,-1]]
以下是结果:
[[1],[3],[1,3],[6],[1,6],[3,6],[-1],[1,-1],[3,-1],[1,3,-1],[6,-1],[1,6,-1],[3,6,-1],[1,3,6,-1],[10,-1]]
您可以在此JSFiddle中查看所有这些内容的工作方式和不同组合。
答案 3 :(得分:2)
修改:给予应有的信用..从this answer借用了大部分逻辑
var combinations = function(a,m) {
var gc = function(a) {
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 = 0; i < a.length; i++) {
fn(i, a, [], all);
}
all.push(a);
return all;
}
var c = gc(a);
return c.filter(function(e) {
var n = e.length;
var sum = 0;
while(n--)
sum += parseFloat(e[n]) || 0;
return sum<=m;
},m);
}
var a = [1,3,6,10,-1];
combinations(a,9);
<强>输出强>
[[1], [3], [6], [-1], [1, 3], [1, 6], [1, -1], [3, 6], [3, -1], [6, -1], [10, -1], [1, 3, -1], [1, 6, -1], [3, 6, -1], [1, 3, 6, -1]]
答案 4 :(得分:2)
看起来很有趣,不玩,这就是我所拥有的。
的Javascript
function kCombs(set, k) {
var setLength = set.length,
combs = [],
i = 0,
tailLength,
head,
tail,
j,
t,
u;
if (k > 0 && k <= setLength) {
if (k === setLength) {
combs.push(set);
} else if (k === 1) {
while (i < setLength) {
combs.push([set[i]]);
i += 1;
}
} else {
u = k - 1;
setLength = setLength - k + 1;
while (i < setLength) {
t = i + 1;
head = set.slice(i, t);
tail = kCombs(set.slice(t), u);
j = 0;
tailLength = tail.length;
while (j < tailLength) {
combs.push(head.concat(tail[j]));
j += 1;
}
i = t;
}
}
}
return combs;
}
function combinations(array, n) {
var arrayLength = array.length,
combs = [],
combsLength,
results = [],
temp = 0,
current,
currentLength,
i,
j,
k = 1;
while (k <= arrayLength) {
i = 0;
current = kCombs(array, k);
currentLength = current.length;
while (i < currentLength) {
combs.push(current[i]);
i += 1;
}
k += 1;
}
i = 0;
combsLength = combs.length;
while (i < combsLength) {
j = 0;
current = combs[i];
currentLength = current.length;
while (j < currentLength) {
temp += current[j];
j += 1;
}
if (temp <= n) {
results.push(current);
}
temp = 0;
i += 1;
}
return results;
}
var a = [1, 3, 6, 10, -1];
console.log(JSON.stringify(combinations(a, 9)));
输出
[[1],[3],[6],[-1],[1,3],[1,6],[1,-1],[3,6],[3,-1],[6,-1],[10,-1],[1,3,-1],[1,6,-1],[3,6,-1],[1,3,6,-1]]
上
所有这些jsPerf,虽然@jcarpenter解决方案含糊不清。
在现代浏览器上,您可以使用for
intead while
从这个解决方案中挤出更多,因为它们已针对for
进行了高度优化。按索引分配而不是push
也可以提高性能。
将性能测试扩展到包含更多测试集会很好,如果我觉得无聊的话。
答案 5 :(得分:2)
以下代码将为您提供总计最多9个或更少的所有子数组。
function getSubArrays(arr,n){
var len = arr.length,
subs = Array(Math.pow(2,len)).fill();
return subs.map((_,i) => { var j = -1,
k = i,
res = [];
while (++j < len ) {
k & 1 && res.push(arr[j]);
k = k >> 1;
}
return res;
}).slice(1)
.filter(a => a.reduce((p,c) => p+c) <= n);
}
var arr = [1,3,6,10,-1],
result = getSubArrays(arr,9);
console.log(JSON.stringify(result));
答案 6 :(得分:1)
这里的简洁非常神秘。一些描述性功能怎么样?
该方法使用二进制来创建所有可能组合的地图。然后地图用于从数组中提取项目。把弹出的物品加起来,就是这个。
combinations([1, 3, 6, 10, -1], 9)
生成的结果是:[[-1],[10,-1],[6],[6,-1],[3],[3,-1],[3,6],[3,6,-1],[1],[1,-1],[1,6],[1,6,-1],[1,3],[1,3,-1],[1,3,6,-1]]
。
/**
* Get an array of all the possible combinations
* of x items. Combinations are represented as binary.
* @param {Number} x - example 2
* @return {String[]} - example ['00', '01', '10', '11']
*/
function getCombinationsOfXItems(x) {
var allOn = '',
numCombos = 0,
i = 0,
combos = [];
// find upper limit
while (allOn.length < x) {
allOn += 1;
}
// number of possible combinations
numCombos = parseInt(allOn, 2) + 1;
// generate the combos
while(i < numCombos) {
combos.push(pad(toBase2(i++), allOn.length));
}
return combos;
}
/**
* Pad a string with leading zeros.
* @param {String} x - example '100'
* @param {Number} length - example 6
* @return {String} - example '000100'
*/
function pad(x, length) {
while (x.length < length) {
x = 0 + x;
}
return x;
}
/**
* Get a number as a binary string.
* @param {Number} x - example 3
* @return {String} - example '11'
*/
function toBase2(x) {
return x.toString(2);
}
/**
* Given an array and a map of its items as a binary string,
* return the items identified by 1.
* @param {Array} arr - example [1,2,3]
* @param {String} binary - example '101'
* @return {Array} - example [1,3]
*/
function pluckFromArrayByBinary(arr, binary) {
var plucked = [],
i = 0,
max = binary.length;
for (; i < max; i++) {
if (binary[i] === '1') {
plucked.push(arr[i]);
}
}
return plucked;
}
/**
* Given an array, return a multi-dimensional
* array of all the combinations of its items.
* @param {Array} - example [1, 2];
* @return {Array[]} - [ [1], [1, 2], [2] ]
*/
function getCombosOfArrayItems(arr) {
var comboMaps = getCombinationsOfXItems(arr.length),
combos = [];
// remove the "all off" combo (ex: '00000')
comboMaps.shift();
for (var i = 0; i < comboMaps.length; i++) {
combos.push(pluckFromArrayByBinary(arr, comboMaps[i]));
}
return combos;
}
/**
* Return all possible combinations of numbers in an
* array whose sum is less than or equal to n
* @param {Number[]} arr
* @param {Number} x
* return {Number[]} - stringified for readability
*/
function combinations(arr, x) {
var combos = getCombosOfArrayItems(arr),
i = 0,
max = combos.length,
combo;
for (; i < max; i++) {
if (sumArray(combos[i]) > x) {
combos.splice(i, 1);
i--;
max--;
}
}
return JSON.stringify(combos);
}
/**
* Return the sum of an array of numbers.
* @param {Number[]} arr
* @return {Number}
*/
function sumArray(arr) {
var sum = 0,
i = 0,
max = arr.length;
for (; i < max; i++) {
sum += arr[i];
}
return sum;
}
console.log(combinations([1, 3, 6, 10, -1], 9));
答案 7 :(得分:1)
@jcarpenter解决方案非常好我只需要为喜欢ECMA5的人重做它。这不会像for
的原始力量那么快,现代方法没有那么长的时间进行如此高度优化(并且他们做了相当多的工作)。但是性能结果确实显示了powerSet
算法有多好(并且它是一个可重用的函数)。我也过滤掉了歧义,这会让事情变得缓慢。
的Javascript
function powerSet(arr) {
var lastElement,
val;
if (!arr.length) {
val = [[]];
} else {
lastElement = arr.pop();
val = powerSet(arr).reduce(function (previous, element) {
previous.push(element);
element = element.slice();
element.push(lastElement);
previous.push(element);
return previous;
}, []);
}
return val;
}
function combinations(array, n) {
return powerSet(array).filter(function (set) {
return set.length && set.reduce(function (previous, element) {
return previous + element;
}, 0) <= n;
});
}
var a = [1, 3, 6, 10, -1];
console.log(JSON.stringify(combinations(a, 9)));
输出
[[-1],[10,-1],[6],[6,-1],[3],[3,-1],[3,6],[3,6,-1],[1],[1,-1],[1,6],[1,6,-1],[1,3],[1,3,-1],[1,3,6,-1]]
上
并添加到jsPerf
答案 8 :(得分:0)
试试这个:
var a = [1,3,6,10,-1];
function combinations(array, n) {
var arrayCopy = [],
results = [];
// duplicate the array
for (var i in array)
arrayCopy[i] = array[i];
for (var i in array)
for (var j in arrayCopy)
if ((array[i] + arrayCopy[j]) <= n)
results.push([array[i], arrayCopy[j]]);
return results;
}
console.log(combinations(a, 9));
记录:
[1, 1], [1, 3], [1, 6], [1, -1],
[3, 1], [3, 3], [3, 6], [3, -1],
[6, 1], [6, 3], [6, -1],
[10, -1],
[-1, 1], [-1, 3], [-1, 6], [-1, 10], [-1, -1]