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

后端 未结 4 704
青春惊慌失措
青春惊慌失措 2021-01-02 07:31

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=4

4条回答
  •  佛祖请我去吃肉
    2021-01-02 07:58

    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 inputs = Arrays.asList(19,23,41,5,40,36);
    
            int opt = 0;                // optimal solution so far          
    
            Set sums = new HashSet<>();
            sums.add(opt);
    
            // loop over all input values
            for (Integer input : inputs) {
                Set 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 subList;
    
        public SubList() {
            this(0, new ArrayList<>());
        }
    
        public SubList(int size, List subList) {
            this.size = size;
            this.subList = subList;
        }
    }
    

    The initialization now becomes:

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

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

    for (Integer input : inputs) {
        Set newSums = new HashSet<>();
    
        // loop over all sums so far                        
        for (SubList sum : sums) {
            List 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);
    }
    

提交回复
热议问题