I\'m trying to write a function that does the following:
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;
r
(result) array we will add the next item of the input array.t
. (well except for the first one not to waste time with 0 rotation)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.
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])));
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`)
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' ]
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]));
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}