使用另一个数组的元素生成由一个数组定义的所有分区

Posted

技术标签:

【中文标题】使用另一个数组的元素生成由一个数组定义的所有分区【英文标题】:Generate all partitions, defined by one array, with elements of another array 【发布时间】:2015-10-20 13:13:06 【问题描述】:

我正在尝试查找数组元素的所有分区,但有一个重要的变化:

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

给定这两个数组:

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.

【问题讨论】:

数组中元素的顺序重要吗? 我不在乎订单。我希望结果是唯一的(所以如果已经存在 A=1,2,3 则 A=2,1,3 将被省略),但即使这样也不是灾难。 看看这里的代码是否对Generating all combinations of an array有帮助。或者查看一个组合数学库,比如github.com/dankogai/js-combinatorics 编辑:很遗憾,我无法将其应用于我的问题。 left 总是正好是两个数组吗?然后this 应该会有所帮助(当您不推送到A 时,只需推送到B 【参考方案1】:

任意参数的解(只要结果是可数的):

编辑:此版本避免了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 

0 1 1 的分布 i = 3 计算为:

    取第一个0作为左left[0] = A的索引,将右right[0] = 10位置值移动到设置A。 取第二个1作为左left[1] = B的索引,将右right[1] = 21的位置值移动到设置B。 取第三个1作为左left[1] = B的索引,将右right[2] = 32的位置值移动到集合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一起使用?我将修改问题以使其更清楚。 不错的答案。我们需要carry 吗?无论如何,数组的最大长度为2^32-1 @KingMob,见编辑的older version,有结果无进位。想法是将解决方案与使用 Math.pow(...) 分开。 哦,当然,我只是想知道是否需要 carry 考虑到数组的限制。 @KingMob,只是我个人的喜好。以及不将数字转换为字符串而不是直接使用数组的想法。并且您可以使用超过 36 的长度。以Number.toString(base) 为基础的转换限制为 36。【参考方案2】:

我想我想出了一种可能的解决方案,但我敢肯定它远非有效。

给定数组:

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

首先创建一个的幂集:

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

然后对 left 中的每个值都有嵌套循环。每个循环都会检查值是否已经在前一个循环中,最后一个循环也会检查所有值是否都存在。

在 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);
                
            
        
    

【讨论】:

【参考方案3】:

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

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

我会提供一个递归的解决方案,也许你可以用非递归的方式重写,虽然它不会降低算法的复杂性(也许是常数)。实际上没有办法降低复杂性,因为在某些时候您需要生成每个解决方案。所以不要对 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);

【讨论】:

【参考方案4】:

我的建议是尝试按照您的要求编写代码:

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]]]"

【讨论】:

【参考方案5】:

只是为了说明,这是@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);
&lt;pre id="out"&gt;&lt;/pre&gt;

【讨论】:

以上是关于使用另一个数组的元素生成由一个数组定义的所有分区的主要内容,如果未能解决你的问题,请参考以下文章

如何根据另一个数组的均值和标准差生成有界随机数组?

EXCEL 数组和维数

什么是一维数组

获取数组中元素的最大值最小值平均值总和

PHP中数组的定义及声明实例

如何将一个一维数组中的所有数都赋值为-1?不用循环。