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 user I need to find all possible subsets from this set whose sum equals to zero for example in this case in the above set the subsets will be
{(1,2,-3)}
{(1,-1)}
{(3,-3)}
{(5,-5)}
etc etc
I have tried this code but it is not returning me answer when I am setting target
equal to zero.
import java.util.ArrayList;
import java.util.Arrays;
class SumSet {
static void sum_up_recursive(ArrayList<Integer> numbers, int target,
ArrayList <Integer> partial)
{
int s=0;
for (int x: partial) s += x;
if (s == target)
System.out.println("sum("+Arrays.toString(partial.toArray())+")="+target);
if (s >= target)
return;
for(int i=0;i<numbers.size();i++) {
ArrayList<Integer> remaining = new ArrayList<Integer>();
int n = numbers.get(i);
for (int j=i+1; j<numbers.size();j++) remaining.add(numbers.get(j));
ArrayList<Integer> partial_rec = new ArrayList<Integer>(partial);
partial_rec.add(n);
sum_up_recursive(remaining,target,partial_rec);
}
}
static void sum_up(ArrayList<Integer> numbers, int target)
{
sum_up_recursive(numbers,target,new ArrayList<Integer>());
}
public static void main(String args[]) {
Integer[] numbers = {3,4,6,4,5,2,6};
int target = 9;
sum_up(new ArrayList<Integer>(Arrays.asList(numbers)),target);
}
}
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.
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}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With