Я бы перебрал возможные биты числа.Например, если есть 3 аргумента, то есть 3 бита, и наибольшее число, представляемое этими битами, равно 2 ** 3 - 1
или 7 (когда установлены все 3 бита, 111
или 1 + 2 + 4).Затем выполните итерацию от 0 до 7 и проверьте, установлен ли каждый битовый индекс.
Например, на первой итерации, когда число равно 0, биты равны 000
, что соответствует +++
- добавьте все 3 аргумента вверх.
На второй итерации, когда число равно 1, биты равны 001
, что соответствует -++
, поэтому вычтите первый аргумент и добавьте два других аргумента.
Третья итерация будет иметь 2
, или 010
, или +-+
.
Третья итерация будет иметь 3
, или 011
, или +--
.
Третья итерация будет иметь 4
, или 100
, или -++
.
Продолжить паттерн до конца, сохраняя при этом общее значение, ближайшее к нулю..
Вы также можете немедленно вернуться, если подытог 0 найден, если хотите.
const sumAll = (...args) => {
const limit = 2 ** args.length - 1; // eg, 2 ** 3 - 1 = 7
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
// eg '000', or '001', or '010', or '011', or '100', etc
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = 0;
console.log('i:', i, 'bitStr:', bitStr);
args.forEach((arg, bitPos) => {
if (bitStr[args.length - 1 - bitPos] === '0') {
console.log('+', arg);
subtotal += arg;
} else {
console.log('-', arg);
subtotal -= arg;
}
});
console.log('subtotal', subtotal);
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
Вы можете "упростить", заменив [args.length - 1 - bitPos]
на [bitPos]
для того же результата, но это будет выглядеть немного более запутанным - например, 3
(011
, или +--
), станет 110
(--+
).
Это намного короче без всех журналов, которые демонстрируют, что код работает должным образом:
const sumAll = (...args) => {
const limit = 2 ** args.length - 1;
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = 0;
args.forEach((arg, bitPos) => {
subtotal += (bitStr[bitPos] === '0' ? -1 : 1) * arg;
});
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
Вы можете сократить количество операций вдвое, произвольно выбрав знак для первой цифры.Например.в настоящее время с sumAll(9, 1)
оба ответа 8
(9 - 1
) и -8
(1 - 9
) будут действительны, потому что они оба одинаково близки к 0. Независимо от ввода, если +-
производит число, ближайшее к 0, затем -+
также, только с противоположным знаком.Точно так же, если ++---
выдает число, близкое к 0, то --+++
также с противоположным знаком.Выбирая знак для первой цифры, вы можете заставить расчетный результат иметь только один знак, но это не повлияет на расстояние результата алгоритма от 0.
Это немногоулучшения (например, 10 аргументов, 2 ** 10 - 1
-> 1023 итерации улучшаются до 2 ** 9 - 1
-> 511 итераций), но это нечто.
const sumAll = (...args) => {
let initialDigit = args.shift();
const limit = 2 ** args.length - 1;
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = initialDigit;
args.forEach((arg, bitPos) => {
subtotal += (bitStr[bitPos] === '0' ? -1 : 1) * arg;
});
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));