JavaScript-从n个数组和m个元素生成组合

JavaScript-从n个数组和m个元素生成组合,javascript,permutation,combinations,Javascript,Permutation,Combinations,在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

在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]]
任何帮助都将不胜感激。

var f=function(arr){
var f = function(arr){
    if(typeof arr !== 'object'){
        return false;
    }

    arr = arr.filter(function(elem){ return (elem !== null); }); // remove empty elements - make sure length is correct
    var len = arr.length;

    var nextPerm = function(){ // increase the counter(s)
        var i = 0;

        while(i < len)
        {
            arr[i].counter++;

            if(arr[i].counter >= arr[i].length){
                arr[i].counter = 0;
                i++;
            }else{
                return false;
            }
        }

        return true;
    };

    var getPerm = function(){ // get the current permutation
        var perm_arr = [];

        for(var i = 0; i < len; i++)
        {
            perm_arr.push(arr[i][arr[i].counter]);
        }

        return perm_arr;
    };

    var new_arr = [];

    for(var i = 0; i < len; i++) // set up a counter property inside the arrays
    {
        arr[i].counter = 0;
    }

    while(true)
    {
        new_arr.push(getPerm()); // add current permutation to the new array

        if(nextPerm() === true){ // get next permutation, if returns true, we got them all
            break;
        }
    }

    return new_arr;
};
if(arr的类型!=='object'){ 返回false; } arr=arr.filter(函数(elem){return(elem!==null);});//删除空元素-确保长度正确 var len=阵列长度; var nextPerm=function(){//增加计数器 var i=0; 而(我=arr[i]。长度){ arr[i]。计数器=0; i++; }否则{ 返回false; } } 返回true; }; var getPerm=function(){//获取当前排列 var perm_arr=[]; 对于(变量i=0;i
在做了一点研究之后,我发现了一个以前的相关问题:

我对其中的一些代码进行了修改,以便它返回一个包含所有排列的数组:

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;
}
函数(arraysToCombine){
var除数=[];
对于(var i=arraysToCombine.length-1;i>=0;i--){
除数[i]=除数[i+1]?除数[i+1]*阵列组合[i+1]。长度:1;
}
函数getPermutation(n,arraysToCombine){
var结果=[],
库拉里;
对于(变量i=0;i

我在上放置了一个工作副本,该副本将获取您先前提供的数组([[0,1],[0,1,2,3],[0,1,2]]),并将结果输出到浏览器控制台。

还有另一种方法。我将所有数组的索引视为一个数字,其数字都是不同的基数(如时间和日期),使用数组的长度作为基数

因此,使用第一组数据,第一个数字是基数2,第二个是基数4,第三个是基数3。计数器从000开始,然后转到001、002、010。这些数字对应于数组中的索引,由于保留了顺序,所以这没有问题

我在这里工作时有一把小提琴:

代码如下:

// Arbitrary base x number class 
var BaseX = function(initRadix){
    this.radix     = initRadix ? initRadix : 1;    
    this.value     = 0;
    this.increment = function(){
        return( (this.value = (this.value + 1) % this.radix) === 0);
    }
}

function combinations(input){
    var output    = [],    // Array containing the resulting combinations
        counters  = [],    // Array of counters corresponding to our input arrays
        remainder = false, // Did adding one cause the previous digit to rollover?
        temp;              // Holds one combination to be pushed into the output array

    // Initialize the counters
    for( var i = input.length-1; i >= 0; i-- ){
        counters.unshift(new BaseX(input[i].length));
    }

    // Get all possible combinations
    // Loop through until the first counter rolls over
    while( !remainder ){
        temp      = [];   // Reset the temporary value collection array
        remainder = true; // Always increment the last array counter

        // Process each of the arrays
        for( i = input.length-1; i >= 0; i-- ){
            temp.unshift(input[i][counters[i].value]); // Add this array's value to the result

            // If the counter to the right rolled over, increment this one.
            if( remainder ){
                remainder = counters[i].increment();
            }
        }
        output.push(temp); // Collect the results.
    }

    return output;
}

// Input is an array of arrays
console.log(combinations([[0,1], [0,1,2,3], [0,1,2]]));

下面是一个使用递归辅助函数的非常简单和简短的示例:

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

要使函数采用数组数组,只需将签名更改为
函数笛卡尔(args)
,而不是使用rest参数语法。

为了好玩,我的第一个答案中有一个更实用的解决方案:

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;
}
笛卡尔函数(){
var r=[],args=Array.from(参数);
参数reduceRight(函数(续,因子,i){
返回函数(arr){

对于(var j=0,l=factor.length;j我建议一个简单的递归:

//生成数组元素的所有组合:
函数*笛卡尔(头,…尾){
让余数=tail.length?笛卡尔(…tail):[[]];
对于(剩余部分的设r)对于(头部的设h)屈服[h,…r];
}
//例如:
对于(笛卡尔([0,1],[0,1,2,3],[0,1,2]),设c){
console.log(…c);

}
另一个ES6递归风格的实现

Array.prototype.cartesian=函数(a,…as){
返回a?this.reduce((p,c)=>(p.push(…a.cartesian(…as).map(e=>as.length?[c,…e]:[c,e])),p),[])
:这个;
};

log(JSON.stringify([0,1].cartesian([0,1,2,3],[0],[1],[2]]);
您可以通过构建子数组来采用迭代方法

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

。作为控制台包装{max height:100%!important;top:0;}
您可以使用递归函数获取所有组合

const字符集=[[“A”、“B”]、[“C”、“D”、“E”]、[“F”、“G”、“H”、“I”];
let loopOver=(arr,str='',final=[])=>{
如果(arr.length>1){
arr[0].forEach(v=>loopOver(arr.slice(1),str+v,final))
}否则{
arr[0].forEach(v=>final.push(str+v))
}
返回决赛
}
console.log(loopOver(charSet))
const字符集=[[“A”、“B”]、[“C”、“D”、“E”]、[“F”、“G”、“H”、“I”];

console.log(charSet.reduce((a,b)=>a.flatMap(x=>b.map(y=>x+y)),['')
工作得很好。我做了一个基准测试:。您的解决方案在我的计算机上用Chrome运行100000次大约需要0.5秒。感谢您的解决方案。这里提供了基准测试:。它运行您的函数
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);
}