Find all combinations of two arrays

前端 未结 5 681
长情又很酷
长情又很酷 2021-02-15 17:02

I am trying to find all the combination of two arrays, but with an important variation:

Each value of the second array needs to be spread out over the val

相关标签:
5条回答
  • 2021-02-15 17:33

    I believe that your problem can be rephrased like this: generate all possible assignments of labels in left to the elements in right. This is a standard backtracking problem.

    The number of solutions is l^r (since you can assign any of the labels to each element, independently), where l is the number of elements in left and r is the number of elements in right.

    I'll provide a recursive solution, which is maybe you can rewrite in a non-recursive way, though it will not lower the complexity of the algorithm (maybe the constant). There is actually no way to lower the complexity since at some point you need to generate each solution. So do not test it for l=20 and r=20, try smaller numbers :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);
    
    0 讨论(0)
  • 2021-02-15 17:36

    I think I came up with one possible solution, but I’m certain it’s far from efficiënt.

    Given the arrays:

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

    First create a power set of right:

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

    Then have nested loops through this for each value in left. Each loop will check if the value is not already in the previous loop and the last loop will also check if all values are present.

    In psuedo this would look something like this:

    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
    

    Edit

    Here's this crappy inefficient solution in javascript. Posting it for completion sake.

    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);
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-15 17:36

    Just for illustration, this is a version of @NinaScholz's answer that doesn't use toString for base conversion or implement counting manually. I've kept the structure of the code the same. values.length-i-1 could simply be `i', but I wanted to keep the order of the output the same too.

    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>

    0 讨论(0)
  • 2021-02-15 17:45

    Solution for any parameter (as long as the result is countable):

    Edit: This version avoids possible problems with len = Math.pow(left.length, right.length) and problems with length over 36 (cnr!).

    Example:

    combine(['A', 'B'], [1, 2, 3]) all possible rows are 2^3 = 8. In this example, the distribution is binary, but it changes the base with more parameters of 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 }
    

    The distribution i = 3 of 0 1 1 evaluates as:

    1. Take the first 0 and take it as index of left left[0] = A and move the place value of 0 of right right[0] = 1 to set A.
    2. Take the second 1 and take it as index of left left[1] = B and move the place value of 1 of right right[1] = 2 to set B.
    3. Take the third 1 and take it as index of left left[1] = B and move the place value of 2 of right right[2] = 3 to set B.

    Another example:

    combine(['A', 'B', 'C'], [1, 2]) all possible rows are 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>

    0 讨论(0)
  • 2021-02-15 17:46

    My suggestion is to try and code exactly what you are asking for:

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

    Output:

    "[[[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]]]"
    
    0 讨论(0)
提交回复
热议问题