Отрабатывая ответ Мейсона, я подумал, что настроил бы его немного больше для повышения производительности, избежав накладных расходов при копировании с использованием Array.prototype.concat()
и неявного использования итератора массива с использованием for...of
:
function getCombinations (array) {
const combinations = [];
for (let i = 0; i < array.length; ++i) {
const value = array[i];
const { length } = combinations;
combinations.push([value]);
for (let j = 0; j < length; ++j) {
const oldCombination = combinations[j];
const oldLength = oldCombination.length;
const newCombination = [];
for (let k = 0; k < oldLength; ++k) {
newCombination.push(oldCombination[k]);
}
newCombination.push(value);
combinations.push(newCombination);
}
}
return combinations;
}
const input = Array.from({ length: 23 }, (_, i) => i);
const start = performance.now();
getCombinations(input);
console.log(performance.now() - start);
Ниже я предоставил тест для сравнения с ответом Мейсона:
function getCombinationsMason(array) {
let combinations = [];
for (let value of array) {
combinations = combinations
.concat(combinations.map(value0 => value0.concat([value])))
.concat([
[value]
]);
}
return combinations;
}
function getCombinationsPatrick(array) {
const combinations = [];
for (let i = 0; i < array.length; ++i) {
const value = array[i];
const {
length
} = combinations;
combinations.push([value]);
for (let j = 0; j < length; ++j) {
const oldCombination = combinations[j];
const oldLength = oldCombination.length;
const newCombination = [];
for (let k = 0; k < oldLength; ++k) {
newCombination.push(oldCombination[k]);
}
newCombination.push(value);
combinations.push(newCombination);
}
}
return combinations;
}
function average(algorithm, input, times) {
let total = 0;
for (let i = 0; i < times; ++i) {
total -= performance.now();
algorithm(input);
total += performance.now();
}
return `${algorithm.name}(): ${(total / times).toFixed(2)}ms average over ${times} times`;
}
const input = Array.from({
length: 18
}, (_, i) => i);
console.log('input.length:', input.length);
// randomize order of testing
if (Math.random() > 0.5) {
// warmup
average(getCombinationsMason, input, 2);
average(getCombinationsPatrick, input, 2);
// benchmark
console.log(average(getCombinationsMason, input, 20));
console.log(average(getCombinationsPatrick, input, 20));
} else {
// warmup
average(getCombinationsPatrick, input, 2);
average(getCombinationsMason, input, 2);
// benchmark
console.log(average(getCombinationsPatrick, input, 20));
console.log(average(getCombinationsMason, input, 20));
}