Say we have an integer, position that equals 1850
We also have an array of integers, components that equals [150, 200, 500]
We can reach zero by:
components[2]
from position
three times (500*3), making it 350components[1]
from position
, making it 150components[0]
from position
, making it 0Is there an algorithm to determine the best way to reach zero from a number by subtracting only allowed numbers?
It would be ideal if the solution uses the least number of components / subtractions possible.
function getCombinations(array, rest) {
function test(state, col) {
let best = state;
const value = array[col];
// what's the most I can subtract with this value?
const max = Math.floor(state.rest / value);
// if this is the last column to check (0), subtracting 4*value, 3*, 2*, ...
// will not yield a better result that subtracting 5*value
// so check only the max value.
const min = col ? 0 : max;
for (let i = max; i >= min; --i) {
const tmp = i ? {
...state,
[value]: i,
rest: state.rest - value * i
} : state;
const item = col ? test(tmp, col - 1) : tmp;
// no need to check any further
if (item.rest === 0) return item;
if (item.rest < best.rest) best = item;
}
// log checked state; since this function is recursive,
// avoid logging the same `best` for each `col`
col || console.log("checked", best);
return best;
}
// copy and sort ASC
array = [...array].sort((a, b) => a - b);
const state = { rest };
array.forEach(value => state[value] = 0); // optional
// going from right to left (hi to lo) so that I don't have to
// check `col < array.length-1` all the time.
// this way I can check for `col !== 0` or simply `col?`
return test(state, array.length - 1);
}
// get 3 random values
const set = new Set();
while (set.size < 3) set.add(Math.floor(Math.random() * 999 + 1));
console.log("values", ...set);
console.log(getCombinations(set, 2250));
technically this is a brute-force algorithm trying combinations but with some short circuiting. Like avoiding some pointless combinations and returning early when a combination is found with remainder: 0.
And it works backwards from highest to lowest value, trying to reduce the remainder as much as possible and then checking if there is a better result with a lower count for this value to ensure that when there are multiple possible results it finds the one with the lowest count first.