31

Requirement: Algorithm to generate all possible combinations of a set , without duplicates , or recursively calling function to return results.

The majority , if not all of the Answers provided at Permutations in JavaScript? recursively call a function from within a loop or other function to return results.

Example of recursive function call within loop

function p(a, b, res) {
  var b = b || [], res = res || [], len = a.length;
  if (!len) 
    res.push(b)
  else 
    for (var i = 0; i < len 
         // recursive call to `p` here
       ; p(a.slice(0, i).concat(a.slice(i + 1, len)), b.concat(a[i]), res)
       , i++
    );
  return res
}

p(["a", "b", "c"]);

The current Question attempts to create the given permutation in a linear process , relying on the previous permutation.

For example , given an array

var arr = ["a", "b", "c"];

to determine the total number of possible permutations

for (var len = 1, i = k = arr.length; len < i ; k *= len++);

k should return 6 , or total number of possible permutations of arr ["a", "b", "c"]

With the total number of individual permutations determined for a set , the resulting array which would contain all six permutations could be created and filled using Array.prototype.slice() , Array.prototype.concat() and Array.prototype.reverse()

var res = new Array(new Array(k));

res[0] = arr;

res[1] = res[0].slice(0,1).concat(res[0].slice(-2).reverse());

res[2] = res[1].slice(-1).concat(res[1].slice(0,2));

res[3] = res[2].slice(0,1).concat(res[2].slice(-2).reverse());

res[4] = res[3].slice(-2).concat(res[3].slice(0,1));

res[5] = res[4].slice(0,1).concat(res[4].slice(-2).reverse());

Attempted to reproduce results based on the pattern displayed at the graph for An Ordered Lexicographic Permutation Algorithm based on one published in Practical Algorithms in C++ at Calculating Permutations and Job Interview Questions .

There appears to be a pattern that could be extended if the input set was , for example

["a", "b", "c", "d", "e"]

where 120 permutations would be expected.

An example of an attempt at filling array relying only on previous permutation

// returns duplicate entries at `j`
var arr = ["a", "b", "c", "d", "e"], j = [];
var i = k = arr.length;
arr.forEach(function(a, b, array) {
 if (b > 1) {
  k *= b;
  if (b === i -1) {
    for (var q = 0;j.length < k;q++) {
      if (q === 0) {
       j[q] = array;
      } else {
       j[q] = !(q % i) 
              ? array.slice(q % i).reverse().concat(array.slice(0, q % i)) 
              : array.slice(q % i).concat(array.slice(0, q % i));
      }
    }
  }
 }
})

however have not yet been able to make the necessary adjustments at parameters for .slice() , .concat() , .reverse() at above js to step from one permutation to the next ; while only using the previous array entry within res to determine current permutation , without using recursive.

Noticed even , odd balance of calls and tried to use modulus % operator and input array .length to either call .reverse() or not at ["a", "b", "c", "d", "e"] array , though did not produce results without duplicate entries.

The expected result is that the above pattern could be reduced to two lines called in succession for the duration of the process until all permutations completed, res filled ; one each for call to .reverse() , call without .reverse() ; e.g., after res[0] filled

// odd , how to adjust `.slice()` , `.concat()` parameters 
// for array of unknown `n` `.length` ?
res[i] = res[i - 1].slice(0,1).concat(res[i - 1].slice(-2).reverse());
// even    
res[i] = res[1 - 1].slice(-1).concat(res[i - 1].slice(0,2));

Question: What adjustments to above pattern are necessary , in particular parameters , or index , passed .slice() , .concat() to produce all possible permutations of a given set without using a recursive call to the currently processing function ?

var arr = ["a", "b", "c"];

for (var len = 1, i = k = arr.length; len < i; k *= len++);

var res = new Array(new Array(k));

res[0] = arr;

res[1] = res[0].slice(0, 1).concat(res[0].slice(-2).reverse());

