In Java, how should I find the closest (or equal) possible sum of an Array's elements to a particular value K?
For example, for the array {19,23,41,5,40,36} and K=44, the closest possible sum is 23+19=42. I've been struggling on this for hours; I know almost nothing about dynamic programming. By the way, the array contains only positive numbers.
You can see it as a
n-choose-k
problem for all possiblek
so the complexity is exponential.K
. The set should includei
numbers, fori=1; i<=N; i++
. To implement this, for eachi
just take all then-choose-i
combinations of the numbers in the array.finalResult
variable with the best set of numbers found so far and their sum.finalResult
and update it if necessary.It reminds me of the Knapsack problem, so you may want to take a look at it.
I would say first sort the array. Then your example would be: arr = {5, 19, 23, 36, 40, 41}. Then: 1) Take arr[0] and arr[i], where i = arr.Size. Sum it and record the difference between the sum and K, if the sum is lower than K. 2) If sum > K, do step 1, but instead of arr[i], use arr[i-1], because we want to lower our sum. If sum < K, do step 1, but instead of arr[0], use arr[1], because we want to increase our sum. Keep repeating step2, by either increasing or decreasing the indices, until the indices for the two elements are equal. Then, we know the pair of elements that result in the smallest difference between the sum and K.
----------------Edited for arbitrary number of elements in the solution----------------
I believe you may need a tree. Here's what I'm thinking:
1) Choose a number as your top node.
2) For each number in the set, create a child node, and for each branch that was created, calculate the sum of that branch.
3) If the sum is less then K, we branch again, creating child nodes for all the elements in the set. If the sum is greater than K, we stop, keep the difference between the sum and K(If sum < K). If we find a branch with a better sum, then we keep that branch. Repeat this process until all branches are done branching.
Do steps 1-3 with different topnodes.
You would typically use dynamic programming for such a problem. However, that essentially boils down to keeping a set of possible sums and adding the input values one by one, as in the following code, and has the same asymptotic running time:
O(n K)
, wheren
is the size of your input array andK
is the target value.The constants in the version below are probably bigger, however, but I think the code is much easier to follow, than the dynamic programming version would be.
EDIT
A short note on running time might be useful, since I just claimed
O(n K)
without justification.Clearly, initialization and printing the result just takes constant time, so we should analyse the double loop.
The outer loop runs over all inputs, so it's body is executed
n
times.The inner loop runs over all sums so far, which could be an exponential number in theory. However, we use an upper bound of
K
, so all values insums
are in the range[0, K]
. Sincesums
is a set, it contains at mostK+1
elements.All computations inside the inner loop take constant time, so the total loop takes
O(K)
. The setnewSums
also contains at mostK+1
elements, for the same reason, so theaddAll
in the end takesO(K)
as well.Wrapping up: the outer loop is executed
n
times. The loop body takesO(K)
. Therefore, the algorithm runs inO(n K)
.EDIT 2
Upon request on how to also find the elements that lead to the optimal sum:
Instead of keeping track of a single integer - the sum of the sublist - you should also keep track of the sublist itself. This is relatively straightforward if you create a new type (no getters/setters to keep the example concise):
The initialization now becomes:
The inner loop over the
sums
needs some small adaptations as well: