Abordarea mea este aceea de a folosi o funcție recursivă pentru a sparge problema în probleme mai mici, pornind de la dreapta la stânga.
O reprezentare vizuală a ce se întâmplă:
[1,2,3,4,5,6,7,8,9] = 100
[[1,2,3,4,5,6,7,8] + 9] = 100
[[[1,2,3,4,5,6,7] + 8] + 9] = 100
[[[[1,2,3,4,5,6] + 7] + 8] + 9] = 100
[[[[1,2,3,4,5,6] - 7] + 8] + 9] = 100
...
... [[[[[[[[1] + 2] + 3] - 4] + 5] + 6] + 78] + 9] = 100
...
[[[1,2,3,4,5,6,7] - 8] + 9] = 100
[[[[1,2,3,4,5,6] + 7] - 8] + 9] = 100
[[[[1,2,3,4,5,6] - 7] - 8] + 9] = 100
...
[[1,2,3,4,5,6,7,8] - 9] = 100
[[[1,2,3,4,5,6,7] + 8] - 9] = 100
[[[1,2,3,4,5,6,7] - 8] - 9] = 100
...
[[1,2,3,4,5,6,7] + 89] = 100
[[[1,2,3,4,5,6] + 7] + 89] = 100
[[[1,2,3,4,5,6] - 7] + 89] = 100
...
[[1,2,3,4,5,6,7] - 89] = 100
[[[1,2,3,4,5,6] + 7] - 89] = 100
[[[1,2,3,4,5,6] - 7] - 89] = 100
Codul este un pic optimizat prin faptul că nu încearcă toate combinațiile posibile și sare peste cele rezultate atunci când numărul maxim ce poate fi obținut dintr-un subset este mai mic decât cel necesar pentru a obține rezultatul dorit. De exemplu, codul nu mai încearcă combinațiile pentru subsetul [1,2,3,4]
unde
[[1,2,3,4] - 56789] = 100
sau [1,2,3]
unde
[[[1,2,3] + 567] - 89] = 100
Asta a dus execuția la ~30 ms
față de ~500 ms
cât îi ia codului lui @asdfilesystem pe laptop-ul meu (nu e nici un reproș sau altceva, doar o comparație ).
function _sum(numbers, total) {
// numărul maxim ce poate fi obținut prin concatenarea elementelor
let max = parseInt(numbers.join(''), 10);
let combinations = [];
// dacă numărul maxim este mai mic decât totalul,
// nu există nici o combinație posibilă
if (max < Math.abs(total)) {
return [];
}
// dacă numărul maxim este egal cu totalul,
// se returnează singura combinație posibilă
if (max == total) {
return [[max]];
}
// se împarte recursiv setul în subseturi mai mici, de la dreapta la stânga
for (let cursor = numbers.length - 1; cursor > 0; cursor--) {
// rezultatul concatenării numerelor din dreapta
let target = parseInt(numbers.slice(cursor).join(''), 10);
// subsetul rămas după eliminarea numerelor de după cursor
let values = numbers.slice(0, cursor);
// suma subsetului trebuie să fie `total +/- target`
for (let sign of [-1, 1]) {
let right = target * sign;
let left = _sum(values, total - right);
for (let subset of left) {
// la fiecare combinație posibilă din subset se adaugă
// elementele din dreapta cursorului
combinations.push(subset.concat([right]));
}
}
}
return combinations;
}
let results = _sum([1,2,3,4,5,6,7,8,9], 100);
// =============================================================================
// afișare rezultate
// =============================================================================
console.log('Combinații posibile:', results.length);
for (let result of results) {
console.log(result, result.reduce((carry, value) => carry + value, 0));
}
// Combinații posibile: 11
// [ 123, -4, -5, -6, -7, 8, -9 ] 100
// [ 123, 45, -67, 8, -9 ] 100
// [ 1, 23, -4, 5, 6, 78, -9 ] 100
// [ 1, 2, 34, -5, 67, -8, 9 ] 100
// [ 1, 23, -4, 56, 7, 8, 9 ] 100
// [ 12, 3, -4, 5, 67, 8, 9 ] 100
// [ 1, 2, 3, -4, 5, 6, 78, 9 ] 100
// [ 123, 4, -5, 67, -89 ] 100
// [ 12, 3, 4, 5, -6, -7, 89 ] 100
// [ 12, -3, -4, 5, -6, 7, 89 ] 100
// [ 123, -45, -67, 89 ] 100
Dacă tot punem și screenshot-uri…