我试图写一个函数,它做以下工作:

以一个整数数组作为参数(例如[1,2,3,4]) 创建一个包含[1,2,3,4]的所有可能排列的数组,每个排列的长度为4

下面的函数(我在网上找到的)通过接受一个字符串作为参数,并返回该字符串的所有排列来实现这一点

我不知道如何修改它,使它与整数数组一起工作,(我认为这与一些方法在字符串上的工作方式不同于在整数上的工作方式有关,但我不确定…)

let permArr = [];
let usedChars = [];

function permute(input) {
    const chars = input.split("");
    for (let i = 0; i < chars.length; i++) {
        const ch = chars.splice(i, 1);
        usedChars.push(ch);
        if (chars.length === 0) {
            permArr[permArr.length] = usedChars.join("");
        }
        permute(chars.join(""));
        chars.splice(i, 0, ch);
        usedChars.pop();
    }
    return permArr
};

注意:我希望函数返回整数数组,而不是字符串数组。

我真的需要解决方案是在JavaScript。我已经知道如何在python中做到这一点


当前回答

这是一个有趣的任务这是我的贡献。它非常简单和快速。如果有兴趣,请耐心阅读。

如果你想快速完成这项工作,你肯定得让自己进入动态编程。这意味着您应该忘记递归方法。那是肯定的……

好的,le_m的代码,它使用了Heap的方法,似乎是目前为止最快的。我的算法还没有名字,我不知道它是否已经实现了,但它非常简单和快速。与所有动态规划方法一样,我们将从最简单的问题开始,然后走向最终的结果。

假设我们有一个数组a =[1,2,3],我们将从它开始

r = [[1]]; // result
t = [];    // interim result

然后遵循以下三个步骤;

对于r (result)数组的每一项,我们将添加输入数组的下一项。 我们将旋转每个项的长度多次,并将每个实例存储在中间结果数组t中(好吧,除了第一个不浪费时间旋转0)。 一旦我们处理完r的所有项,中间数组t应该保存下一层的结果,因此我们使r = t;T = [];一直到输入数组a的长度。

下面是我们的步骤;

r array   | push next item to |  get length many rotations
          |  each sub array   |       of each subarray
-----------------------------------------------------------
[[1]]     |     [[1,2]]       |     [[1,2],[2,1]]
----------|-------------------|----------------------------
[[1,2],   |     [[1,2,3],     |     [[1,2,3],[2,3,1],[3,1,2],
 [2,1]]   |      [2,1,3]]     |      [2,1,3],[1,3,2],[3,2,1]]
----------|-------------------|----------------------------
previous t|                   |
-----------------------------------------------------------

这就是代码

function perm(a){ var r = [[a[0]]], t = [], s = []; if (a.length <= 1) return a; for (var i = 1, la = a.length; i < la; i++){ for (var j = 0, lr = r.length; j < lr; j++){ r[j].push(a[i]); t.push(r[j]); for(var k = 1, lrj = r[j].length; k < lrj; k++){ for (var l = 0; l < lrj; l++) s[l] = r[j][(k+l)%lrj]; t[t.length] = s; s = []; } } r = t; t = []; } return r; } var arr = [0,1,2,4,5]; console.log("The length of the permutation is:",perm(arr).length); console.time("Permutation test"); for (var z = 0; z < 2000; z++) perm(arr); console.timeEnd("Permutation test");

在多次测试中,我已经看到它在25~35ms内解决了[0,1,2,3,4]的120个排列2000次。

其他回答

这是一个很短的解决方案,只适用于1或2个长字符串。它是一个联机程序,使用ES6而不依赖jQuery,速度非常快。享受:

var p = l => l.length<2 ? [l] : l.length==2 ? [l[0]+l[1],l[1]+l[0]] : Function('throw Error("unimplemented")')();

我认为下面的解决方案的唯一不同之处在于,我在出现空情况前一步停止了递归。希望嵌入的评论是充分的解释。

function Permutations (A) // computes all possible ordered sequences of the entries in array A and returns them as an array of arrays
{
var perms = [];

for (var i = 0 ; i < A.length ; i++)
    {
    var rem = A.slice (0); // copy input array to retain remainder of elements after removing i'th element
    var el = rem.splice (i,1);
    if (A.length == 2) {perms.push ([el [0],rem [0]])} // recursion end case
    else 
        {
        var sub = Permutations (rem); // recursive call
        for (var s = 0 ; s < sub.length ; s++) // process recursive response, adding el to the start of each returned sequence
            {
            sub [s].splice (0,0,el [0]);
            perms.push (sub [s]);
            };
        };
    };

return perms ;

};// end of Permutations function

大多数其他答案没有利用新的javascript生成器函数,这是一个完美的解决这类问题。在内存中,一次可能只需要一个排列。此外,我更喜欢生成一系列索引的排列,因为这允许我对每个排列进行索引,并直接跳转到任何特定的排列,以及用于排列任何其他集合。

