Here is the code that I came up with:
static void findNumbers(int[] list, int index, int current, int goal, String result)
{
if (list.length < index || current>goal)
return;
for (int i = index; i < list.length; i++) {
if (current + list[i] == goal) {
System.out.println(result + " " + String.valueOf(list[i]));
}
else if (current + list[i] < goal) {
findNumbers(list, i + 1, current + list[i], goal, result + " " + String.valueOf(list[i]));
}
}
}
Call it using:
findNumbers(array, starting_index, current_sum_till_now, target_sum, "");
Can someone help me figure out the time complexity of this code I believe its exponential.
What is the most optimal way to solve this problem? Is it using backtrack?
Here is a way to do it using dynamic programming and knapsack analogy : -
Sort the set in ascending order
Evaluate subset till
list[i] <= N
Solve Knapsack for sack of capacity N and items having value and weight as their
list[i]
If at end knapsack capacity N == max profit then atleast one solution subset exists.
retrace all solutions for knapsack using cost matrix and get all the solution subsets.
Time complexity:
O(|S|*N + K) |S|- length of set and K is number of subsets.
This is a pseudo polynomial time algorithm.Note: Problem being NP-hard no polynomial time algorithm is yet discovered.
Edit:- Retrace solution from boolean matrix
You can modify your code to work on the principle "if a number is good - add it; disregarding any condition skip current number". In this case the code will be:
In this case the complexity will be
O(2^n)
which is better forn=>5
thenO(n!)
. As it was pointed out, the complexity is reduced if the array is sorted. Meaning you can put the 2nd recursive call inside theelse if
because you will be sure that all number that follow are greater then the currentlist[index]
meaning there is no use skipping this value because all following branches from this call will not generate any valid subsets.In this case the worst case is
O(2^l)
wherel
is either the index of a number that is greater then your goal and is in your array, orn
if such a number doesn't exist.The call should be:
findNumbers(list,0,0,goal,"")
As has just been pointed out it is worse than N^2, in fact it looks like O(N!). You will save a bit as you can exit on some of the loops early but the degree of saving would depend on how fast possibilities get eliminated.
In terms of a more optimal solutions you are going to struggle, this is an excellent case for recursion as any loop based construct is going to be horrific. You may be able to save some time by pre-sorting your data so larger values are first and hence you reach target faster (this will essentially eliminate anything in your list that is larger than your goal immediately). After the too-big entries are eliminated I am not sure if it will help directly as you still need to compare everything with everything but it might improve the processor branch predictions.
It has been pointed out that I've done a mistake. I were multiplying the complexities of recursive calls while I should have added them. So
C(N) = C(N-1) + C(N-2) + ...
. The same would then apply toC(N-1)
,C(N-2)
, etc. This means that the complexity isnt'O(N!)
.This have made me thinking on the algorithm from another point of view. It is checking every single possible subset. Since there are
2^N - 1
possible subsets (the empty subset is not taken into account), then the complexity isO(2^N)
, which I think is your original bet.