Last active
January 29, 2020 00:27
-
-
Save sebinsua/5284e13f9f9891e7919fe72905453b5a to your computer and use it in GitHub Desktop.
Permutations and combinations from scratch.
This file contains 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
function permute(arr) { | |
if (arr.length === 2) { | |
const [a, b] = arr; | |
return [[a, b], [b, a]]; | |
} | |
let permutationItems = []; | |
for (let idx = 0; idx < arr.length; idx++) { | |
const first = arr[idx]; | |
const tail = [...arr]; | |
tail.splice(idx, 1); | |
permutationItems.push(...permute(tail).map(part => [first, ...part])); | |
} | |
return permutationItems; | |
} | |
const permutations = permute(["F", "I", "A", "D", "H"]); | |
for (let permutation of permutations) { | |
console.log(permutation.join("")); | |
} |
What if permute
handled more edge-cases?
function* permute(arr) {
if (arr.length === 0) {
return;
}
if (arr.length === 1) {
yield [arr[0]];
return;
}
if (arr.length === 2) {
const [a, b] = arr;
yield [a, b];
yield [b, a];
return;
}
for (let idx = 0; idx < arr.length; idx++) {
const first = arr[idx];
const tail = [...arr];
tail.splice(idx, 1);
for (let part of permute(tail)) {
yield [first, ...part];
}
}
}
for (let permutation of permute(["F", "I", "A", "D", "H"])) {
console.log(permutation.join(""));
}
What if it were possible to use permute
to generate all permutations of a particular length
?
function* permute(arr, _length = arr.length) {
const length = Math.min(_length, arr.length);
if (arr.length === 0) {
return;
}
if (arr.length === 1) {
yield [arr[0]];
return;
}
if (arr.length === 2) {
const [a, b] = arr;
yield [a, b];
yield [b, a];
return;
}
let seen = new Set();
for (let idx = 0; idx < arr.length; idx++) {
const first = arr[idx];
const tail = [...arr];
tail.splice(idx, 1);
for (let part of permute(tail, length - 1)) {
const permutation = [first, ...part].slice(0, length);
const key = permutation.join("");
if (seen.has(key)) {
continue;
}
seen.add(key);
yield permutation;
}
}
}
let count = 0;
for (let permutation of permute(["A", "B", "C", "D", "E", "F", "G"], 3)) {
console.log(permutation.join(""));
count++;
}
console.log('Count:', count);
What if it were possible to generate distinct combinations that do not care about the ordering?
const defaultKey = arr => arr.join("");
function createPermute(toKey = defaultKey) {
return function* permute(arr, _length = arr.length) {
const length = Math.min(_length, arr.length);
if (arr.length === 0) {
return;
}
if (arr.length === 1) {
yield [arr[0]];
return;
}
if (arr.length === 2) {
const [a, b] = arr;
yield [a, b];
yield [b, a];
return;
}
let seen = new Set();
for (let idx = 0; idx < arr.length; idx++) {
const first = arr[idx];
const tail = [...arr];
tail.splice(idx, 1);
for (let part of permute(tail, length - 1)) {
const permutation = [first, ...part].slice(0, length);
const key = toKey(permutation);
if (seen.has(key)) {
continue;
}
seen.add(key);
yield permutation;
}
}
};
}
const combine = createPermute(arr =>
[...arr].sort((a, b) => a.localeCompare(b)).join("")
);
let count = 0;
for (let combination of combine(["A", "B", "C", "D", "E", "F", "G"], 5)) {
console.log(combination.join(""));
count++;
}
console.log("Count:", count);
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What if
permute
was more memory efficient due to the usage of generators?