// ES6 generator version of python itertools [permutations and combinations] const range = function*(l) { for (let i = 0; i < l; i+=1) yield i; } const isEmpty = arr => arr.length === 0; const permutations = function*(a) { const r = arguments[1] || []; if (isEmpty(a)) yield r; for (let i of range(a.length)) { const aa = [...a]; const rr = [...r, ...aa.splice(i, 1)]; yield* permutations(aa, rr); } } console.log('permutations of ABC'); console.log(JSON.stringify([...permutations([...'ABC'])])); const combinations = function*(a, count) { const r = arguments[2] || []; if (count) { count = count - 1; for (let i of range(a.length - count)) { const aa = a.slice(i); const rr = [...r, ...aa.splice(0, 1)]; yield* combinations(aa, count, rr); } } else { yield r; } } console.log('combinations of 2 of ABC'); console.log(JSON.stringify([...combinations([...'ABC'], 2)])); const permutator = function() { const range = function*(args) { let {begin = 0, count} = args; for (let i = begin; count; count--, i+=1) { yield i; } } const factorial = fact => fact ? fact * factorial(fact - 1) : 1; return { perm: function(n, permutationId) { const indexCount = factorial(n); permutationId = ((permutationId%indexCount)+indexCount)%indexCount; let permutation = [0]; for (const choiceCount of range({begin: 2, count: n-1})) { const choice = permutationId % choiceCount; const lastIndex = permutation.length; permutation.push(choice); permutation = permutation.map((cv, i, orig) => (cv < choice || i == lastIndex) ? cv : cv + 1 ); permutationId = Math.floor(permutationId / choiceCount); } return permutation.reverse(); }, perms: function*(n) { for (let i of range({count: factorial(n)})) { yield this.perm(n, i); } } }; }(); console.log('indexing type permutator'); let i = 0; for (let elem of permutator.perms(3)) { console.log(`${i}: ${elem}`); i+=1; } console.log(); console.log(`3: ${permutator.perm(3,3)}`);

这是一个有趣的任务这是我的贡献。它非常简单和快速。如果有兴趣,请耐心阅读。

如果你想快速完成这项工作,你肯定得让自己进入动态编程。这意味着您应该忘记递归方法。那是肯定的……

好的,le_m的代码,它使用了Heap的方法,似乎是目前为止最快的。我的算法还没有名字,我不知道它是否已经实现了,但它非常简单和快速。与所有动态规划方法一样,我们将从最简单的问题开始,然后走向最终的结果。

假设我们有一个数组a =[1,2,3],我们将从它开始

r = [[1]]; // result
t = [];    // interim result

然后遵循以下三个步骤;

对于r (result)数组的每一项,我们将添加输入数组的下一项。 我们将旋转每个项的长度多次,并将每个实例存储在中间结果数组t中(好吧,除了第一个不浪费时间旋转0)。 一旦我们处理完r的所有项,中间数组t应该保存下一层的结果,因此我们使r = t;T = [];一直到输入数组a的长度。

下面是我们的步骤;

r array   | push next item to |  get length many rotations
          |  each sub array   |       of each subarray
-----------------------------------------------------------
[[1]]     |     [[1,2]]       |     [[1,2],[2,1]]
----------|-------------------|----------------------------
[[1,2],   |     [[1,2,3],     |     [[1,2,3],[2,3,1],[3,1,2],
 [2,1]]   |      [2,1,3]]     |      [2,1,3],[1,3,2],[3,2,1]]
----------|-------------------|----------------------------
previous t|                   |
-----------------------------------------------------------

这就是代码

function perm(a){ var r = [[a[0]]], t = [], s = []; if (a.length <= 1) return a; for (var i = 1, la = a.length; i < la; i++){ for (var j = 0, lr = r.length; j < lr; j++){ r[j].push(a[i]); t.push(r[j]); for(var k = 1, lrj = r[j].length; k < lrj; k++){ for (var l = 0; l < lrj; l++) s[l] = r[j][(k+l)%lrj]; t[t.length] = s; s = []; } } r = t; t = []; } return r; } var arr = [0,1,2,4,5]; console.log("The length of the permutation is:",perm(arr).length); console.time("Permutation test"); for (var z = 0; z < 2000; z++) perm(arr); console.timeEnd("Permutation test");

在多次测试中,我已经看到它在25~35ms内解决了[0,1,2,3,4]的120个排列2000次。

  let permutations = []

  permutate([], {
    color: ['red', 'green'],
    size: ['big', 'small', 'medium'],
    type: ['saison', 'oldtimer']
  })

  function permutate (currentVals, remainingAttrs) {
    remainingAttrs[Object.keys(remainingAttrs)[0]].forEach(attrVal => {
      let currentValsNew = currentVals.slice(0)
      currentValsNew.push(attrVal)

      if (Object.keys(remainingAttrs).length > 1) {
        let remainingAttrsNew = JSON.parse(JSON.stringify(remainingAttrs))
        delete remainingAttrsNew[Object.keys(remainingAttrs)[0]]

        permutate(currentValsNew, remainingAttrsNew)
      } else {
        permutations.push(currentValsNew)
      }
    })
  }

结果:

[ 
  [ 'red', 'big', 'saison' ],
  [ 'red', 'big', 'oldtimer' ],
  [ 'red', 'small', 'saison' ],
  [ 'red', 'small', 'oldtimer' ],
  [ 'red', 'medium', 'saison' ],
  [ 'red', 'medium', 'oldtimer' ],
  [ 'green', 'big', 'saison' ],
  [ 'green', 'big', 'oldtimer' ],
  [ 'green', 'small', 'saison' ],
  [ 'green', 'small', 'oldtimer' ],
  [ 'green', 'medium', 'saison' ],
  [ 'green', 'medium', 'oldtimer' ] 
]