如何找到一个多集的所有分区,其中每个部分都有不同的元素?

时间:2019-02-08 15:59:40

标签: javascript arrays algorithm combinations combinatorics

假设我们有这样一个数组: myArray = [A,A,B,B,C,C,D,E]

我想创建一种算法,以便找到所有加在整个数组上的组合,其中没有重复任何元素。

示例组合:

[A, B, C, D, E] [A, B, C]
[A, B, C, D] [A, B, C, E]
[A, B, C] [A, B, C] [D, E]

说明:[A, B, C] [A, B, C] [D, E][A, B, C] [D, E] [A, B, C]是相同的组合。同样,子集的排序也无关紧要。例如,[A,B,C][B,A,C]应该相同。 到目前为止,我没有超越

var myArray = ["A", "A", "B", "B", "C", "C", "D", "E"]

console.log([...new Set(myArray)])

但这完全没有帮助,它只返回一个不同的集合。 我找不到以前发布的类似问题,所以有人可以在这里指导我如何实现此目标吗?

5 个答案:

答案 0 :(得分:3)

我得到315种组合。那正确吗? :)

这是递归:

function distribute(e, i, _comb){
  // No more e's
  if (e[1] == 0)
    return [_comb];
  // We're beyond the combination
  if (i == -1)
    return [_comb.concat([e])];
  let result = [];
  for (let c=1; c<=Math.min(_comb[i][1], e[1]); c++){
    let comb = _comb.map(x => x.slice());

    if (c == comb[i][1]){
      comb[i][0] += e[0];

    } else {
      comb[i][1] -= c;
      comb.push([comb[i][0] + e[0], c]);
    }
    result = result.concat(distribute([e[0], e[1] - c], i - 1, comb));
  }
  let comb = _comb.map(x => x.slice());
  return result.concat(distribute(e, i - 1, comb));
}

function f(arr){
  function g(i){
    if (i == 0)
      return [[arr[0]]];
    const combs = g(i - 1);
    let result = [];
    for (let comb of combs)
      result = result.concat(
        distribute(arr[i], comb.length - 1, comb));
    return result;
  }
  return g(arr.length - 1);
}

function show(arr){
  const rs = f(arr);
  const set = new Set();

  for (let r of rs){
    const _r = JSON.stringify(r);
    if (set.has(_r))
      console.log('Duplicate: ' + _r);
    set.add(_r);
  }

  let str = '';
  for (let r of set)
    str += '\n' + r
  str += '\n\n';

  console.log(JSON.stringify(arr));
  console.log(set.size + ' combinations:');
  console.log(str);
}

show([['A', 2], ['B', 2], ['C', 2], ['D', 1], ['E', 1]]);

答案 1 :(得分:1)

这会生成所需结果的所有可能排列,因此无法回答问题。


 function* combinations(combos) {
    yield combos;
    for(const [i1, group] of combos.entries()) {
       for(const [i2, group2] of combos.slice(i1 + 1).entries()) {
          if(group.some(v => group2.includes(v))) continue;
          yield* combinations([
            ...combos.filter((_, index) => index !== i1 && index !== i2), 
            [...group, ...group2]
         ]);
       }
    }
}

 console.log([...combinations([1, 2, 3, 4, 1].map(v => ([v])))]);

您可以从包含仅包含一个元素的组合的数组开始,然后遍历这些组合并合并其中的两个数组,然后递归进行。

Playground

答案 2 :(得分:1)

您可以通过枚举所有可能的组合,然后为每个组合找到置换来完成,然后过滤该元素以确保它们是唯一的。可以通过插入Set来完成此过滤。

子集函数来自@le_m(Check this answer)。

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 [];
}

function* permutations(elements) {
  if (elements.length === 1) {
    yield elements;
  } else {
    let [first, ...rest] = elements;
    for (let perm of permutations(rest)) {
      for (let i = 0; i < elements.length; i++) {
        let start = perm.slice(0, i);
        let rest = perm.slice(i);
        yield [...start, first, ...rest];
      }
    }
  }
}

const arr = ['A', 'a', 'B', 'b', 'C', 'c', 'd', 'e'];
const results = new Set();

for (let subset of subsets(arr)) {
  if (subset.length) {
    for (let permut of permutations(subset)) {
       results.add(permut.join(''));
    }
  }
}

console.log([...results]);

答案 3 :(得分:1)

您可以先对相同的元素进行分组并对其进行计数,从而得到如下表:

 1 | 2 | 3 | 4
 1 |    | 3 | 4
 1

(重复1次,重复3次和4次)

现在您可以开始并取出前四个元素,然后在第二行中取出3个,然后在最后一行中取出一个,

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

现在要获取下一个组合,只需从第一行中取出3个,然后将其他值向上移动:

 1 |   | 3 | 4
 1 |   |    | 4

现在再次取出行,您会得到

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

现在,在第一行(采用2、1)重复此模式,依此类推,再对各行重复此模式,那么您应该获得想要获得的结果。

 const counts = new Map;

 for(const value of  [1, 1, 1, 2, 3, 3, 4, 4])
   counts.set(value, (counts.get(value) || 0) + 1);

 const result = [];

 for(let combo = 0; combo < counts.size; combo++) {
   const subResult = [];
   const combos = [...counts];
   for(let i = 0; i <= combo; i++) combos[i][0] -= 1;
   subResult.push(combos.slice(0, combo).map(([_, value]) => value);
   while(combos.some(([count]) => count > 0)) {
      subResult.push(combos.filter(([count]) => count > 0).map(([_, value] => value));
   }
   result.push(subResult);
}

答案 4 :(得分:1)

从头开始实施一种算法:

  • 获取字母的频率
  • 从密钥长度开始,然后向下移至1
  • 频率图不为空
    • 将密钥添加到子结果

const decrementKey = (o, k) => o[k]--;
const isEmpty = (o) => Object.keys(o).length === 0;
const removeKeys = (o) => Object.keys(o).forEach(k => { if (o[k] < 1) delete o[k]; });
const frequencyMap = (a) => a.reduce((r, v) => Object.assign(r, { [v] : (r[v] || 0) + 1 }), {});
const cloneMap = (o) => Object.keys(o).reduce((r, k) => Object.assign(r, { [k] : o[k] }), {});

let myArray = ["A", "A", "B", "B", "C", "C", "D", "E"];
let groups = solve(myArray);

console.log(groups.map(g => g.map(a => a.join('')).join(' | ')).join('\n'));

function solve(arr) {
  let freq = frequencyMap(arr), results = [];
  for (let max = Object.keys(freq).length; max > 1; max--) {
    let result = [], clone = cloneMap(freq);
    while (!isEmpty(clone)) {
      result.push(Object.keys(clone).reduce((res, key, index) => {
        if (index < max) {
          decrementKey(clone, key);
          res.push(key);
        }
        return res;
      }, []));
      removeKeys(clone);
    }
    if (result.length > 0) results.push(result);
  }
  return results;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }