Add combinations.

This commit is contained in:
Oleksii Trekhleb 2018-04-21 09:24:16 +03:00
parent 3e5e78d291
commit a3697c56fd
2 changed files with 107 additions and 0 deletions

View File

@ -0,0 +1,40 @@
import combineWithoutRepetitions from '../combineWithoutRepetitions';
import factorial from '../../../math/factorial/factorial';
describe('combineWithoutRepetitions', () => {
it('should combine string without repetitions', () => {
expect(combineWithoutRepetitions('AB', 3)).toEqual([]);
expect(combineWithoutRepetitions('AB', 1)).toEqual(['A', 'B']);
expect(combineWithoutRepetitions('A', 1)).toEqual(['A']);
expect(combineWithoutRepetitions('AB', 2)).toEqual(['AB']);
expect(combineWithoutRepetitions('ABC', 2)).toEqual(['AB', 'AC', 'BC']);
expect(combineWithoutRepetitions('ABC', 3)).toEqual(['ABC']);
expect(combineWithoutRepetitions('ABCD', 3)).toEqual([
'ABC',
'ABD',
'ACD',
'BCD',
]);
expect(combineWithoutRepetitions('ABCDE', 3)).toEqual([
'ABC',
'ABD',
'ABE',
'ACD',
'ACE',
'ADE',
'BCD',
'BCE',
'BDE',
'CDE',
]);
const combinationOptions = 'ABCDEFGH';
const combinationSlotsNumber = 4;
const combinations = combineWithoutRepetitions(combinationOptions, combinationSlotsNumber);
const n = combinationOptions.length;
const r = combinationSlotsNumber;
const expectedNumberOfCombinations = factorial(n) / (factorial(r) * factorial(n - r));
expect(combinations.length).toBe(expectedNumberOfCombinations);
});
});

View File

@ -0,0 +1,67 @@
/*
@see: https://stackoverflow.com/a/127898/7794070
Lets say your array of letters looks like this: "ABCDEFGH".
You have three indices (i, j, k) indicating which letters you
are going to use for the current word, You start with:
A B C D E F G H
^ ^ ^
i j k
First you vary k, so the next step looks like that:
A B C D E F G H
^ ^ ^
i j k
If you reached the end you go on and vary j and then k again.
A B C D E F G H
^ ^ ^
i j k
A B C D E F G H
^ ^ ^
i j k
Once you j reached G you start also to vary i.
A B C D E F G H
^ ^ ^
i j k
A B C D E F G H
^ ^ ^
i j k
...
*/
/**
* @param {string} combinationOptions
* @param {number} combinationLength
* @return {string[]}
*/
export default function combineWithoutRepetitions(combinationOptions, combinationLength) {
// If combination length is just 1 then return combinationOptions.
if (combinationLength === 1) {
return Array.from(combinationOptions);
}
// Init combinations array.
const combinations = [];
for (let i = 0; i <= (combinationOptions.length - combinationLength); i += 1) {
const smallerCombinations = combineWithoutRepetitions(
combinationOptions.substr(i + 1),
combinationLength - 1,
);
for (let j = 0; j < smallerCombinations.length; j += 1) {
combinations.push(combinationOptions[i] + smallerCombinations[j]);
}
}
// Return all calculated combinations.
return combinations;
}