Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I find the closest possible sum of an Array's elements to a particular value?

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.

like image 754
Karim El Sheikh Avatar asked Apr 15 '13 18:04

Karim El Sheikh


People also ask

How do you find the nearest sum of an array?

If we can create an array of sums (like in the previous approach) we can then find the closest value to the target in that array using a binary search, and then output the pair that is the same index as the closest value in sums to get our closest pair.

How do you find the Subarray that has sum closest to zero or a certain value T in O Nlogn?

Calculate the prefix sum array S[], where S[i] = A[0]+A[1]+... +A[i]. And then sort this S according to the element value, along with its original index information kept, to find subarray sum closest to 0, just iterate the S array and do the diff of the two neighboring values and update the minimum absolute diff.

What is the time complexity to identify whether the sum of any two elements in a sorted array is less than a given threshold or not *?

Time Complexity: O(n), where n is the length of an Array.

How do you find the middle element of an array by value?

Given an integer array of size n and a number k. If the indexing is 1 based then the middle element of the array is the element at index (n + 1) / 2, if n is odd otherwise n / 2.


2 Answers

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), where n is the size of your input array and K 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.

public class Test {
    public static void main(String[] args) {
        int K = 44;
        List<Integer> inputs = Arrays.asList(19,23,41,5,40,36);

        int opt = 0;                // optimal solution so far          

        Set<Integer> sums = new HashSet<>();
        sums.add(opt);

        // loop over all input values
        for (Integer input : inputs) {
            Set<Integer> newSums = new HashSet<>();

            // loop over all sums so far                        
            for (Integer sum : sums) {
                int newSum = sum + input;

                // ignore too big sums
                if (newSum <= K) {
                    newSums.add(newSum);

                    // update optimum                       
                    if (newSum > opt) {
                        opt = newSum;
                    }
                }
            }

            sums.addAll(newSums);
        }

        System.out.println(opt);
    }
}

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 in sums are in the range [0, K]. Since sums is a set, it contains at most K+1 elements.

All computations inside the inner loop take constant time, so the total loop takes O(K). The set newSums also contains at most K+1 elements, for the same reason, so the addAll in the end takes O(K) as well.

Wrapping up: the outer loop is executed n times. The loop body takes O(K). Therefore, the algorithm runs in O(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):

public class SubList {
    public int size;
    public List<Integer> subList;

    public SubList() {
        this(0, new ArrayList<>());
    }

    public SubList(int size, List<Integer> subList) {
        this.size = size;
        this.subList = subList;
    }
}

The initialization now becomes:

SubList opt = new SubList();

Set<SubList> sums = new HashSet<>();
sums.add(opt);  

The inner loop over the sums needs some small adaptations as well:

for (Integer input : inputs) {
    Set<SubList> newSums = new HashSet<>();

    // loop over all sums so far                        
    for (SubList sum : sums) {
        List<Integer> newSubList = new ArrayList<>(sum.subList);
        newSubList.add(input);
        SubList newSum = new SubList(sum.size + input, newSubList);         

        // ignore too big sums
        if (newSum.size <= K) {
            newSums.add(newSum);

            // update optimum                       
            if (newSum.size > opt) {
                opt = newSum;
            }
        }
    }

    sums.addAll(newSums);
}
like image 101
Vincent van der Weele Avatar answered Sep 20 '22 16:09

Vincent van der Weele


You can see it as a n-choose-k problem for all possible k so the complexity is exponential.

  1. Find a set of numbers that sum at most up to K. The set should include i numbers, for i=1; i<=N; i++. To implement this, for each i just take all the n-choose-i combinations of the numbers in the array.
  2. Keep a finalResult variable with the best set of numbers found so far and their sum.
  3. Compare each sub-result of step 1 with the finalResult and update it if necessary.

It reminds me of the Knapsack problem, so you may want to take a look at it.

like image 45
zafeiris.m Avatar answered Sep 17 '22 16:09

zafeiris.m