I am trying to build upon a problem, to solve another similar problem... given below is a code for finding the total number of subsets that sum to a particular value, and I am trying to modify the code so that I can return all subsets that sum to that value (instead of finding the count).
Code for finding the total number of suibsets that sum to 'sum':
/**
* method to return number of sets with a given sum.
**/
public static int count = 0;
public static void countSubsetSum2(int arr[], int k, int sum) {
if(sum == 0) {
count++;
return;
}
if(sum != 0 && k == 0) {
return;
}
if(sum < arr[k - 1]) {
countSubsetSum2(arr, k-1, sum);
}
countSubsetSum2(arr, k-1, sum - arr[k-1]);
countSubsetSum2(arr, k-1, sum);
}
Can someone propose some changes to this code, to make it return the subsets rather than the subset count?
A Python implementation with k moving from 0 to len() - 1:
Firstly, your code isn't correct.
The function, at every step, recurses with the sum excluding and including the current element 1, moving on to the next element, thanks to these lines:
But then there's also this:
which causes it to recurse twice with the sum excluding the current element under some circumstances (which it should never do).
Essentially you just need to remove that if-statement.
If all the elements are positive and
sum - arr[k-1] < 0
, we'd keep going, but we can never get a sum of 0 since the sum can't increase, thus we'd be doing a lot of unnecessary work. So, if the elements are all positive, we can add a check forif(arr[k - 1] <= sum)
to the first call to improve the running time. If the elements aren't all positive, the code won't find all sums.Now on to printing the sums
If you understand the code well, changing it to print the sums instead should be pretty easy. I suggest you work on understanding it a bit more - trace what the program will do by hand, then trace what you want the program to do.
And a hint for solving the actual problem: On noting that
countSubsetSum2(arr, k-1, sum - arr[k-1]);
recurses with the sum including the current element (and the other recursive call recurses with the sum excluding the current element), what you should do should become clear.1: Well, technically it's reversed (we start with the target sum and decrease to 0 instead of starting at 0 and increasing to sum), but the same idea is there.
This is the code that works:
Based, on the comments/suggestions here, I have been able to get the solution for this problem in this way:
The main method looks like this:
Output:
First off, the code you have there doesn't seem to actually work (I tested it on input
[1,2,3, ..., 10]
with a sum of3
and it output128
).To get it working, first note that you implemented the algorithm in a pretty unorthodox way. Mathematical functions take input and produce output. (Arguably) the most elegant programming functions should also take input and produce output because then we can reason about them as we reason about math.
In your case you don't produce any output (the return type is
void
) and instead store the result in a static variable. This means it's hard to tell exactly what it means to callcountSubsetSum2
. In particular, what happens if you call it multiple times? It does something different each time (because thecount
variable will have a different starting value!) Instead, if you writecountSubsetSum2
so that it returns a value then you can define its behavior to be:countSubsetSum2
returns the number of subsets of the inputarr[0...k]
that sum tosum
. And then you can try proving why your implementation meets that specification.I'm not doing the greatest job of explaining, but I think a more natural way to write it would be:
As described above, this function takes an input and outputs a value that we can define and prove to be true mathematically (caveat: it's usually not quite a proof because there are crazy edge cases in most programming languages unfortunately).
Anyways, to get back to your question. The issue with the above code is that it doesn't store any data... it just returns the count. Instead, let's generate the actual subsets while we're generating them. In particular, when I say
Any valid subset either includes arr[k]
I mean... the subset we're generating includesarr[k]
; so add it. Below I assumed that the code you wrote above is java-ish. Hopefully it makes sense:The code is pretty cluttered with all the comments; but the key point is that this implementation always returns what it's specified to return (a list of sets of ints from arr[0] to arr[k] which sum to whatever sum was passed in).
FYI, there is another approach which is "bottom-up" (i.e. doesn't use recursion) which should be more performant. If you implement it that way, then you need to store extra data in static state (a "memoized table")... which is a bit ugly but practical. However, when you implement it this way you need to have a more clever way of generating the subsets. Feel free to ask that question in a separate post after giving it a try.