繁体   English   中英

找到两个数组的所有组合

[英]Find all combinations of two arrays

我试图找到两个数组的所有组合,但有一个重要的变化:

第二个数组的每个值都需要分散在第一个数组的值上。 因此总是使用第二个数组的所有值。

鉴于这两个数组:

left = [A, B];
right = [1, 2, 3];

我期待收集以下结果:

A = [1, 2, 3]
B = []

A = [1, 2]
B = [3]

A = [1, 3]
B = [2]

A = [2, 3]
B = [1]

A = [1]
B = [2, 3]

A = [2]
B = [1, 3]

A = [3]
B = [1, 2]

A = []
B = [1, 2, 3]

编辑:

所以要清楚。 这需要针对两个阵列进行扩展。

给定数组:

left = [A, B, C, D]
right = [1, 2, 3, 4, 5, 6]

一些(许多可能的)结果将是:

A = [2, 5]
B = [1]
C = []
D = [3, 4, 6]

A = [6]
B = []
C = [1, 2, 3, 4, 5]
D = []

etc. etc. etc.

任何参数的解决方案(只要结果可数):

编辑 :此版本避免了len = Math.pow(left.length, right.length)和长度超过36(cnr!)的问题。

例:

combine(['A', 'B'], [1, 2, 3])所有可能的行都是2 ^ 3 = 8 在此示例中,分布是二进制的,但它使用更多left参数更改基数。

  distribution      included in set
i       c            A           B   
----------------------------------------
0     0 0 0     { 1, 2, 3 } {         }
1     0 0 1     { 1, 2    } {       3 }
2     0 1 0     { 1,    3 } {    2    }
3     0 1 1     { 1       } {    2, 3 }
4     1 0 0     {    2, 3 } { 1       }
5     1 0 1     {    2    } { 1,    3 }
6     1 1 0     {       3 } { 1, 2    }
7     1 1 1     {         } { 1, 2, 3 }

分布i = 30 1 1评估为:

  1. 取第0并把它作为左的索引left[0] = A和移动的右0位值right[0] = 1来设定A.
  2. 取第二个1并将其作为左左left[1] = B索引并移动右边的1的位置值right[1] = 2来设置B.
  3. 从第三个1并把它作为左的索引left[1] = B和移动的右2位值right[2] = 3设置B.

另一个例子:

combine(['A', 'B', 'C'], [1, 2])所有可能的行都是3 ^ 2 = 9。

  distribution     included in set
i      c          A        B       C
----------------------------------------
0     0 0     { 1, 2 } {      } {      }
1     0 1     { 1    } {    2 } {      }
2     0 2     { 1    } {      } {    2 }
3     1 0     {    2 } { 1    } {      }
4     1 1     {      } { 1, 2 } {      }
5     1 2     {      } { 1    } {    2 }
6     2 0     {    2 } {      } { 1    }
7     2 1     {      } {    2 } { 1    }
8     2 2     {      } {      } { 1, 2 }

 function combine(left, right) { function carry() { return c.reduceRight(function (r, _, i, o) { return r && !(o[i] = (o[i] + 1) % left.length); }, 1); } var c = Array.apply(null, { length: right.length }).map(function () { return 0; }), result = []; do { result.push(c.reduce(function (r, a, i) { r[left[a]].push(right[i]); return r; }, left.reduce(function (r, a) { r[a] = []; return r; }, {}))); } while (!carry()); return result; } document.getElementById('out').innerHTML = "combine(['A', 'B'], [1, 2, 3]) = " + JSON.stringify(combine(['A', 'B'], [1, 2, 3]), null, 4) + '\\n'+ "combine(['A', 'B', 'C'], [1, 2] = " + JSON.stringify(combine(['A', 'B', 'C'], [1, 2]), null, 4) +'\\n'+ "combine(['A', 'B', 'C'], [1, 2, 3] = " + JSON.stringify(combine(['A', 'B', 'C'], [1, 2, 3]), null, 4) +'\\n'+ "combine(['A', 'B', 'C', 'D'], [1, 2, 3, 4, 5, 6]) = " + JSON.stringify(combine(['A', 'B', 'C', 'D'], [1, 2, 3, 4, 5, 6]), null, 4); 
 <pre id="out"></pre> 

我想我想出了一个可能的解决方案,但我确信它远没有效率。

