在数组中找到所有可能的子集组合?

IT技术 javascript arrays subset
2021-02-10 10:41:06

我需要使用最少的2项目和未知的最大值来获取数组的所有可能子集谁能帮我一下?

假设我有以下数组:

[1, 2, 3]

我怎么得到这个?

[
    [1, 2],
    [1, 3],
    [2, 3],
    [1, 2, 3]
]
6个回答

在窃取了这个JavaScript 组合生成器之后,我添加了一个参数来提供最小长度,结果是,

var combine = function(a, min) {
    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([1, 2, 3], 2);

输出是,

[[1, 2], [1, 3], [2, 3], [1, 2, 3]]
这似乎缺少空集。
2021-03-13 10:41:06
当我组合 30 个号码时,应用程序会崩溃
2021-03-26 10:41:06

组合,简短的一个:

function combinations(array) {
  return new Array(1 << array.length).fill().map(
    (e1, i) => array.filter((e2, j) => i & 1 << j));
}

console.log(combinations([1, 2, 3]).filter(a => a.length >= 2))

很好,但它是一个如此“简洁”的算法,它确实应该比你放在那里的词多一些:) 不需要太多,只需要一些总结来给它一些上下文。
2021-03-16 10:41:06
fill() 至少需要一个值...这不起作用
2021-04-09 10:41:06

通过这个问题的一个小调整,我希望我的解决方案更有效,因为它使用位运算符来生成所有子集。

var sets = (function(input, size) {
    var results = [], result, mask, i, total = Math.pow(2, input.length);
    for (mask = size; mask < total; mask++) {
        result = [];
        i = input.length - 1;

        do {
            if ((mask & (1 << i)) !== 0) {
                result.push(input[i]);
            }
        } while (i--);

        if (result.length >= size) {
            results.push(result);
        }
    }

    return results; 
})(['a','b','c','d','e','f'], 2);
console.log(sets);
如果只想要一种特定大小的子集怎么办?一种选择是更改if(result.length >= size)if(result.length === size). 但这似乎效率低下,因为它仍在生成所有 2^n 个子集,然后丢弃大小错误的子集。可以进一步调整您的代码以仅关注一种尺寸吗?
2021-03-20 10:41:06
这是一个很好的解决方案,但一旦您的数组达到任何超过 16 个字符的唯一分类,就会崩溃。
2021-04-01 10:41:06
由于反向 while 循环,结果是倒退的。如果使用前向循环,它将具有正确的顺序
2021-04-08 10:41:06

这个算法要求递归……这就是我要做的

var arr = [1,2,3,4,5];
function getSubArrays(arr){
  if (arr.length === 1) return [arr];
  else {
  	subarr = getSubArrays(arr.slice(1));
  	return subarr.concat(subarr.map(e => e.concat(arr[0])), [[arr[0]]]);
  }
}
console.log(JSON.stringify(getSubArrays(arr)));

上述算法的另一个奇特版本;

var arr = [1,2,3,4,5],
    sas = ([n,...ns],sa) => !ns.length ? [[n]]
                                       : (sa = sas(ns),
                                          sa.concat(sa.map(e => e.concat(n)),[[n]]));

为了了解发生了什么让我们一步一步

  • 直到我们最终得到一个长度为 1 的数组作为参数,我们一直getSubArrays使用参数数组尾部调用相同的函数所以尾部[1,2,3,4,5][2,3,4,5]
  • 一旦我们将单个项目数组作为参数,例如[5]我们返回[[5]]到前一个getSubArrays函数调用。
  • 然后在前面的getSubArrays功能arr[4,5]subarr被分配到[[5]]
  • 现在我们返回[[5]].concat([[5]].map(e => e.concat(4), [[4]])实际上[[5], [5,4], [4]]是上一个getSubArrays函数调用。
  • 然后在前面的getSubArrays功能arr[3,4,5]subarr被分配到[[5], [5,4], [4]]
  • 等等...
运行代码段后,我可以告诉我这得到了我正在寻找的答案,而且我喜欢它使用递归的事实。但我很难理解它。@Redu 您能否解释一下您是如何使用这种方法的?
2021-03-29 10:41:06
@user3295436 谢谢.. 答案中添加了相同算法的更现代版本和解释。
2021-03-30 10:41:06

这是一种使用 ECMAScript 2015生成器函数查找所有组合的方法

要限制为问题中要求的最小尺寸,只需在产生组合之前确保组合的长度:

function* generateCombinations(arr, minSize) {
  function* doGenerateCombinations(offset, combo) {
    if (combo.length >= minSize) {
      yield combo;
    }
    for (let i = offset; i < arr.length; i++) {
      yield* doGenerateCombinations(i + 1, combo.concat(arr[i]));
    }
  }
  yield* doGenerateCombinations(0, []);
}

for (let combo of generateCombinations([1, 2, 3, 4, 5], 2)) {
  console.log(JSON.stringify(combo));
}

在点上yield进行限制允许以一种可读的方式将此功能调整到其他常见用例,例如,选择精确大小的所有组合:

谢谢,我会尝试对其进行测试并让您知道结果。
2021-03-19 10:41:06
@PiotrBerebecki 看起来你需要一个else围绕你的 for 循环。
2021-03-28 10:41:06
我仍然试图围绕这个答案,但我真的很喜欢代码。
2021-03-28 10:41:06
我正在使用您的方法,但是在非常大的输入上尝试该功能时会超时。这是因为我们首先生成所有可能的组合,然后生成每个组合。我们如何修改这个函数以使其更有效?这是挑战的功能和描述的链接:repl.it/E2QW/1
2021-03-29 10:41:06