mirror of
https://github.com/trekhleb/javascript-algorithms.git
synced 2025-07-05 08:26:14 +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
|
||||
// combinations of smaller lengths.
|
||||
for (let optionIndex = 0; optionIndex < comboOptions.length; optionIndex += 1) {
|
||||
const currentOption = comboOptions[optionIndex];
|
||||
|
||||
comboOptions.forEach((currentOption, optionIndex) => {
|
||||
const smallerCombos = combineWithRepetitions(
|
||||
comboOptions.slice(optionIndex),
|
||||
comboLength - 1,
|
||||
@ -24,7 +22,7 @@ export default function combineWithRepetitions(comboOptions, comboLength) {
|
||||
smallerCombos.forEach((smallerCombo) => {
|
||||
combos.push([currentOption].concat(smallerCombo));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return combos;
|
||||
}
|
||||
|
@ -13,9 +13,7 @@ export default function combineWithoutRepetitions(comboOptions, comboLength) {
|
||||
|
||||
// Eliminate characters one by one and concatenate them to
|
||||
// combinations of smaller lengths.
|
||||
for (let optionIndex = 0; optionIndex <= (comboOptions.length - comboLength); optionIndex += 1) {
|
||||
const currentOption = comboOptions[optionIndex];
|
||||
|
||||
comboOptions.forEach((currentOption, optionIndex) => {
|
||||
const smallerCombos = combineWithoutRepetitions(
|
||||
comboOptions.slice(optionIndex + 1),
|
||||
comboLength - 1,
|
||||
@ -24,7 +22,7 @@ export default function combineWithoutRepetitions(comboOptions, comboLength) {
|
||||
smallerCombos.forEach((smallerCombo) => {
|
||||
combos.push([currentOption].concat(smallerCombo));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return combos;
|
||||
}
|
||||
|
@ -2,9 +2,6 @@ import permutateWithRepetitions from '../permutateWithRepetitions';
|
||||
|
||||
describe('permutateWithRepetitions', () => {
|
||||
it('should permutate string with repetition', () => {
|
||||
const permutations0 = permutateWithRepetitions([]);
|
||||
expect(permutations0).toEqual([]);
|
||||
|
||||
const permutations1 = permutateWithRepetitions(['A']);
|
||||
expect(permutations1).toEqual([
|
||||
['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 {number} permutationLength
|
||||
* @return {*[]}
|
||||
*/
|
||||
export default function permutateWithRepetitions(permutationOptions) {
|
||||
// There is no permutations for empty array.
|
||||
if (!permutationOptions || permutationOptions.length === 0) {
|
||||
return [];
|
||||
export default function permutateWithRepetitions(
|
||||
permutationOptions,
|
||||
permutationLength = permutationOptions.length,
|
||||
) {
|
||||
if (permutationLength === 1) {
|
||||
return permutationOptions.map(permutationOption => [permutationOption]);
|
||||
}
|
||||
|
||||
// There is only one permutation for the 1-element array.
|
||||
if (permutationOptions.length === 1) {
|
||||
return [permutationOptions];
|
||||
}
|
||||
// Init permutations array.
|
||||
const permutations = [];
|
||||
|
||||
// Let's create initial set of permutations.
|
||||
let previousPermutations = permutationOptions.map(option => [option]);
|
||||
let currentPermutations = [];
|
||||
let permutationSize = 1;
|
||||
// Go through all options and join it to the smaller permutations.
|
||||
permutationOptions.forEach((currentOption) => {
|
||||
const smallerPermutations = permutateWithRepetitions(
|
||||
permutationOptions,
|
||||
permutationLength - 1,
|
||||
);
|
||||
|
||||
// While the size of each permutation is less then or equal to options length...
|
||||
while (permutationSize < permutationOptions.length) {
|
||||
// Reset all current permutations.
|
||||
currentPermutations = [];
|
||||
smallerPermutations.forEach((smallerPermutation) => {
|
||||
permutations.push([currentOption].concat(smallerPermutation));
|
||||
});
|
||||
});
|
||||
|
||||
for (let permIndex = 0; permIndex < previousPermutations.length; permIndex += 1) {
|
||||
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;
|
||||
return permutations;
|
||||
}
|
||||
|
@ -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