Permutations in JavaScript?

前端 未结 30 2515
不思量自难忘°
不思量自难忘° 2020-11-21 06:52

I\'m trying to write a function that does the following:

  • takes an array of integers as an argument (e.g. [1,2,3,4])
  • creates an array of all the possib
相关标签:
30条回答
  • 2020-11-21 07:15

    This is an interesting task and and here is my contribution. It's very simple and fast. If interested please bear with me and read on.

    If you would like to this job fast, you definitely have to get yourself into dynamical programming. Which means you should forget about recursive approaches. That's for sure...

    OK le_m's code which uses the Heap's method seems to be the fastest so far. Well i haven't got a name for my algorithm, i don't know if it's already been implemented or not but it's very simple and fast. As with all dynamical programming approaches we will start with the simplest problem and go for the final result.

    Assuming that we have an array of a = [1,2,3] we will start with

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

    Then follow these three steps;

    1. For each item of our r (result) array we will add the next item of the input array.
    2. We will rotate each item it's length many times and will store each instance at the interim result array t. (well except for the first one not to waste time with 0 rotation)
    3. Once we finish with all items of r the interim array t should hold the next level of results so we make r = t; t = []; and carry on up until the length of the input array a.

    So the following are our steps;

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

    So here is the code

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

    In multiple test i have seen it resolving the 120 permutations of [0,1,2,3,4] for 2000 times in 25~35ms.

    0 讨论(0)
  • 2020-11-21 07:16

    Some version inspired from Haskell:

    perms [] = [[]]
    perms xs = [ x:ps | x <- xs , ps <- perms ( xs\\[x] ) ]
    

    function perms(xs) {
      if (!xs.length) return [[]];
      return xs.flatMap(x => {
        // get permutations of xs without x, then prepend x to each
        return perms(xs.filter(v => v!==x)).map(vs => [x, ...vs]);
      });
    }
    document.write(JSON.stringify(perms([1,2,3])));

    0 讨论(0)
  • 2020-11-21 07:17

    Fastest, most (resorces) effective and most elegant version nowadays (2020)

    function getArrayMutations (arr, perms = [], len = arr.length) {
      if (len === 1) perms.push(arr.slice(0))
    
      for (let i = 0; i < len; i++) {
        getArrayMutations(arr, perms, len - 1)
    
        len % 2 // parity dependent adjacent elements swap
          ? [arr[0], arr[len - 1]] = [arr[len - 1], arr[0]]
          : [arr[i], arr[len - 1]] = [arr[len - 1], arr[i]]
      }
    
      return perms
    }
    
    const arrayToMutate = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    const startTime = performance.now()
    const arrayOfMutations = getArrayMutations(arrayToMutate)
    const stopTime = performance.now()
    const duration = (stopTime - startTime) / 1000
    
    console.log(`${arrayOfMutations.length.toLocaleString('en-US')} permutations found in ${duration.toLocaleString('en-US')}s`)

    0 讨论(0)
  • 2020-11-21 07:17

    Here is another "more recursive" solution.

    function perms(input) {
      var data = input.slice();
      var permutations = [];
      var n = data.length;
    
      if (n === 0) {
        return [
          []
        ];
      } else {
        var first = data.shift();
        var words = perms(data);
        words.forEach(function(word) {
          for (var i = 0; i < n; ++i) {
            var tmp = word.slice();
            tmp.splice(i, 0, first)
            permutations.push(tmp);
          }
        });
      }
    
      return permutations;
    }
    
    var str = 'ABC';
    var chars = str.split('');
    var result = perms(chars).map(function(p) {
      return p.join('');
    });
    
    console.log(result);

    Output:

    [ 'ABC', 'BAC', 'BCA', 'ACB', 'CAB', 'CBA' ]
    
    0 讨论(0)
  • 2020-11-21 07:19

    const permutations = array => {
      let permut = [];
      helperFunction(0, array, permut);
      return permut;
    };
    
    const helperFunction = (i, array, permut) => {
      if (i === array.length - 1) {
        permut.push(array.slice());
      } else {
        for (let j = i; j < array.length; j++) {
          swapElements(i, j, array);
          helperFunction(i + 1, array, permut);
          swapElements(i, j, array);
        }
      }
    };
    
    function swapElements(a, b, array) {
      let temp = array[a];
      array[a] = array[b];
      array[b] = temp;
    }
    
    console.log(permutations([1, 2, 3]));

    0 讨论(0)
  • 2020-11-21 07:22

    Most answers to this question use expensive operations like continuous insertions and deletions of items in an array, or copying arrays reiteratively.

    Instead, this is the typical backtracking solution:

    function permute(arr) {
      var results = [],
          l = arr.length,
          used = Array(l), // Array of bools. Keeps track of used items
          data = Array(l); // Stores items of the current permutation
      (function backtracking(pos) {
        if(pos == l) return results.push(data.slice());
        for(var i=0; i<l; ++i) if(!used[i]) { // Iterate unused items
          used[i] = true;      // Mark item as used
          data[pos] = arr[i];  // Assign item at the current position
          backtracking(pos+1); // Recursive call
          used[i] = false;     // Mark item as not used
        }
      })(0);
      return results;
    }
    
    permute([1,2,3,4]); // [  [1,2,3,4], [1,2,4,3], /* ... , */ [4,3,2,1]  ]
    

    Since the results array will be huge, it might be a good idea to iterate the results one by one instead of allocating all the data simultaneously. In ES6, this can be done with generators:

    function permute(arr) {
      var l = arr.length,
          used = Array(l),
          data = Array(l);
      return function* backtracking(pos) {
        if(pos == l) yield data.slice();
        else for(var i=0; i<l; ++i) if(!used[i]) {
          used[i] = true;
          data[pos] = arr[i];
          yield* backtracking(pos+1);
          used[i] = false;
        }
      }(0);
    }
    
    var p = permute([1,2,3,4]);
    p.next(); // {value: [1,2,3,4], done: false}
    p.next(); // {value: [1,2,4,3], done: false}
    // ...
    p.next(); // {value: [4,3,2,1], done: false}
    p.next(); // {value: undefined, done: true}
    
    0 讨论(0)
提交回复
热议问题