res[2] = res[1].slice(-1).concat(res[1].slice(0, 2));

res[3] = res[2].slice(0, 1).concat(res[2].slice(-2).reverse());

res[4] = res[3].slice(-2).concat(res[3].slice(0, 1));

res[5] = res[4].slice(0, 1).concat(res[4].slice(-2).reverse());

console.log(res);

Edit, Update

Have found a process to utilize pattern described above to return permutations in lexicographic order for an input up to .length 4 , using a single for loop. Expected results are not returned for array with .length of 5.

The pattern is based on the second chart at "Calculating Permutations and Job Interview Questions"[0].

Would prefer not to use .splice() or .sort() to return results, though used here while attempting to adhere to last "rotate" requirement at each column. The variable r should reference the index of the first element of the next permutation, which it does.

The use of .splice() , .sort() could be included if their usage followed the pattern at the chart ; though at js below, they actually do not.

Not entirely certain that the issue with js below is only the statement following if (i % (total / len) === reset) , though that portion required the most investment of time; yet still does not return expected results.

Specifically, now referring to the chart, at rotating , for example 2 to index 0, 1 to index 2. Attempted to achieve this by using r , which is a negative index, to traverses from right to left to retrieve next item that should be positioned at index 0 of adjacent "column".

At next column, 2 would be placed at index 2 , 3 would be placed at index 0. This is portion, as far as have been able to grasp or debug, so far, is the area where error is occurring.

Again, returns expected results for [1,2,3,4], though not for [1,2,3,4,5]

var arr = [1, 2, 3, 4];
for (var l = 1, j = total = arr.length; l < j ; total *= l++);
for (var i = 1
     , reset = 0
     , idx = 0
     , r = 0
     , len = arr.length
     , res = [arr]
     ; i < total; i++) {
  // previous permutation
  var prev = res[i - 1];
  // if we are at permutation `6` here, or, completion of all 
  // permutations beginning with `1`;
  // setting next "column", place `2` at `index` 0;
  // following all permutations beginning with `2`, place `3` at
  // `index` `0`; with same process for `3` to `4`
  if (i % (total / len) === reset) {
    r = --r % -(len);
    var next = prev.slice(r);
    if (r === -1) {
      // first implementation used for setting item at index `-1`
      // to `index` 0
      // would prefer to use single process for all "rotations",
      // instead of splitting into `if` , `else`, though not there, yet
      res[i] = [next[0]].concat(prev.slice(0, 1), prev.slice(1, len - 1)
               .reverse());
    } else {
      // workaround for "rotation" at from `index` `r` to `index` `0`
      // the chart does not actually use the previous permutation here,
      // but rather, the first permutation of that particular "column";
      // here, using `r` `,i`, `len`, would be 
      // `res[i - (i - 1) % (total / len)]`
      var curr = prev.slice();
      // this may be useful, to retrieve `r`, 
      // `prev` without item at `r` `index`
      curr.splice(prev.indexOf(next[0]), 1);
      // this is not optiomal
      curr.sort(function(a, b) {
        return arr.indexOf(a) > arr.indexOf(b)
      });
      // place `next[0]` at `index` `0`
      // place remainder of sorted array at `index` `1` - n
      curr.splice(0, 0, next[0])
      res[i] = curr
    }
    idx = reset;
  } else {
    if (i % 2) {
      // odd
      res[i] = prev.slice(0, len - 2).concat(prev.slice(-2)
              .reverse())
    } else {
      //  even
      --idx
      res[i] = prev.slice(0, len - (len - 1))
               .concat(prev.slice(idx), prev.slice(1, len + (idx)))
    }
  }
}
// try with `arr` : `[1,2,3,4,5]` to return `res` that is not correct;
// how can above `js` be adjusted to return correct results for `[1,2,3,4,5]` ?
console.log(res, res.length)

Resources:

Generating Permutation with Javascript

