如何在JavaScript中查找集合的所有子集?

时间:2017-03-13 21:35:23

标签: javascript subset powerset

我需要获取数组的所有可能子集。

说我有这个:

[1, 2, 3]

我如何得到这个?

[], [1], [2], [1, 2], [2, 3], [1, 3], [1, 2, 3]

我对所有子集感兴趣。对于特定长度的子集,请参阅以下问题:

  • 查找大小为n的子集:12
  • 查找大小的子集> 1:1

11 个答案:

答案 0 :(得分:26)

这是一个非常优雅的解决方案,没有循环或递归,只使用map和reduce数组本机函数。



const getAllSubsets = 
      theArray => theArray.reduce(
        (subsets, value) => subsets.concat(
         subsets.map(set => [value,...set])
        ),
        [[]]
      );

console.log(getAllSubsets([1,2,3]));




答案 1 :(得分:15)

我们可以从offset开始为输入数组的子集解决此问题。然后我们回过头来获得完整的解决方案。

使用generator function允许我们迭代具有恒定内存使用的子集:



// Generate all array subsets:
function* subsets(array, offset = 0) {
  while (offset < array.length) {
    let first = array[offset++];
    for (let subset of subsets(array, offset)) {
      subset.push(first);
      yield subset;
    }
  }
  yield [];
}

// Example:
for (let subset of subsets([1, 2, 3])) {
  console.log(subset); 
}
&#13;
&#13;
&#13;

运行时复杂度与每个解的平均长度(n / 2)= O(n2ⁿ)的解数(2ⁿ)乘以成比例。

答案 2 :(得分:7)

另一个简单的解决方案。

function getCombinations(array) {

    function fork(i, t) {
        if (i === array.length) {
            result.push(t);
            return;
        }
        fork(i + 1, t.concat([array[i]]));
        fork(i + 1, t);
    }

    var result = [];
    fork(0, []);
    return result;
}

var data = [1, 2, 3],
    result = getCombinations(data);
	
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 3 :(得分:6)

您可以使用以下内容轻松地从数组生成powerset:

&#13;
&#13;
var arr = [1, 2, 3];

function generatePowerSet(array) {
  var result = [];
  result.push([]);

  for (var i = 1; i < (1 << array.length); i++) {
    var subset = [];
    for (var j = 0; j < array.length; j++)
      if (i & (1 << j))
        subset.push(array[j]);

    result.push(subset);
  }

  return result;
}

console.log(generatePowerSet(arr));
&#13;
&#13;
&#13;

在整个函数的主循环中,创建子集,然后将其推入result数组。

答案 4 :(得分:2)

我着手了解这篇文章中的示例正在发生什么。虽然函数生成器示例,按位运算符示例以及数组映射和reduce函数的示例用法非常优雅且令人印象深刻,但我发现很难直观地看到正在发生的事情。我有以下两个示例,我相信它们很容易将非递归和递归解决方案可视化。我希望这有助于其他人在寻找所有子集的过程中全神贯注。

非递归: 对于阵列的每个值,克隆所有现有子集(包括空集),并将新值添加到每个克隆,将克隆推回结果。

show-msg

递归地: 通过递归推动与不断增加的值前缀数组连接的当前索引值的组合来构建功率集。

#! /bin/bash

tput sc; tput cup 0 0
printf '%s
================
PROCESS FINISHED
================
%s\n' "$(tput setab 13)" "$(tput sgr0)"
tput rc

