Recursively print all permutations of a string (Javascript)
Let's write a function that returns all permutations of a string as an array. As you don't want any global variables, returning the permutations is crucial.
function permut(string) {
if (string.length < 2) return string; // This is our break condition
var permutations = []; // This array will hold our permutations
for (var i = 0; i < string.length; i++) {
var char = string[i];
// Cause we don't want any duplicates:
if (string.indexOf(char) != i) // if char was used already
continue; // skip it this time
var remainingString = string.slice(0, i) + string.slice(i + 1, string.length); //Note: you can concat Strings via '+' in JS
for (var subPermutation of permut(remainingString))
permutations.push(char + subPermutation)
}
return permutations;
}
To print them, just iterate over the array afterwards:
var myString = "xyz";
permutations = permut(myString);
for (permutation of permutations)
print(permutation) //Use the output method of your choice
Hope I could help you with your question.
All permutations of a given string - complexity
There exists O(n!)
permutations of a string of length n
.
In generating each string in the permutation, you are doing O(n)
operation by having a for loop of O(string.length) = O(n)
It might not be obvious why there's n!
, but you are recursively calling permutation(..)
function with remaining string, so the string length will be n * (n - 1) * (n - 2) * (n - 3) * ... * 1.
Thus, the time complexity of your algorithm is O(n * n!)
.
Other popular known solutions (swap-based, which is similar to yours, and next-permutation-based) have the same time complexity.
Finding all permutations of array elements as concatenated strings
Heap's algorithm can be used to generate all permutations (without repetitions) of array elements, you can then use those permutations with array.join("")
to convert them to strings. This works for arrays of any size and any type:
Here is a quick javascript implementation:
// some global variable to store the results
var result = []
// currentSize should be invoked with the array size
function permutation(arr, currentSize) {
if (currentSize == 1) { // recursion base-case (end)
result.push(arr.join(""));
return;
}
for (let i = 0; i < currentSize; i++){
permutation(arr, currentSize - 1);
if (currentSize % 2 == 1) {
let temp = arr[0];
arr[0] = arr[currentSize - 1];
arr[currentSize - 1] = temp;
} else {
let temp = arr[i];
arr[i] = arr[currentSize - 1];
arr[currentSize - 1] = temp;
}
}
}
let array = ["1","2","3","4"];
permutation(array, array.length);
console.log(result);
// use result here
Keep in mind that this is very computationally expensive, with complexity of O(n!).
Permutations in JavaScript?
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])));
Generating all permutations of a given string
public static void permutation(String str) {
permutation("", str);
}
private static void permutation(String prefix, String str) {
int n = str.length();
if (n == 0) System.out.println(prefix);
else {
for (int i = 0; i < n; i++)
permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i+1, n));
}
}
(via Introduction to Programming in Java)
Generating all permutations of elements in a single array
This simplistic approach uses bitmasks, so it will only work for a relatively small number of words.
var array = ["apple", "banana", "lemon", "mango"];
var result = [];for (var n = 0; n < 1 << array.length; n++) { var i, a = []; for (i = 0; i < array.length; i++) if ((n & (1 << i)) == 0) a.push(array[i]); for (i = 0; i < array.length; i++) if ((n & (1 << i)) != 0) a.push("not " + array[i]); result.push(a.join(" "));}
console.dir(result.join("\n"));
Permutations without recursive function call
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 arefactorial(len)
total permutations of a set oflen
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 seta
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 asfactorial(len)
.- For each step,
f
is divided bylen
, giving exacty the previous factorial. n
divided by this new value off
gives the slot number among thelen
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 divisionn / 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;
}
Related Topics
JavaScript Return Number of Days,Hours,Minutes,Seconds Between Two Dates
How to Convert Dataurl to File Object in JavaScript
Is There a Downside to Using Es6 Template Literals Syntax Without a Templated Expression
What Does Variable Declaration with Multiple Comma Separated Values Mean (E.G. Var a = B,C,D;)
Fade in on Scroll Down, Fade Out on Scroll Up - Based on Element Position in Window
JavaScript Equivalent of Jquery's Extend Method
Call a Vue.Js Component Method from Outside the Component
Convert MySQL Datetime Stamp into JavaScript's Date Format
Reactjs: Prevent Multiple Times Button Press
Google Maps Move Marker with Lat/Lng from Ajax Success Returned Data
JavaScript Replace with Reference to Matched Group
How to Delete All Cookies with JavaScript
Remembering State Before App Goes in Foreground
JSON Left Out Infinity and Nan; JSON Status in Ecmascript
How to Prevent Moment.Js from Loading Locales with Webpack
Is It a Good Idea to Learn JavaScript Before Learning Jquery