(Countdown) QuickPerm Head Lexicography: (Formally Example_03 ~ Palindromes)

Generating all Permutations [non-recursive] (Attempt to port to from C++ to javascript jsfiddle http://jsfiddle.net/tvvvjf3p/)

Calculating Permutation without Recursion - Part 2

permutations of a string using iteration

iterative-permutation

Permutations by swapping

Evaluation of permutation algorithms

Permutation algorithm without recursion? Java

Non-recursive algorithm for full permutation with repetitive elements?

String permutations in Java (non-recursive)

Generating permutations lazily

How to generate all permutations of a list in Python

Can all permutations of a set or string be generated in O(n log n) time?

Finding the nth lexicographic permutation of ‘0123456789’

Combinations and Permutations

Community
  • 1
  • 1
guest271314
  • 1
  • 10
  • 82
  • 156
  • 2
    There is an algorithm at [*Quora: What's an algorithm to print the permutations of a string without recursion?*](https://www.quora.com/Whats-an-algorithm-to-print-the-permutations-of-a-string-without-recursion) that may help. You can also search for algorithms in other languages and convert them to ECMAScript. – RobG Dec 01 '15 at 06:59
  • 2
    have a look at this [Generalized Permutation (without repetitions and recursion) in C++:](http://stackoverflow.com/a/30498154/2521214) – Spektre Dec 07 '15 at 12:09
  • @Spektre If possible, can formulate solution as `javascript` , post as Answer at present Question with description of process, approach implemented ? – guest271314 Dec 07 '15 at 15:37
  • @guest271314 sorry I do not code in Javascript That is why I use just comment but the thread contains also description how it works with example so it should not be too hard to port to javascript for any one coding in it. – Spektre Dec 07 '15 at 21:29
  • 2
    The Steinhaus–Johnson–Trotter algorithm might interest you, https://en.m.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm – גלעד ברקן Dec 08 '15 at 09:55

8 Answers8

20

Here is a simple solution to compute the nth permutation of a string:

function string_nth_permutation(str, n) {
    var len = str.length, i, f, res;

    for (f = i = 1; i <= len; i++)
        f *= i;

    if (n >= 0 && n < f) {
        for (res = ""; len > 0; len--) {
            f /= len;
            i = Math.floor(n / f);
            n %= f;
            res += str.charAt(i);
            str = str.substring(0, i) + str.substring(i + 1);
        }
    }
    return res;
}

The algorithm follows these simple steps:

  • first compute f = len!, there are factorial(len) total permutations of a set of len different elements.
  • as the first element, divide the permutation number by (len-1)! and chose the element at the resulting offset. There are (len-1)! different permutations that have any given element as their first element.
  • remove the chosen element from the set and use the remainder of the division as the permutation number to keep going.
  • perform these steps with the rest of the set, whose length is reduced by one.

This algorithm is very simple and has interesting properties:

  • It computes the n-th permutation directly.
  • If the set is ordered, the permutations are generated in lexicographical order.
  • It works even if set elements cannot be compared to one another, such as objects, arrays, functions...
  • Permutation number 0 is the set in the order given.
  • Permutation number factorial(a.length)-1 is the last one: the set a in reverse order.
  • Permutations outside this range are returned as undefined.

It can easily be converted to handle a set stored as an array:

function array_nth_permutation(a, n) {
    var b = a.slice();  // copy of the set
    var len = a.length; // length of the set
    var res;            // return value, undefined
    var i, f;

    // compute f = factorial(len)
    for (f = i = 1; i <= len; i++)
        f *= i;

    // if the permutation number is within range
    if (n >= 0 && n < f) {
        // start with the empty set, loop for len elements
        for (res = []; len > 0; len--) {
            // determine the next element:
            // there are f/len subsets for each possible element,
            f /= len;
            // a simple division gives the leading element index
            i = Math.floor(n / f);
            // alternately: i = (n - n % f) / f;
            res.push(b.splice(i, 1)[0]);
            // reduce n for the remaining subset:
            // compute the remainder of the above division
            n %= f;
            // extract the i-th element from b and push it at the end of res
        }
    }
    // return the permutated set or undefined if n is out of range
    return res;
}

clarification:

  • f is first computed as factorial(len).
  • For each step, f is divided by len, giving exacty the previous factorial.
  • n divided by this new value of f gives the slot number among the len slots that have the same initial element. Javascript does not have integral division, we could use (n / f) ... 0) to convert the result of the division to its integral part but it introduces a limitation to sets of 12 elements. Math.floor(n / f) allows for sets of up to 18 elements. We could also use (n - n % f) / f, probably more efficient too.
  • n must be reduced to the permutation number within this slot, that is the remainder of the division n / f.

We could use i differently in the second loop, storing the division remainder, avoiding Math.floor() and the extra % operator. Here is an alternative for this loop that may be even less readable:

        // start with the empty set, loop for len elements
        for (res = []; len > 0; len--) {
            i = n % (f /= len);
            res.push(b.splice((n - i) / f, 1)[0]);
            n = i;
        }
chqrlie
  • 98,886
  • 10
  • 89
  • 149
  • @chqlie If possible, can include comments at second `for` loop describing process occurring at each line ? – guest271314 Dec 12 '15 at 17:35
  • Can describe mathematics, programmatic operation of `i = (n / (f /= len)) >> 0` , and `n -= f * i;`? – guest271314 Dec 12 '15 at 18:18
  • Can describe programmatic operation of `>> 0` ? in relation to precedin `(n / (f /= len))` ? Not well versed on bitwise operators, here, as of yet. Please enlighten. Did not intend to misspell user name at first comment , above – guest271314 Dec 12 '15 at 19:01
  • `>> 0` or `>>> 0` is a trick to convert a floating point number to its integral part if it is small enough to fit in a 32 bit integer. Since we want to support sets larger than `8` elements (`8!` = `40320`), I shall remove this trick and use` Math.floor()`. – chqrlie Dec 12 '15 at 19:06
  • Enlightening explanations. Is this the expected result of `>> 0` _"So doing a bitwise operation with no actual effect, like a rightward-shift of 0 bits >>0, is a quick way to round a number and ensure it is in the 32-bit int range"_ http://stackoverflow.com/a/1822769/ ? – guest271314 Dec 12 '15 at 19:17
  • 1
    Yes indeed. `x >>= 0` converts a floating point number to its integral part if it is in the range `]-2147483649 .. 2147483648[`. `x >>>= 0`, its unsigned equivalent, first converts `x` to `UInt32`, thus works for `x` in range `[0 .. 4294967296[`. Yet in your case, we should be able to handle sets with more than 12 elements, thus exceeding the capacity of a 32 bit integer. Using `Math.floor()` allows for sets upto 18 elements. – chqrlie Dec 12 '15 at 19:25
  • _"We could also use `(n - n % f) / f`, probably more efficient too."_ Would `(n - n % f) / f` be more efficient due to not having to call `Math.floor()` ? or would `(n - n % f) / f` be included as parameter to `Math.floor((n - n % f) / f)` ? – guest271314 Dec 12 '15 at 19:35
  • Tried substituting `Math.floor((n - n % f) / f)` for `Math.floor(n / (f /= len)` , did not appear return same results ? – guest271314 Dec 12 '15 at 19:48
  • 1
    @guest271314: `(n - n % f) / f` computes the integer division, no need to call `Math.floor` around it. It performs one extra modulus operation but avoids the expensive lookup operation for `Math.floor`, the function call overhead and the actual `floor` floating point operation. All these can be optimized but probably still slower than `(n - n % f) / f`. – chqrlie Dec 12 '15 at 20:06
  • 1
    @guest271314: If you use `(n - n % f) / f` you need to compute `f /= len` in a separate statement before hand. – chqrlie Dec 12 '15 at 20:11
  • @guest271314: `f /= len` needs to happen before. I updated the answer with a slightly different alternative. – chqrlie Dec 12 '15 at 20:24
  • Last update does not return same results as setting `f /= len;` , next line `b.splice((n - n % f) / f, 1)[0]` , next line `n %= f;` ; where both `n` , `f` and `len` are reset within a `for` loop containing permutation `for` loop, to return all permutations. `[1,2,3,4]` returned array having `.length` of `120` instead of `24` . `n` would be set to current iteration index, `f` reset to an alias of `f` which does not decrement, or divied after each iteration – guest271314 Dec 12 '15 at 20:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/97724/discussion-between-chqrlie-and-guest271314). – chqrlie Dec 12 '15 at 20:45
  • Is there any name for this algorithm? – Siddhesh Rane Jan 31 '19 at 16:37
8

I think this post should help you. The algorithm should be easily translatable to JavaScript (I think it is more than 70% already JavaScript-compatible).

slice and reverse are bad calls to use if you are after efficiency. The algorithm described in the post is following the most efficient implementation of the next_permutation function, that is even integrated in some programming languages (like C++ e.g.)

EDIT

As I iterated over the algorithm once again I think you can just remove the types of the variables and you should be good to go in JavaScript.

EDIT

JavaScript version:

function nextPermutation(array) {
    // Find non-increasing suffix
    var i = array.length - 1;
    while (i > 0 && array[i - 1] >= array[i])
        i--;
    if (i <= 0)
        return false;

    // Find successor to pivot
    var j = array.length - 1;
    while (array[j] <= array[i - 1])
        j--;
    var temp = array[i - 1];
    array[i - 1] = array[j];
    array[j] = temp;

    // Reverse suffix
    j = array.length - 1;
    while (i < j) {
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
        i++;
        j--;
    }
    return true;
}
Nayuki
  • 16,655
  • 5
  • 47
  • 75
Boris Strandjev
  • 43,262
  • 13
  • 98
  • 123
7

One method to create permutations is by adding each element in all of the spaces between elements in all of the results so far. This can be done without recursion using loops and a queue.

JavaScript code:

function ps(a){
  var res = [[]];

  for (var i=0; i<a.length; i++){
    while(res[res.length-1].length == i){
      var l = res.pop();
      for (var j=0; j<=l.length; j++){
        var copy = l.slice();
        copy.splice(j,0,a[i]);
        res.unshift(copy);
      }
    }
  }
  return res;
}

console.log(JSON.stringify(ps(['a','b','c','d'])));
גלעד ברקן
  • 21,095
  • 3
  • 19
  • 57
  • What adjustments can be made at solution to return results in lexicographic order ? – guest271314 Dec 11 '15 at 17:00
  • @guest271314 I'm not sure if this algorithm can be adjusted to output the permutations in lexicographic order. There is a known algorithm for "next lexicographic permutation," however: https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order – גלעד ברקן Dec 11 '15 at 17:43
  • Was able to convert to lexicographic order using `.sort()` on result, though will try other methods within loop – guest271314 Dec 11 '15 at 17:50
  • `return res.sort()` appear to achieve lexicographic order. Any cases where this would return unexpected results ? Perhaps `.sort()` could also be used to return permutations ?, when seeded with arrays within array having `.length` of total possible permutations, where each inner array is original input array ? – guest271314 Dec 11 '15 at 18:08
  • The `sort()` function will try to sort the input you give it in lexicographic (or user-defined) order. I don't see how it could be used to return permutations - if you give `sort()` two arrays with the same elements in different order, `sort()` will return two identical arrays - both sorted in the same way. – גלעד ברקן Dec 11 '15 at 18:21
  • A naive start `var arr = [1,2,3]; res = []; arr.sort(function(a, b) { var p = [a,b]; var next = arr.filter(function(el) {return p.indexOf(el) == -1}) res.push(p.concat(next[0]))})` ? Though the four steps at https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order could possibly be incorporated into compare function ? – guest271314 Dec 11 '15 at 18:35
  • @guest271314 not sure what you mean - that code snippet did not work for me – גלעד ברקן Dec 11 '15 at 18:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/97671/discussion-between-guest271314-and--). – guest271314 Dec 11 '15 at 18:39
6

Here could be another solution, inspired from the Steinhaus-Johnson-Trotter algorithm:

function p(input) {
  var i, j, k, temp, base, current, outputs = [[input[0]]];
  for (i = 1; i < input.length; i++) {
    current = [];
    for (j = 0; j < outputs.length; j++) {
      base = outputs[j];
      for (k = 0; k <= base.length; k++) {
        temp = base.slice();
        temp.splice(k, 0, input[i]);
        current.push(temp);
      }
    }
    outputs = current;
  }
  return outputs;
}

// call

var outputs = p(["a", "b", "c", "d"]);
for (var i = 0; i < outputs.length; i++) {
  document.write(JSON.stringify(outputs[i]) + "<br />");
}
sp00m
  • 44,266
  • 23
  • 127
  • 230
5

Here's a snippet for an approach that I came up with on my own, but naturally was also able to find it described elsewhere:

generatePermutations = function(arr) {
  if (arr.length < 2) {
    return arr.slice();
  }
  var factorial = [1];
  for (var i = 1; i <= arr.length; i++) {
    factorial.push(factorial[factorial.length - 1] * i);
  }

  var allPerms = [];
  for (var permNumber = 0; permNumber < factorial[factorial.length - 1]; permNumber++) {
    var unused = arr.slice();
    var nextPerm = [];
    while (unused.length) {
      var nextIndex = Math.floor((permNumber % factorial[unused.length]) / factorial[unused.length - 1]);
      nextPerm.push(unused[nextIndex]);
      unused.splice(nextIndex, 1);
    }
    allPerms.push(nextPerm);
  }
  return allPerms;
};
Enter comma-separated string (e.g. a,b,c):
<br/>
<input id="arrInput" type="text" />
<br/>
<button onclick="perms.innerHTML = generatePermutations(arrInput.value.split(',')).join('<br/>')">
  Generate permutations
</button>
<br/>
<div id="perms"></div>

Explanation

Since there are factorial(arr.length) permutations for a given array arr, each number between 0 and factorial(arr.length)-1 encodes a particular permutation. To unencode a permutation number, repeatedly remove elements from arr until there are no elements left. The exact index of which element to remove is given by the formula (permNumber % factorial(arr.length)) / factorial(arr.length-1). Other formulas could be used to determine the index to remove, as long as it preserves the one-to-one mapping between number and permutation.

Example

The following is how all permutations would be generated for the array (a,b,c,d):

#    Perm      1st El        2nd El      3rd El    4th El
0    abcd   (a,b,c,d)[0]   (b,c,d)[0]   (c,d)[0]   (d)[0]
1    abdc   (a,b,c,d)[0]   (b,c,d)[0]   (c,d)[1]   (c)[0]
2    acbd   (a,b,c,d)[0]   (b,c,d)[1]   (b,d)[0]   (d)[0]
3    acdb   (a,b,c,d)[0]   (b,c,d)[1]   (b,d)[1]   (b)[0]
4    adbc   (a,b,c,d)[0]   (b,c,d)[2]   (b,c)[0]   (c)[0]
5    adcb   (a,b,c,d)[0]   (b,c,d)[2]   (b,c)[1]   (b)[0]
6    bacd   (a,b,c,d)[1]   (a,c,d)[0]   (c,d)[0]   (d)[0]
7    badc   (a,b,c,d)[1]   (a,c,d)[0]   (c,d)[1]   (c)[0]
8    bcad   (a,b,c,d)[1]   (a,c,d)[1]   (a,d)[0]   (d)[0]
9    bcda   (a,b,c,d)[1]   (a,c,d)[1]   (a,d)[1]   (a)[0]
10   bdac   (a,b,c,d)[1]   (a,c,d)[2]   (a,c)[0]   (c)[0]
11   bdca   (a,b,c,d)[1]   (a,c,d)[2]   (a,c)[1]   (a)[0]
12   cabd   (a,b,c,d)[2]   (a,b,d)[0]   (b,d)[0]   (d)[0]
13   cadb   (a,b,c,d)[2]   (a,b,d)[0]   (b,d)[1]   (b)[0]
14   cbad   (a,b,c,d)[2]   (a,b,d)[1]   (a,d)[0]   (d)[0]
15   cbda   (a,b,c,d)[2]   (a,b,d)[1]   (a,d)[1]   (a)[0]
16   cdab   (a,b,c,d)[2]   (a,b,d)[2]   (a,b)[0]   (b)[0]
17   cdba   (a,b,c,d)[2]   (a,b,d)[2]   (a,b)[1]   (a)[0]
18   dabc   (a,b,c,d)[3]   (a,b,c)[0]   (b,c)[0]   (c)[0]
19   dacb   (a,b,c,d)[3]   (a,b,c)[0]   (b,c)[1]   (b)[0]
20   dbac   (a,b,c,d)[3]   (a,b,c)[1]   (a,c)[0]   (c)[0]
21   dbca   (a,b,c,d)[3]   (a,b,c)[1]   (a,c)[1]   (a)[0]
22   dcab   (a,b,c,d)[3]   (a,b,c)[2]   (a,b)[0]   (b)[0]
23   dcba   (a,b,c,d)[3]   (a,b,c)[2]   (a,b)[1]   (a)[0]

Note that each permutation # is of the form:

(firstElIndex * 3!) + (secondElIndex * 2!) + (thirdElIndex * 1!) + (fourthElIndex * 0!)

which is basically the reverse process of the formula given in the explanation.

Community
  • 1
  • 1
heenenee
  • 18,987
  • 1
  • 49
  • 81
5

I dare to add another answer, aiming at answering you question regarding slice, concat, reverse.

The answer is it is possible (almost), but it would not be quite effective. What you are doing in your algorithm is the following:

  • Find the first inversion in the permutation array, right-to-left (inversion in this case defined as i and j where i < j and perm[i] > perm[j], indices given left-to-right)
  • place the bigger number of the inversion
  • concatenate the processed numbers in reversed order, which will be the same as sorted order, as no inversions were observed.
  • concatenate the second number of the inversion (still sorted in accordsnce with the previos number, as no inversions were observed)

This is mainly, what my first answer does, but in a bit more optimal manner.

Example

Consider the permutation 9,10, 11, 8, 7, 6, 5, 4 ,3,2,1 The first inversion right-to-left is 10, 11. And really the next permutation is: 9,11,1,2,3,4,5,6,7,8,9,10=9concat(11)concat(rev(8,7,6,5,4,3,2,1))concat(10)

Source code Here I include the source code as I envision it:

var nextPermutation = function(arr) {
  for (var i = arr.length - 2; i >= 0; i--) {
     if (arr[i] < arr[i + 1]) {
        return arr.slice(0, i).concat([arr[i + 1]]).concat(arr.slice(i + 2).reverse()).concat([arr[i]]);
     }
  }
  // return again the first permutation if calling next permutation on last.
  return arr.reverse();
}

console.log(nextPermutation([9, 10, 11, 8, 7, 6, 5, 4, 3, 2, 1]));
console.log(nextPermutation([6, 5, 4, 3, 2, 1]));
console.log(nextPermutation([1, 2, 3, 4, 5, 6]));

The code is avaiable for jsfiddle here.

Boris Strandjev
  • 43,262
  • 13
  • 98
  • 123
  • Yes, attempting to use `.slice()`, `.reverse()` , `.concat()` and single `for` loop. Tried to convert recursive solution at OP to iterative solution, though not yet able. Your initial Answer actually demanded further inquiry; created a working version using a similar pattern. Likewise, am hesitant to post it here, as it does not actually address patterns described at OP. – guest271314 Dec 08 '15 at 16:47
  • So are you striving fpr iterative or recursive? – Boris Strandjev Dec 08 '15 at 17:06
  • Iterative, non-recursive. _"Consider the permutation 9,10, 11, 8, 7, 6, 5, 4 ,3,2,1 The first inversion right-to-left is 10, 11. And really the next permutation is: 9,11,1,2,3,4,5,6,7,8,9,10=9concat(11)concat(rev(8,7,6,5,4,3,2,1))concat(10)"_ Can this be achieved with modifications to either long-form approach , which does not return expected results after sixth permutation, or `.forEach()` approach ? Created a `for` which includes both `input[i] < input[i + 1]` , though was unable thus far to use even, odd approach attempted at Question – guest271314 Dec 08 '15 at 17:10
  • Attempting to reduce to single or two lines within `for` loop ; initial attempt , which is incomplete, does not return expected results http://jsfiddle.net/ya31o4km/2/ . Noticed pattern at long form at Question where `-1` is applied at `res[2] = res[1].slice(-1).concat(res[1].slice(0,2));` ; `n` and `i` at jsfiddle should be able to used to decrement at `.slice()` over six iterations ? Missing something, here, when observing pattern , why posted Question – guest271314 Dec 08 '15 at 17:19
  • 1
    @guest271314 I have added my code, have not yet had the chance to try it – Boris Strandjev Dec 09 '15 at 08:31
  • Was presently substituting `.concat()` for `.push()`. Tried to extend to return all permutations ? , set next parameter to returned array `arr` ? Or is this process already included ? – guest271314 Dec 09 '15 at 09:00
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/97379/discussion-between-boris-strandjev-and-guest271314). – Boris Strandjev Dec 09 '15 at 09:07
3

A fairly simple C++ code without recursion.

#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <string>

// Integer data
void print_all_permutations(std::vector<int> &data) {
    std::stable_sort(std::begin(data), std::end(data));
    do {
        std::copy(data.begin(), data.end(), std::ostream_iterator<int>(std::cout, " ")), std::cout << '\n';
    } while (std::next_permutation(std::begin(data), std::end(data)));
}

// Character data (string)
void print_all_permutations(std::string &data) {
    std::stable_sort(std::begin(data), std::end(data));
    do {
        std::copy(data.begin(), data.end(), std::ostream_iterator<char>(std::cout, " ")), std::cout << '\n';
    } while (std::next_permutation(std::begin(data), std::end(data)));
}

int main()
{
    std::vector<int> v({1,2,3,4});
    print_all_permutations(v);

    std::string s("abcd");
    print_all_permutations(s);

    return 0;
}

We can find next permutation of a sequence in linear time.

Venki
  • 101
  • 2
  • 6
0

Here is an answer from @le_m. It might be of help.

The following very efficient algorithm uses Heap's method to generate all permutations of N elements with runtime complexity in O(N!):

function permute(permutation) {
  var length = permutation.length,
      result = [permutation.slice()],
      c = new Array(length).fill(0),
      i = 1, k, p;

  while (i < length) {
    if (c[i] < i) {
      k = i % 2 && c[i];
      p = permutation[i];
      permutation[i] = permutation[k];
      permutation[k] = p;
      ++c[i];
      i = 1;
      result.push(permutation.slice());
    } else {
      c[i] = 0;
      ++i;
    }
  }
  return result;
}

console.log(JSON.stringify(permute([1, 2, 3, 4])));
jo_va
  • 11,779
  • 3
  • 20
  • 39