JavaScript - 从具有 m 个元素的 n 个数组生成组合

IT技术 javascript permutation combinations
2021-01-24 19:02:42

在 JavaScript 中,我无法编写代码来从 n 个数组和 m 个元素生成组合。我已经看到其他语言的类似问题,但答案包含了我不确定如何翻译的句法或库魔法。

考虑这个数据:

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

3 个数组,其中包含不同数量的元素。我想要做的是通过组合每个数组中的一个项目来获得所有组合。

例如:

0,0,0 // item 0 from array 0, item 0 from array 1, item 0 from array 2
0,0,1
0,0,2
0,1,0
0,1,1
0,1,2
0,2,0
0,2,1
0,2,2

等等。

如果数组的数量是固定的,则很容易进行硬编码实现。但数组的数量可能会有所不同:

[[0,1], [0,1]]
[[0,1,3,4], [0,1], [0], [0,1]]

任何帮助将非常感激。

6个回答

这是一个使用递归辅助函数的非常简单和简短的方法:

function cartesian(...args) {
    var r = [], max = args.length-1;
    function helper(arr, i) {
        for (var j=0, l=args[i].length; j<l; j++) {
            var a = arr.slice(0); // clone arr
            a.push(args[i][j]);
            if (i==max)
                r.push(a);
            else
                helper(a, i+1);
        }
    }
    helper([], 0);
    return r;
}

用法:

cartesian([0,1], [0,1,2,3], [0,1,2]);

要使函数采用数组数组,只需将签名更改为 ,function cartesian(args)而不是使用其余参数语法。

这与我最初采用的方法相似,但无法到达那里......刚出生的婴儿有点睡眠不足,但很高兴有人这样做了,所以我可以看到!!
2021-03-30 19:02:42
太好了,谢谢。基准测试可在此处获得:jsfiddle.net/9uvfP您的解决方案需要 0.14 秒才能运行 100,000 次,这是迄今为止提交的最快的实现。:)
2021-04-02 19:02:42
尽管小提琴基准@Neob91 的答案对我来说是最快的,但这个 jsperf 似乎表明这个答案是最快的:jsperf.com/array-combos
2021-04-07 19:02:42
啊,我注意到基准测试中有错误。在这里更新:jsfiddle.net/2xt5F大约需要 0.6 秒。
2021-04-08 19:02:42
看来,我要成为你的粉丝了。你是天才。
2021-04-08 19:02:42

您可以通过构建子数组来采用迭代方法。

var parts = [[0, 1], [0, 1, 2, 3], [0, 1, 2]],
    result = parts.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));

console.log(result.map(a => a.join(', ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

虽然似乎有一场将多行塞进一行(缩小)的竞争,但这段代码无疑非常优雅。
2021-03-19 19:02:42
当零件是[[0, 1], [0, 1, 2, 3], [[0], [1], [2]]]..时怎么样
2021-03-24 19:02:42

我建议一个简单的递归生成器函数

// Generate all combinations of array elements:
function* cartesian(head, ...tail) {
  let remainder = tail.length ? cartesian(...tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}


// Example:
for (let c of cartesian([0,1], [0,1,2,3], [0,1,2])) {
  console.log(...c);
}

这在有大量组合时特别有用。无需同时实现它们。
2021-03-30 19:02:42

在做了一些研究之后,我发现了一个以前的相关问题: Finding All Combinations of JavaScript array values

我从那里改编了一些代码,以便它返回一个包含所有排列的数组数组:

function(arraysToCombine) {
    var divisors = [];
    for (var i = arraysToCombine.length - 1; i >= 0; i--) {
       divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
    }

    function getPermutation(n, arraysToCombine) {
       var result = [], 
           curArray;    
       for (var i = 0; i < arraysToCombine.length; i++) {
          curArray = arraysToCombine[i];
          result.push(curArray[Math.floor(n / divisors[i]) % curArray.length]);
       }    
       return result;
    }

    var numPerms = arraysToCombine[0].length;
    for(var i = 1; i < arraysToCombine.length; i++) {
        numPerms *= arraysToCombine[i].length;
    }

    var combinations = [];
    for(var i = 0; i < numPerms; i++) {
        combinations.push(getPermutation(i, arraysToCombine));
    }
    return combinations;
}

我在http://jsfiddle.net/7EakX/放了一份工作副本,它采用您之前提供的数组 ([[0,1], [0,1,2,3], [0,1,2] ]) 并将结果输出到浏览器控制台。

效果很好。我做了一个基准测试:jsfiddle.net/kLfq9您的解决方案在我的计算机上的 Chrome 中运行 100,000 次大约需要 0.5 秒。
2021-03-14 19:02:42

只是为了好玩,这是我的第一个答案中解决方案的一个更实用的变体:

function cartesian() {
    var r = [], args = Array.from(arguments);
    args.reduceRight(function(cont, factor, i) {
        return function(arr) {
            for (var j=0, l=factor.length; j<l; j++) {
                var a = arr.slice(); // clone arr
                a[i] = factor[j];
                cont(a);
            }
        };
    }, Array.prototype.push.bind(r))(new Array(args.length));
    return r;
}

或者,为了全速,我们可以动态编译我们自己的循环:

function cartesian() {
    return (cartesian.cache[arguments.length] || cartesian.compile(arguments.length)).apply(null, arguments);
}
cartesian.cache = [];
cartesian.compile = function compile(n) {
    var args = [],
        indent = "",
        up = "",
        down = "";
    for (var i=0; i<n; i++) {
        var arr = "$"+String.fromCharCode(97+i),
            ind = String.fromCharCode(105+i);
        args.push(arr);
        up += indent+"for (var "+ind+"=0, l"+arr+"="+arr+".length; "+ind+"<l"+arr+"; "+ind+"++) {\n";
        down = indent+"}\n"+down;
        indent += "  ";
        up += indent+"arr["+i+"] = "+arr+"["+ind+"];\n";
    }
    var body = "var res=[],\n    arr=[];\n"+up+indent+"res.push(arr.slice());\n"+down+"return res;";
    return cartesian.cache[n] = new Function(args, body);
}
杰出的!谢谢@Bergi 这个“全速”运行良好(我还没有测试过另一个)
2021-04-04 19:02:42