Permutations in JavaScript?

前端 未结 30 2641
不思量自难忘°
不思量自难忘° 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:25

    This is a very nice use-case for map/reduce:

    function permutations(arr) {
        return (arr.length === 1) ? arr :
        arr.reduce((acc, cv, index) => {
            let remaining = [...arr];
            remaining.splice(index, 1);
            return acc.concat(permutations(remaining).map(a => [].concat(cv,a)));
        }, []);
    }
    
    • First, we handle the base case and simply return the array if there is only on item in it
    • In all other cases
      • we create an empty array
      • loop over the input-array
      • and add an array of the current value and all permutations of the remaining array [].concat(cv,a)
    0 讨论(0)
  • 2020-11-21 07:27
    function nPr(xs, r) {
        if (!r) return [];
        return xs.reduce(function(memo, cur, i) {
            var others  = xs.slice(0,i).concat(xs.slice(i+1)),
                perms   = nPr(others, r-1),
                newElms = !perms.length ? [[cur]] :
                          perms.map(function(perm) { return [cur].concat(perm) });
            return memo.concat(newElms);
        }, []);
    }
    
    0 讨论(0)
  • 2020-11-21 07:28

    I had a crack at making a version of this that attempts to be concise yet readable, and purely functional programming.

    function stringPermutations ([...input]) {
      if (input.length === 1) return input;
    
      return input
        .map((thisChar, index) => {
          const remainingChars = [...input.slice(0, index), ...input.slice(index + 1)];
          return stringPermutations(remainingChars)
            .map(remainder => thisChar + remainder);
        })
        .reduce((acc, cur) => [...acc, ...cur]);
    }
    

    Note that the argument formatting turns an input string into an array. Not sure if that's a bit too magical.. Not sure I've seen it in the wild. For real readability I'd probably instead do input = [...input] for the first line of the function.

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

    This is an implementation of Heap's algorithm (similar to @le_m's), except it's recursive.

    function permute_kingzee(arr,n=arr.length,out=[]) {
        if(n == 1) {
            return out.push(arr.slice());
        } else {
            for(let i=0; i<n; i++) {
                permute_kingzee(arr,n-1, out);
                let j = ( n % 2 == 0 ) ? i : 0;
                let t = arr[n-1];
                arr[n-1] = arr[j];
                arr[j] = t;
            }
            return out;
        }
    }
    

    It looks like it's quite faster too : https://jsfiddle.net/3brqzaLe/

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

    If you notice, the code actually splits the chars into an array prior to do any permutation, so you simply remove the join and split operation

    var permArr = [],
      usedChars = [];
    
    function permute(input) {
      var i, ch;
      for (i = 0; i < input.length; i++) {
        ch = input.splice(i, 1)[0];
        usedChars.push(ch);
        if (input.length == 0) {
          permArr.push(usedChars.slice());
        }
        permute(input);
        input.splice(i, 0, ch);
        usedChars.pop();
      }
      return permArr
    };
    
    
    document.write(JSON.stringify(permute([5, 3, 7, 1])));

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

    Most of the other answers do not utilize the new javascript generator functions which is a perfect solution to this type of problem. You probably only need one permutation at time in memory. Also, I prefer to generate a permutation of a range of indices as this allows me to index each permutation and jump straight to any particular permutation as well as be used to permutate any other collection.

    // 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)}`);

    0 讨论(0)
提交回复
热议问题