获取对象中所有项目组合的高效算法
Efficient algorithm to get the combinations of all items in object
给定一个包含 n 个键的数组或对象,我需要找到长度为 x
的所有组合。
鉴于 X
是可变的。 binomial_coefficient(n,x)
.
目前我正在使用这个:
function combine(items) {
var result = [];
var f = function(prefix, items) {
for (var i = 0; i < items.length; i++) {
result.push(prefix + items[i]);
f(prefix + items[i], items.slice(i + 1));
}
}
f('', items);
return result;
}
var combinations = combine(["a", "b", "c", "d"]);
输出为:
["a", "ab", "abc", "abcd", "abd", "ac", "acd", "ad", "b", "bc", "bcd", "bd", "c", "cd", "d"]
所以如果我想要 n=4
的二项式系数 x=3
我 select 所有长度等于 3 的字符串。 {abc, abd, acd, bcd}.
所以我分两步进行。
有没有更高效复杂度更小的算法?
我们可以只创建我们感兴趣的那些组合。另外,我们可以使用指向原始数组的指针,而不是在每次调用中使用切片来克隆数组。这是一个版本。在没有外部全局变量的情况下将其转换为递归留作练习。
function choose(ns,r){
var res = [];
function _choose(i,_res){
if (_res.length == r){
res.push(_res);
return;
} else if (_res.length + ns.length - i == r){
_res = _res.concat(ns.slice(i));
res.push(_res);
return
}
var temp = _res.slice();
temp.push(ns[i]);
_choose(i + 1,temp);
_choose(i + 1,_res);
}
_choose(0,[]);
return res;
}
var combinations = choose(["a", "b", "c", "d"], 3);
console.log(JSON.stringify(combinations));
你的算法差不多O(2^n)
,你可以舍弃很多组合,但是元素的个数会是(n! * (n-x)!) / x!
。
要丢弃无用的组合,您可以使用索引数组。
function combine(items, numSubItems) {
var result = [];
var indexes = new Array(numSubItems);
for (var i = 0 ; i < numSubItems; i++) {
indexes[i] = i;
}
while (indexes[0] < (items.length - numSubItems + 1)) {
var v = [];
for (var i = 0 ; i < numSubItems; i++) {
v.push(items[indexes[i]]);
}
result.push(v);
indexes[numSubItems - 1]++;
var l = numSubItems - 1; // reference always is the last position at beginning
while ( (indexes[numSubItems - 1] >= items.length) && (indexes[0] < items.length - numSubItems + 1)) {
l--; // the last position is reached
indexes[l]++;
for (var i = l +1 ; i < numSubItems; i++) {
indexes[i] = indexes[l] + (i - l);
}
}
}
return result;
}
var combinations = combine(["a", "b", "c", "d"], 3);
console.log(JSON.stringify(combinations));
例如,第一个组合有索引:[0, 1, 2]
和元素["a", "b", "c"]
。为了计算下一个组合,它获取最后一个索引 2
并尝试递增,如果递增低于最大位置(在本例中为 4
),则达到下一个组合,但如果它不是,它必须增加到以前的索引。
您可以使用强调数组长度和仍然需要的项目的迭代和递归方法。
基本上 combine()
接受一个数组,其中包含要组合的值和所需组合结果集的大小。
内部函数c()
取一个之前组合的数组和一个起始值作为组合的原始数组的索引。 return 是一个包含所有组合的数组。
第一次调用总是 c([], 0)
,因为结果数组为空且起始索引为 0。
function combine(array, size) {
function c(part, start) {
var result = [], i, l, p;
for (i = start, l = array.length; i < l; i++) {
p = part.slice(0); // get a copy of part
p.push(array[i]); // add the iterated element to p
if (p.length < size) { // test if recursion can go on
result = result.concat(c(p, i + 1)); // call c again & concat rresult
} else {
result.push(p); // push p to result, stop recursion
}
}
return result;
}
return c([], 0);
}
console.log(combine(["a", "b", "c", "d"], 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
这才是真正的递归。
function seq(a,b){
var res = [];
for (var i=a; i<=b; i++)
res.push(i);
return res;
}
function f(n,k){
if (k === 0)
return [[]];
if (n === k)
return [seq(1,n)];
let left = f(n - 1, k - 1),
right = f(n - 1, k);
for (let i=0; i<left.length; i++)
left[i].push(n);
return left.concat(right);
}
console.log(JSON.stringify(f(4,3)))
给定一个包含 n 个键的数组或对象,我需要找到长度为 x
的所有组合。
鉴于 X
是可变的。 binomial_coefficient(n,x)
.
目前我正在使用这个:
function combine(items) {
var result = [];
var f = function(prefix, items) {
for (var i = 0; i < items.length; i++) {
result.push(prefix + items[i]);
f(prefix + items[i], items.slice(i + 1));
}
}
f('', items);
return result;
}
var combinations = combine(["a", "b", "c", "d"]);
输出为:
["a", "ab", "abc", "abcd", "abd", "ac", "acd", "ad", "b", "bc", "bcd", "bd", "c", "cd", "d"]
所以如果我想要 n=4
的二项式系数 x=3
我 select 所有长度等于 3 的字符串。 {abc, abd, acd, bcd}.
所以我分两步进行。
有没有更高效复杂度更小的算法?
我们可以只创建我们感兴趣的那些组合。另外,我们可以使用指向原始数组的指针,而不是在每次调用中使用切片来克隆数组。这是一个版本。在没有外部全局变量的情况下将其转换为递归留作练习。
function choose(ns,r){
var res = [];
function _choose(i,_res){
if (_res.length == r){
res.push(_res);
return;
} else if (_res.length + ns.length - i == r){
_res = _res.concat(ns.slice(i));
res.push(_res);
return
}
var temp = _res.slice();
temp.push(ns[i]);
_choose(i + 1,temp);
_choose(i + 1,_res);
}
_choose(0,[]);
return res;
}
var combinations = choose(["a", "b", "c", "d"], 3);
console.log(JSON.stringify(combinations));
你的算法差不多O(2^n)
,你可以舍弃很多组合,但是元素的个数会是(n! * (n-x)!) / x!
。
要丢弃无用的组合,您可以使用索引数组。
function combine(items, numSubItems) {
var result = [];
var indexes = new Array(numSubItems);
for (var i = 0 ; i < numSubItems; i++) {
indexes[i] = i;
}
while (indexes[0] < (items.length - numSubItems + 1)) {
var v = [];
for (var i = 0 ; i < numSubItems; i++) {
v.push(items[indexes[i]]);
}
result.push(v);
indexes[numSubItems - 1]++;
var l = numSubItems - 1; // reference always is the last position at beginning
while ( (indexes[numSubItems - 1] >= items.length) && (indexes[0] < items.length - numSubItems + 1)) {
l--; // the last position is reached
indexes[l]++;
for (var i = l +1 ; i < numSubItems; i++) {
indexes[i] = indexes[l] + (i - l);
}
}
}
return result;
}
var combinations = combine(["a", "b", "c", "d"], 3);
console.log(JSON.stringify(combinations));
例如,第一个组合有索引:[0, 1, 2]
和元素["a", "b", "c"]
。为了计算下一个组合,它获取最后一个索引 2
并尝试递增,如果递增低于最大位置(在本例中为 4
),则达到下一个组合,但如果它不是,它必须增加到以前的索引。
您可以使用强调数组长度和仍然需要的项目的迭代和递归方法。
基本上 combine()
接受一个数组,其中包含要组合的值和所需组合结果集的大小。
内部函数c()
取一个之前组合的数组和一个起始值作为组合的原始数组的索引。 return 是一个包含所有组合的数组。
第一次调用总是 c([], 0)
,因为结果数组为空且起始索引为 0。
function combine(array, size) {
function c(part, start) {
var result = [], i, l, p;
for (i = start, l = array.length; i < l; i++) {
p = part.slice(0); // get a copy of part
p.push(array[i]); // add the iterated element to p
if (p.length < size) { // test if recursion can go on
result = result.concat(c(p, i + 1)); // call c again & concat rresult
} else {
result.push(p); // push p to result, stop recursion
}
}
return result;
}
return c([], 0);
}
console.log(combine(["a", "b", "c", "d"], 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
这才是真正的递归。
function seq(a,b){
var res = [];
for (var i=a; i<=b; i++)
res.push(i);
return res;
}
function f(n,k){
if (k === 0)
return [[]];
if (n === k)
return [seq(1,n)];
let left = f(n - 1, k - 1),
right = f(n - 1, k);
for (let i=0; i<left.length; i++)
left[i].push(n);
return left.concat(right);
}
console.log(JSON.stringify(f(4,3)))