鉴于阵列:

left = [A, B, C]
right = [1, 2, 3]

首先创建一个正确的权力集:

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

然后为左边的每个值嵌套循环。 每个循环将检查该值是否已经在前一个循环中,并且最后一个循环还将检查是否所有值都存在。

在psuedo中,这看起来像这样:

for x in powerset
    a = x
    for y in powerset
        if y not in x
            b = y
            for z in powerset
                if z not in y and z not in x and [x + y + z] = right
                    c = z
                    displayresult

编辑

这是javascript中这种糟糕的低效解决方案。 发布完成后。

https://jsfiddle.net/6o03d3L3/

function loop(left, right, powerSet, depth, siblings) {
    for (var i=0; i<powerSet.length; i++) {
        var values = powerSet[i];

        var isValueUsed = false;
        for (var k = 0; k < values.length; k++) {
            for (var l = 0; l < siblings.length; l++) {
                for (var m = 0; m < siblings[l].right.length; m++) {
                    if (values[k] === siblings[l].right[m]) {
                        isValueUsed = true;
                        break;
                    }
                }

                if (isValueUsed) {
                    break;
                }
            }

            if (isValueUsed) {
                break;
            }
        }

        if (!isValueUsed) {
            var result = { };
            result.left = left[depth];
            result.right = values;

            var results = siblings.slice();
            results.push(result);

            if (depth < left.length - 1) {
                loop(left, right, powerSet, depth + 1, results);
            } else {
                var valueCount = 0;
                for (var j = 0; j < results.length; j++) {
                    valueCount += results[j].right.length;
                }

                if (valueCount == right.length) {
                    log(results);
                }
            }
        }
    }
}

我的建议是尝试准确编码您要求的内容:

function clone(arr) {
  var copy = new Array(arr.length);
  for (var i=0; i<arr.length; i++){
    copy[i] = new Array();
    for (var j=0; j<arr[i].length; j++)
      copy[i][j] = arr[i][j];
  }

  return copy;
}

function f(left,right){
  var result = [];

  function _f(left,i){
    if (i == right.length){
      result.push(left);
      return;
    }

    for (var j=0; j<left.length; j++){
      var _left = clone(left);
      _left[j].push(right[i]);
      _f(_left,i + 1);
    }
  }

  _f(left,0);
  return result;
}

console.log(JSON.stringify(f([[],[]],[1,2,3])));
console.log(JSON.stringify(f([[],[],[]],[1,2,3])));

输出:

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

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

我相信,你的问题可以这样改写:在的元素生成标签的所有可能的分配。 这是一个标准的回溯问题。

解决方案的数量是l ^ r (因为您可以独立地为每个元素分配任何标签),其中l左边的元素数, r右边的元素数。

我将提供一个递归解决方案,也许你可以用非递归方式重写,虽然它不会降低算法的复杂性(可能是常量)。 实际上没有办法降低复杂性,因为在某些时候您需要生成每个解决方案。 所以不要测试l = 20r = 20 ,尝试较小的数字:p

// sol keeps the labels assigned to the elements in right
public static int[] sol = new int[r];

public static void generate(int p)
{
    for (int i=0;i<l;i++)
    {
        sol[p] = i;
        if (p==r-1)
        {
            // a solution is ready
            outputSolution(sol);
        }
        else
        {
            // still generating a solution
            generate(p+1);
        }
    }
}

// from where you need the results
generate(0);

仅仅是为了说明,这是@NinaScholz的答案的一个版本,它不使用toString进行基本转换或手动实现计数。 我保持代码结构相同。 values.length-i-1可能只是“我”,但我也希望保持输出顺序相同。

 var combine = function(names, values){ var result = [], max = Math.pow(names.length, values.length), m; for(m=0; m<max; m+=1){ result.push(values.reduce(function(buckets, v, i){ var nidx = Math.floor(m / Math.pow(names.length, values.length-i-1)) % names.length; buckets[names[nidx]].push(v); return buckets; }, names.reduce(function(a,v){ a[v] = []; return a; }, {}))); } return result; }; document.getElementById('out').innerHTML = JSON.stringify( combine(Array.apply(null, Array(50)).map(function(_,i){ return i; }), [1,2]), null, 4); 
 <pre id="out"></pre> 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM