mirror of
https://github.com/trekhleb/javascript-algorithms.git
synced 2025-07-07 01:44:52 +08:00
Simplify permutateWithRepetitions algorithm.
This commit is contained in:
@ -13,9 +13,7 @@ export default function combineWithRepetitions(comboOptions, comboLength) {
|
|||||||
|
|
||||||
// Eliminate characters one by one and concatenate them to
|
// Eliminate characters one by one and concatenate them to
|
||||||
// combinations of smaller lengths.
|
// combinations of smaller lengths.
|
||||||
for (let optionIndex = 0; optionIndex < comboOptions.length; optionIndex += 1) {
|
comboOptions.forEach((currentOption, optionIndex) => {
|
||||||
const currentOption = comboOptions[optionIndex];
|
|
||||||
|
|
||||||
const smallerCombos = combineWithRepetitions(
|
const smallerCombos = combineWithRepetitions(
|
||||||
comboOptions.slice(optionIndex),
|
comboOptions.slice(optionIndex),
|
||||||
comboLength - 1,
|
comboLength - 1,
|
||||||
@ -24,7 +22,7 @@ export default function combineWithRepetitions(comboOptions, comboLength) {
|
|||||||
smallerCombos.forEach((smallerCombo) => {
|
smallerCombos.forEach((smallerCombo) => {
|
||||||
combos.push([currentOption].concat(smallerCombo));
|
combos.push([currentOption].concat(smallerCombo));
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
return combos;
|
return combos;
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,7 @@ export default function combineWithoutRepetitions(comboOptions, comboLength) {
|
|||||||
|
|
||||||
// Eliminate characters one by one and concatenate them to
|
// Eliminate characters one by one and concatenate them to
|
||||||
// combinations of smaller lengths.
|
// combinations of smaller lengths.
|
||||||
for (let optionIndex = 0; optionIndex <= (comboOptions.length - comboLength); optionIndex += 1) {
|
comboOptions.forEach((currentOption, optionIndex) => {
|
||||||
const currentOption = comboOptions[optionIndex];
|
|
||||||
|
|
||||||
const smallerCombos = combineWithoutRepetitions(
|
const smallerCombos = combineWithoutRepetitions(
|
||||||
comboOptions.slice(optionIndex + 1),
|
comboOptions.slice(optionIndex + 1),
|
||||||
comboLength - 1,
|
comboLength - 1,
|
||||||
@ -24,7 +22,7 @@ export default function combineWithoutRepetitions(comboOptions, comboLength) {
|
|||||||
smallerCombos.forEach((smallerCombo) => {
|
smallerCombos.forEach((smallerCombo) => {
|
||||||
combos.push([currentOption].concat(smallerCombo));
|
combos.push([currentOption].concat(smallerCombo));
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
return combos;
|
return combos;
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,6 @@ import permutateWithRepetitions from '../permutateWithRepetitions';
|
|||||||
|
|
||||||
describe('permutateWithRepetitions', () => {
|
describe('permutateWithRepetitions', () => {
|
||||||
it('should permutate string with repetition', () => {
|
it('should permutate string with repetition', () => {
|
||||||
const permutations0 = permutateWithRepetitions([]);
|
|
||||||
expect(permutations0).toEqual([]);
|
|
||||||
|
|
||||||
const permutations1 = permutateWithRepetitions(['A']);
|
const permutations1 = permutateWithRepetitions(['A']);
|
||||||
expect(permutations1).toEqual([
|
expect(permutations1).toEqual([
|
||||||
['A'],
|
['A'],
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import permutateWithRepetitionsRecursive from '../permutateWithRepetitionsRecursive';
|
|
||||||
|
|
||||||
describe('permutateWithRepetitionsRecursive', () => {
|
|
||||||
it('should permutate string with repetition', () => {
|
|
||||||
const permutations0 = permutateWithRepetitionsRecursive([]);
|
|
||||||
expect(permutations0).toEqual([]);
|
|
||||||
|
|
||||||
const permutations1 = permutateWithRepetitionsRecursive(['A']);
|
|
||||||
expect(permutations1).toEqual([
|
|
||||||
['A'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const permutations2 = permutateWithRepetitionsRecursive(['A', 'B']);
|
|
||||||
expect(permutations2).toEqual([
|
|
||||||
['A', 'A'],
|
|
||||||
['A', 'B'],
|
|
||||||
['B', 'A'],
|
|
||||||
['B', 'B'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const permutations3 = permutateWithRepetitionsRecursive(['A', 'B', 'C']);
|
|
||||||
expect(permutations3).toEqual([
|
|
||||||
['A', 'A', 'A'],
|
|
||||||
['A', 'A', 'B'],
|
|
||||||
['A', 'A', 'C'],
|
|
||||||
['A', 'B', 'A'],
|
|
||||||
['A', 'B', 'B'],
|
|
||||||
['A', 'B', 'C'],
|
|
||||||
['A', 'C', 'A'],
|
|
||||||
['A', 'C', 'B'],
|
|
||||||
['A', 'C', 'C'],
|
|
||||||
['B', 'A', 'A'],
|
|
||||||
['B', 'A', 'B'],
|
|
||||||
['B', 'A', 'C'],
|
|
||||||
['B', 'B', 'A'],
|
|
||||||
['B', 'B', 'B'],
|
|
||||||
['B', 'B', 'C'],
|
|
||||||
['B', 'C', 'A'],
|
|
||||||
['B', 'C', 'B'],
|
|
||||||
['B', 'C', 'C'],
|
|
||||||
['C', 'A', 'A'],
|
|
||||||
['C', 'A', 'B'],
|
|
||||||
['C', 'A', 'C'],
|
|
||||||
['C', 'B', 'A'],
|
|
||||||
['C', 'B', 'B'],
|
|
||||||
['C', 'B', 'C'],
|
|
||||||
['C', 'C', 'A'],
|
|
||||||
['C', 'C', 'B'],
|
|
||||||
['C', 'C', 'C'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const permutations4 = permutateWithRepetitionsRecursive(['A', 'B', 'C', 'D']);
|
|
||||||
expect(permutations4.length).toBe(4 * 4 * 4 * 4);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,42 +1,30 @@
|
|||||||
/**
|
/**
|
||||||
* @param {*[]} permutationOptions
|
* @param {*[]} permutationOptions
|
||||||
|
* @param {number} permutationLength
|
||||||
* @return {*[]}
|
* @return {*[]}
|
||||||
*/
|
*/
|
||||||
export default function permutateWithRepetitions(permutationOptions) {
|
export default function permutateWithRepetitions(
|
||||||
// There is no permutations for empty array.
|
permutationOptions,
|
||||||
if (!permutationOptions || permutationOptions.length === 0) {
|
permutationLength = permutationOptions.length,
|
||||||
return [];
|
) {
|
||||||
|
if (permutationLength === 1) {
|
||||||
|
return permutationOptions.map(permutationOption => [permutationOption]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is only one permutation for the 1-element array.
|
// Init permutations array.
|
||||||
if (permutationOptions.length === 1) {
|
const permutations = [];
|
||||||
return [permutationOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's create initial set of permutations.
|
// Go through all options and join it to the smaller permutations.
|
||||||
let previousPermutations = permutationOptions.map(option => [option]);
|
permutationOptions.forEach((currentOption) => {
|
||||||
let currentPermutations = [];
|
const smallerPermutations = permutateWithRepetitions(
|
||||||
let permutationSize = 1;
|
permutationOptions,
|
||||||
|
permutationLength - 1,
|
||||||
|
);
|
||||||
|
|
||||||
// While the size of each permutation is less then or equal to options length...
|
smallerPermutations.forEach((smallerPermutation) => {
|
||||||
while (permutationSize < permutationOptions.length) {
|
permutations.push([currentOption].concat(smallerPermutation));
|
||||||
// Reset all current permutations.
|
});
|
||||||
currentPermutations = [];
|
});
|
||||||
|
|
||||||
for (let permIndex = 0; permIndex < previousPermutations.length; permIndex += 1) {
|
return permutations;
|
||||||
for (let optionIndex = 0; optionIndex < permutationOptions.length; optionIndex += 1) {
|
|
||||||
let currentPermutation = previousPermutations[permIndex];
|
|
||||||
currentPermutation = currentPermutation.concat([permutationOptions[optionIndex]]);
|
|
||||||
currentPermutations.push(currentPermutation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make current permutations to be the previous ones.
|
|
||||||
previousPermutations = currentPermutations.slice(0);
|
|
||||||
|
|
||||||
// Increase permutation size counter.
|
|
||||||
permutationSize += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentPermutations;
|
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* @param {*[]} permutationOptions
|
|
||||||
* @param {number} permutationLength
|
|
||||||
* @param {*[]} currentPermutation
|
|
||||||
* @param {*[][]} permutations
|
|
||||||
* @return {*[]}
|
|
||||||
*/
|
|
||||||
export default function permutateWithRepetitionsRecursive(
|
|
||||||
permutationOptions,
|
|
||||||
permutationLength = permutationOptions.length || 0,
|
|
||||||
currentPermutation = [],
|
|
||||||
permutations = [],
|
|
||||||
) {
|
|
||||||
// If initial options are null or empty then return empty array.
|
|
||||||
if (!permutationOptions || !permutationOptions.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no more iterations required then add current permutation to permutations array.
|
|
||||||
if (permutationLength === 0) {
|
|
||||||
permutations.push(currentPermutation);
|
|
||||||
|
|
||||||
return permutations;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively find permutations and store in permutations array.
|
|
||||||
permutationOptions.forEach((permutationOption) => {
|
|
||||||
permutateWithRepetitionsRecursive(
|
|
||||||
permutationOptions,
|
|
||||||
permutationLength - 1,
|
|
||||||
currentPermutation.concat([permutationOption]),
|
|
||||||
permutations,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return permutations;
|
|
||||||
}
|
|
Reference in New Issue
Block a user