【问题标题】:Generate permutations of multi array生成多数组的排列
【发布时间】:2018-05-20 11:24:38
【问题描述】:

我想在 javascript(或算法)中生成多数组中元素的所有排列:

输入:

[
  ['a', 'b', 'c', 'd'],
  ['e', 'f', 'g'],
  ['h', 'i']
]

输出:

[
  ['a', 'e', 'h'],
  ['a', 'e', 'i'],
  ['a', 'f', 'h'],
  ...
  ['d', 'g', 'i']
]

注意:我不想要 ['a', 'b', 'c', 'd', 'e', 'f', 'g', ' h', 'i'] 因为我不想要这样的结果:['a', 'b', 'c']。

注意2:我只对支持N维数组输入的解决方案感兴趣。

谢谢!

【问题讨论】:

标签: javascript algorithm permutation


【解决方案1】:

如果你喜欢函数式编程,你可以在数组类型上使用lift。就我而言,为了简单起见,我使用了RamdaJS:

const input = [
  ['a', 'b', 'c', 'd'],
  ['e', 'f', 'g'],
  ['h', 'i']
]

const output = R.lift ( ( x, y, z ) => [ x, y, z ] ) ( ...input )

console.log( output )
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

这是一个普通的 JavaScript lift3 实现:

const input = [
  ['a', 'b', 'c', 'd'],
  ['e', 'f', 'g'],
  ['h', 'i']
]

const flatten = array => [].concat.apply ( [], array )

// Array#flatMap please! ;-)
const ap = funs => array => flatten ( funs.map ( f => array.map ( f ) ) )

const lift3 = f => array1 => array2 => array3 => 
                ap ( ap ( array1.map ( f ) ) ( array2 ) ) ( array3 )

const output = lift3 ( x => y => z => [ x, y, z ] ) ( input[0] ) ( input[1] ) ( input[2] )

console.log( output )

【讨论】:

  • 是否需要指定长度并为其取n参数(以及对应的数组)?
  • @NinaScholz 不,我已经切换到R.lift,它会为您推断出这一点。顺便说一句,我更喜欢固定的数量函数(即lift3)
【解决方案2】:
 function mutate(array) {
   if(array.length === 1)
      return array[0].map(el => [el]);
   const mutations = mutate(array.slice(1));
   const result = [];
   for(const el of array[0])
     for(const rest of mutations)
       result.push([el, ...rest]);
   return result;
}

这是一种递归方法。

【讨论】:

    【解决方案3】:

    您似乎感兴趣的是集合的笛卡尔积。输出的长度将是集合长度的乘积,我们可以通过以下方法得出任意输出列表中每个元素的列表索引:

    // i is the zero-based ouput-list index
    // p is the product of input set lengths
    function unrank(i, input, p){
      let output_list = new Array(input.length);
    
      for (let k=0; k<input.length; k++){
        p = Math.floor(p / input[k].length);
        let m = Math.floor(i / p);
        i = i - m * p;
        output_list[k] = input[k][m];
      }
      
      return output_list;
    }
    
    var input = [
      ['a', 'b', 'c', 'd'],
      ['e', 'f', 'g'],
      ['h', 'i']
    ];
    
    // Output
    var prod = input.map(x => x.length).reduce((a, b) => a * b);
    
    console.log(unrank(23, input, prod));
    console.log(unrank(11, input, prod));

    要将它们全部列出,请将索引从 0 循环到(输入集长度的乘积 - 1)。

    【讨论】:

      【解决方案4】:

      您可以使用迭代和递归方法。

      var d = [['a', 'b', 'c', 'd'], ['e', 'f', 'g'], ['h', 'i']],
          processor = array => {
               var result = [],
                   iter = p => p.length < array.length
                      ? array[p.length].forEach(v => iter(p.concat(v)))
                      : result.push(p);
      
              iter([]);
              return result;
          };
      
      console.log(processor(d).map(a => a.join(' ')));
      .as-console-wrapper { max-height: 100% !important; top: 0; }

      更短的方法

      var d = [['a', 'b', 'c', 'd'], ['e', 'f', 'g'], ['h', 'i']],
          result = d.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; }

      【讨论】:

        猜你喜欢
        • 2014-12-06
        • 1970-01-01
        • 2016-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-13
        相关资源
        最近更新 更多