答案 5 :(得分:1)

    cur.execute( sql, (row[0], row[1], row[2], row[3], row[4]))
  File "/usr/local/lib/python3.7/site-packages/pymysql/cursors.py", line 161, in execute
    query = self.mogrify(query, args)
  File "/usr/local/lib/python3.7/site-packages/pymysql/cursors.py", line 140, in mogrify
    query = query % self._escape_args(args, conn)
  File "/usr/local/lib/python3.7/site-packages/pymysql/cursors.py", line 115, in _escape_args
    return tuple(conn.literal(arg) for arg in args)
  File "/usr/local/lib/python3.7/site-packages/pymysql/cursors.py", line 115, in <genexpr>
    return tuple(conn.literal(arg) for arg in args)
  File "/usr/local/lib/python3.7/site-packages/pymysql/connections.py", line 469, in literal
    return self.escape(obj, self.encoders)
  File "/usr/local/lib/python3.7/site-packages/pymysql/connections.py", line 462, in escape
    return converters.escape_item(obj, self.charset, mapping=mapping)
  File "/usr/local/lib/python3.7/site-packages/pymysql/converters.py", line 26, in escape_item
    val = encoder(val, mapping)
  File "/usr/local/lib/python3.7/site-packages/pymysql/converters.py", line 58, in escape_float
    raise ProgrammingError("%s can not be used with MySQL" % s)
NameError: name 'ProgrammingError' is not defined

答案 6 :(得分:1)

@koorchik 答案的简短版本。

var getAllSubsets = (nums) => {
  const subsets = [[]];
  for (n of nums) {
    subsets.map((el) => {
      subsets.push([...el, n]);
    });
  }
  return subsets;
};

console.log(getAllSubsets([1, 2, 3])); 
// [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

答案 7 :(得分:0)

let subsets = (n) => {

let result = [];
result.push([]);

n.forEach(a => {

  //array length
   let length = result.length;
    let i =0;

    while(i < length){

      let temp = result[i].slice(0);
      temp.push(a);

      result.push(temp);
      i++;

    }
})

return result;
}

答案 8 :(得分:0)

这是递归的

var subsets = function(s){
  if(s.length === 0) {
    return [[]]
  }
  var h,t,ss_excl_h;
  var ss_incl_h = [];
  [h,...t] = s;
  ss_excl_h = subsets(t)
  for(ss of ss_excl_h) {
    let hArr = [];
    hArr.push(h);
    let temp = hArr.concat(ss)
    ss_incl_h.push(temp);
  }
  return ss_incl_h.concat(ss_excl_h)
}

console.log(subsets([1,2,3])) // returns distinct subsets

答案 9 :(得分:0)

使用flatMaprest / spread,这可能非常优雅:

const subsets = ([x, ...xs]) =>
  x == undefined
    ? [[]]
    : subsets (xs) .flatMap (ss => [ss, [x, ...ss]]) 

console .log (subsets ([1, 2, 3]))
.as-console-wrapper {max-height: 100% !important; top: 0}

此版本未按请求的顺序返回它们。这样做似乎不太优雅,并且可能有更好的版本:

const subset = (xs = []) => {
  if (xs.length == 0) {return [[]]}
  const ys = subset (xs .slice (0, -1))
  const x = xs .slice (-1) [0]
  return [... ys, ... ys .map (y => [... y, x])]
}

或者,相同算法的风格不同,

const subsets = (
  xs = [], 
  x = xs .slice (-1) [0], 
  ys = xs.length && subsets (xs .slice (0, -1))
) =>
  xs .length == 0
    ? [[]]
    : [... ys, ... ys .map (y => [... y, x])]

答案 10 :(得分:0)

无需递归的简单解决方案:

function getAllSubsets(array) {
    const subsets = [[]];
    
    for (const el of array) {
        const last = subsets.length-1;
        for (let i = 0; i <= last; i++) {
            subsets.push( [...subsets[i], el] );
        }
    }
    
    return subsets;
}


它如何工作?

如果我们有一些从输入数字生成的子集,并且想要在输入数组中再添加一个数字,这意味着我们可以获取所有已经存在的子集,并通过将新数字附加到每个现有子集来生成新的子集。


以下是[1, 2, 3]

的示例
  • 以一个空子集开头:[]

  • 通过在每个现有子集上添加“ 1”来创建新的子集。将会是:[] [1]

  • 通过在每个现有子集上添加“ 2”来创建新的子集。将会是:[], [1] [2], [1, 2]

  • 通过在每个现有子集上添加“ 3”来创建新的子集。它将是:[], [1], [2], [1, 2] [3], [1, 3], [2, 3], [1, 2, 3]