to find a subset from a set whose sum equals to zero?

后端 未结 3 1700
伪装坚强ぢ
伪装坚强ぢ 2021-01-01 01:29

I have a set of integers like this

{1,4,5,2,7,8,-3,-5,-6,9,3,-7,-1,5,6} 

the set can contain any number of items as input is taken from the

相关标签:
3条回答
  • 2021-01-01 02:09

    Here is a proposition of solution.

    I first solve a first sub-problem : I suppose that all the numbers and the target are positive number then I solve the real problem.

    To achieve this, I basically decompose the problem in sub-problems.

    Let's illustrate with an example :

    Numbers : 1,3,8,2,7 target :10

    First : sort the list: Numbers : 8,7,3,2,1 target :10 Then find recursively the solutions to the following problems :

    Numbers : 7,3,2,1 target :10-8 = 2

    Numbers : 3,2,1 target :10-7=3

    Numbers : 2,1 target : 10-3=2

    Numbers : 1 target : 10-1=9

    The aim of placing big numbers before is to quickly eliminate solutions including this number (because the sum quickly exceeds the target).

    Here is the commented code for the resolution of this sub-problem:

    import java.util.ArrayList;
    import java.util.List;
    
    public class Problem {
    
        /*
         * Used at the end to recompose the solutions.
         * This value is null for the root problem.
         */
        private Integer nodeValue;
    
        //The POSITIVE target sum
        private int target;
    
        //List of POSITIVE numbers, supposed to be sorted
        private List<Integer> numbers;
    
        private List<Problem> listSubProblems;
    
        /*
         * Link to the parent problem.
         * Useful at the end to generate the results.
         */
        private Problem parentProblem;
    
        public Problem(int target, List<Integer> numbers, Integer nodeValue, Problem parentProblem){
            this.target=target;
            this.numbers=numbers;
            this.nodeValue=nodeValue;
            this.parentProblem=parentProblem;
            this.listSubProblems =new ArrayList<Problem>();
        }
    
        public void solve(){
            buildSubProblems();
            for(Problem problem : listSubProblems){
                problem.solve();
            }
        }
    
        /**
         * Builds a List of sub problems.
         * For example, if {@link #numbers} contains 9 8 5 3, with target 10
         * this method will return the following sub problems:
         *
         * <table>
         *     <tr>
         *         <td>nodeValue</td><td>target</td><td>numbers</td>
         *     </tr>
         *     <tr>
         *         <td>9</td><td>10-9=1</td><numbers>8 5 3</numbers>
         *     </tr>
         *     <tr>
         *         <td>8</td><td>10-8=2</td><numbers>5 3</numbers>
         *     </tr>
         *     <tr>
         *         <td>5</td><td>10-5=5</td><numbers>3</numbers>
         *     </tr>
         *
         * </table>
         *
         */
        private void buildSubProblems(){
    
            int numbersSize=numbers.size();
    
            /*
             * Numbers are supposed to be positive so if the target is negative,
             * there is no chance to find a valid solution.
             * As the list of numbers is sorted, the case when target < 0 happens quickly
             * Hence, it quickly removes combinations implying big numbers
             */
            if(target>=0 && numbersSize> 1){
    
                for(int i=0;i<numbersSize;i++){
                    Integer nodeValue=numbers.get(i);
                    List<Integer> subList=numbers.subList(i+1,numbersSize);
                    int newTarget=this.target-nodeValue;
                    Problem problem=new Problem(newTarget, subList, nodeValue, this);
                    System.out.println("Created problem: "+problem.dump());
                    this.listSubProblems.add(problem);
                }
            }
        }
    
        /**
         * @return True is the Problem contains exactly one number and that number equals the target.
         */
        public boolean isNodeSolution(){
            return this.target==0;
        }
    
        public Integer getNodeValue(){
            return this.nodeValue;
        }
    
        public List<Problem> getListSubProblems(){
            return this.listSubProblems;
        }
    
        public Problem getParentProblem(){
            return this.parentProblem;
        }
    
        public String dump(){
            StringBuilder sb=new StringBuilder();
            sb.append("{nodeValue: "+this.nodeValue);
            sb.append("; target: "+target);
            sb.append("; numbers:");
            for(Integer integer : numbers){
                sb.append(integer+",");
            }
            sb.append("}");
            sb.append("Valid? : "+ isNodeSolution());
            return sb.toString();
        }
    
    }
    

    Here is the code which shows how to test it:

    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    public class Main {
    
        public static void main(String[] args) throws Exception{
            Integer numbers[]={1,3,8,2,7};
            int target=10;
    
            List<Integer> listNumbers= Arrays.asList(numbers);
    
            Collections.sort(listNumbers);
            Collections.reverse(listNumbers);
    
            //Build the root problem
            Problem problem=new Problem(target,listNumbers,null,null);
    
            //Solve it
            problem.solve();
    
            //Dump the result.
            dumpResult(problem);
    
            System.out.println("Done!");
        }
    
        private static void dumpResult(Problem problem){
            for(Problem p:problem.getListSubProblems()){
                if(p.isNodeSolution()){
                    System.out.print("\nSolution :");
                    dumpSolution(p);
                }
                dumpResult(p);
            }
        }
    
        private static void dumpSolution(Problem problem){
            //If the current node is not the root problem
            if(problem.getParentProblem()!=null){
                System.out.print(problem.getNodeValue() + ", ");
                dumpSolution(problem.getParentProblem());
            }
        }
    }
    

    And here is an example of output:

    Created problem: {nodeValue: 8; target: 2; numbers:7,3,2,1,}Valid? : false
    Created problem: {nodeValue: 7; target: 3; numbers:3,2,1,}Valid? : false
    Created problem: {nodeValue: 3; target: 7; numbers:2,1,}Valid? : false
    Created problem: {nodeValue: 2; target: 8; numbers:1,}Valid? : false
    Created problem: {nodeValue: 1; target: 9; numbers:}Valid? : false
    Created problem: {nodeValue: 7; target: -5; numbers:3,2,1,}Valid? : false
    Created problem: {nodeValue: 3; target: -1; numbers:2,1,}Valid? : false
    Created problem: {nodeValue: 2; target: 0; numbers:1,}Valid? : true
    Created problem: {nodeValue: 1; target: 1; numbers:}Valid? : false
    Created problem: {nodeValue: 3; target: 0; numbers:2,1,}Valid? : true
    Created problem: {nodeValue: 2; target: 1; numbers:1,}Valid? : false
    Created problem: {nodeValue: 1; target: 2; numbers:}Valid? : false
    Created problem: {nodeValue: 2; target: -2; numbers:1,}Valid? : false
    Created problem: {nodeValue: 1; target: -1; numbers:}Valid? : false
    Created problem: {nodeValue: 2; target: 5; numbers:1,}Valid? : false
    Created problem: {nodeValue: 1; target: 6; numbers:}Valid? : false
    
    Solution :2, 8,
    Solution :3, 7, Done!
    

    Now, this does not cover the initial problem which implies negative numbers. To solve this case, isolate all negative numbers and compute all combinations of negative numbers, with there sum.

    Then, for each sum of negative number, create a sub problem containing only positive numbers and a corresponding target (initial target - sum of negative numbers)

    One way to improve it: The complexity of the problems depends on the number of combinations of negative numbers. So, if there are more negative numbers than positive numbers, you can just invert all values and solve the invert problem.

    Another way to improve it: You can maintain in memory the sum of positive numbers of each sub-problems. If sum + nodeValue < target then it is useless to continue exploring the branch.

    0 讨论(0)
  • 2021-01-01 02:09

    You aren't checking if partial is empty, in which case sum_up_recursive() will immediately return on the first try when target == 0. Try this:

    if (partial.size() > 0) {
        for (int x : partial)
            s += x;
    
        if (s == target)
            System.out.println("sum(" + Arrays.toString(partial.toArray()) + ")=" + target);
    
        if (s >= target)
            return;
    }
    

    Note, there may be other ways to vastly improve on the algorithm you are using. I am just answering why your code isn't working as expected.

    0 讨论(0)
  • 2021-01-01 02:10

    I came across this problem in my college days at Google interview and solved it in a very long way.

    Think about it, for a set to be 0 there "has" to be negative number and there "has to be a set of positive number".

    Steps:

    1. Created a 2 arrays negativeNumArrays and POsitiveNumArrays
    2. Create a new negative set(does not allows duplicate) which is possible sums of     negative arrays ex -
        [-1,-2,-3] = [-1,-2,-3, {-1-2=3},{-1,-3=-4},{-2,-3=-5},{-6}] = [-1,-2,-3,-4,-5,-6]
    So the set looked like
    Key:Value
    "1" =-1
    "2" = -2
    ...
    "2:3"=-5 
    "1:2:3"=-6
    
    Here 
    "N6" = -6
    
    3. For this new set of negative array find combination in positive 
       array which matches any of the 6 negative arrays.
    
    Same as above say positive numbers are 3 and 4
    So the set would look like
    "3"=3
    
    "4"=4
    
    "3:4"=7
    
    
    Now simple compare the two sets and see which of these are equal
    So for example Negative Set "1:3" = Positive Set "4"
    and hence use Stringtokenizer to get the numbers from set key {-1,-3,4}
    
    0 讨论(0)
提交回复
热议问题