Created
February 2, 2025 12:27
-
-
Save Paul-Browne/85b5521e11e168e826cd9c94e8ce1846 to your computer and use it in GitHub Desktop.
find all permutations of a string
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Heap's algorithm (iterative version, recursive version took waaaay longer) | |
// https://en.wikipedia.org/wiki/Heap%27s_algorithm | |
// Even though I kinda understand the algorithm, | |
// *after* reading about it and converting it to JS | |
// I can't pretend that I came up with this solution on my own. | |
const permutate = str => { | |
const lengthOfString = str.length; | |
// split the string into an array | |
const arr = str.split(''); | |
// store the results | |
// initialize with the original string, save one iteration | |
const results = [str]; | |
// store the "count" of each element | |
// initialize with zero's eg. [0, 0, 0, 0, 0] | |
// this is used to track the "stack state" since we need to simulate the recursion | |
const count = Array(lengthOfString).fill(0); | |
let i = 1; | |
while (i < lengthOfString) { | |
if (count[i] < i) { | |
// if i is odd, then j = count[i], else j = 0 | |
let j = i % 2 ? count[i] : 0; | |
// swap the elements | |
[arr[j], arr[i]] = [arr[i], arr[j]]; | |
// increment of the while-loop counter | |
count[i] += 1; | |
// simulate recursion hitting the base case | |
i = 1; | |
// push the result | |
results.push(arr.join('')); | |
} else { | |
// reset the state and pop the stack by incrementing i | |
count[i] = 0; | |
i += 1; | |
} | |
} | |
// remove duplicates | |
return [...new Set(results)]; | |
} | |
const STRING = 'abcccdef'; | |
console.time('permutate'); | |
const answers = permutate(STRING); | |
console.timeEnd('permutate'); | |
console.log(answers); | |
// FOR TESTING | |
// if the length of the array of permutations is equal | |
// to the number of possible permutations, the test passes | |
// Math.factorial(n) is not available in javascript | |
// so I used a recursive factorial function | |
const factorial = num => num ? factorial(num - 1) * num : 1 | |
const frequencyOfLetters = str => str.split("").reduce((acc, cur) => (acc[cur] = acc[cur] + 1 || 1, acc), {}); | |
const possiblePermutations = str => { | |
const frequencyTable = frequencyOfLetters(str); | |
const factorialOfLengthOfWord = factorial(str.length); | |
const sumFactorialOfFrequencies = Object.values(frequencyTable).reduce((acc, cur) => acc * factorial(cur), 1); | |
return factorialOfLengthOfWord / sumFactorialOfFrequencies; | |
} | |
const testLengthOfAnswer = (str, answersArray) => { | |
if (possiblePermutations(str) === answersArray.length) { | |
console.log('Test passed'); | |
} else { | |
console.log('Test failed'); | |
} | |
} | |
testLengthOfAnswer(STRING, answers); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment