find a solution to subset sum using dynamic programming
Asked Answered
A

3

5

What I want to do

I want to find a subset of an array that sums to a target T. I also want to use to a dynamic programming approach (and a bottom-up solution at that) to do this.

What I currently have

Currently I only found a way to see if amongst all subsets of size N, whether or not there is at least one subset that has the desired sum. See code below.

public boolean solve(int[] numbers, int target) {

    //Safeguard against invalid parameters
    if ((target < 0) || (sum(numbers) < target)){
        return false;
    }

    boolean [][] table = new boolean [target + 1] [numbers.length + 1]  ;

    for (int i = 0; i <= numbers.length; ++i) {
        table[0][i] = true;
    }


    /* Base cases have been covered. 
     * Now look set subsets [1..n][target] to be true or false.
     * n represents the number of elements from the start that have a subset 
     * that sums to target
     */
    for (int i = 1; i <= target; ++i){
        for (int j = 1; j <= numbers.length; ++j){
            /* Mark index j as one of the numbers in the array
             *  which is part of the solution with the given subtarget */
            table [i][j] = table[i][j-1];
            if (i >= numbers[j-1])
                table[i][j] = table[i][j] || table[i - numbers[j-1]] [j-1];
        }
    }

    return table[target][numbers.length];
}

Where I am stuck

Right now, I know if there is a solution, but I can't think of a way to actually output a solution.

I am not looking for anyone to provide me specific code, but pseudocode is welcome as are hints to how a solution may be saved.

Amalberga answered 15/9, 2013 at 23:10 Comment(3)
subset sum is an np complete problem, are you sure of what are you doing?Backhanded
@Backhanded : subset sum is np complete. But why is that relevant? np-complete only means that it is impossible to solve in polynomial time. Dynamic programming in this case provides a pseudo-polynomial run-time (ie. somewhat fast for sufficiently small T).Amalberga
this means you will find an approssimation, or that your algorithm will be a good heuristic to reach te solution: i am not sure you will find the right answer in this way. Anyhow if you reach the solution, you can save movement throught the table with an array telling you the reverse path from top to bottom in your matrix.. should work..Backhanded
W
7

The algorithm you provided can stay the same, you don't need to store anything else besides the DP-table table[][]. You just need an additional post-processing phase in which you step "backwards" through table[][] to get the solution set.

Just to recall:

You've computed the table table[i][j], which stores for every value 0<=i<=t(:=target) and every 0<=j<=n(:=numbers.length) whether there is a subset of numbers in numbers[0..j-1] that sum to i.

Consider the subset S corresponding to table[i][j] (, which is true). Note that:

  • The subset S contains the number numbers[j] only if table[ i-numbers[j] ][j-1] is true.

    (Proof: recursively take the solution subset S' for table[ i-numbers[j] ][j-1], and add numbers[j])

  • On the other hand, this subset S does not contain the number numbers[j] only if table[ i-numbers[j] ][j-1] is false.

    (Proof: assume S contains numbers[j], trow numbers[j] out of S, this implies table[ i-numbers[j] ][j-1], contradiction)

So to get the subset, simply use the above property to check whether numbers[n-1] is in the subset summing to t.
  • If so, recursively compute whether numbers[n-2] is in the subset summing to t-numbers[n-1],
  • else recursively compute whether numbers[n-2], is in the subset summing to t
Waller answered 30/9, 2013 at 18:44 Comment(1)
What if we want all the possible sets ?Infinitude
P
3

Here are the two Java solutions for the subset sum problem.
First using Recursive Approach.
Second using Dynamic Programming Approach.

/*
Question: Given a set of non-negative integers, and a value sum, determine if there is a subset of the given set 
with sum equal to given sum.

Examples: set[] = {3, 34, 4, 12, 5, 2}, sum = 9
Output:  True  //There is a subset (4, 5) with sum 9.
Let isSubSetSum(int set[], int n, int sum) be the function to find whether there is a subset of set[] with 
sum equal to sum. n is the number of elements in set[].


*/
package SubsetSumProblem;

import java.util.Scanner;

public class UsingResursiveAndDPApproach {
public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    try{
        System.out.println("Enter the number of elements in the array");
        int n =in.nextInt();
        System.out.println("Enter the elements of the array");
        int[] a=new int[n];
        for(int i=0;i<n;i++)
            a[i]=in.nextInt();
        System.out.println("Enter the sum, which you need to find");
        int sum = in.nextInt();
        System.out.println("Using recursion, the result is: "+usingRecursion(a,a.length,sum));
        System.out.println("Using Dynamic Programming, the result is: "+usingDP(a,sum));
    }
    finally{
        in.close();
    }
}


private static boolean usingRecursion(int[] a,int length, int sum) {

    // 1. Base Cases
    if(sum==0)
        return true;
    if(length==0 && sum!=0)
        return false;

    // 2. To avoid unnecessary steps, we will optimize the recursion method by avoiding 
    //    recursive calls to areas where we are definite that we can SAFELY ignore the case since
    //    the SOLUTION does not exist there.

    // If last element is greater than sum, then ignore it
    if(a[a.length-1]>sum)
        return usingRecursion(a,length-1,sum);

    // 3. This is the recursion step where we will call the method again and again
    /* else, check if sum can be obtained by any of the following
    (a) including the last element
    (b) excluding the last element   */
    return (usingRecursion(a, length-1, sum-a[length-1])|| usingRecursion(a, length-1, sum));

}
/*
Analysis:
    Time Complexity = O(2^n)
    Space Complexity =       // Don't know
*/

private static boolean usingDP(int[] a, int sum) {
    // using boolean matrix for DP
    boolean dp[][] = new boolean[a.length+1][sum+1];  // +1 in row and column


    // if the length of the array is variable (and sum is 0) then fill TRUE, since the SUM=0 
    for(int row=0;row<dp.length;row++){
        dp[row][0] = true;    // NOTE: dp[length=VARIABLE][sum=0], thus we satisfy the condition where length is VARIABLE
                              // and the SUM=0
    }

    // if the SUM is variable and length is 0 then FALSE, since (sum=variable && length=0)
    for(int column=1;column<dp[0].length;column++){
        dp[0][column] = false;  // NOTE: dp[length=0][sum=VARIABLE], thus we satisfy the condition where 
                                // (length=0 && sum=variable)
    }

    for(int i=1;i<dp.length;i++){
        for(int j=1;j<dp[0].length;j++){


            /* Check if sum can be obtained by any of the following
              (a) including the last element
              (b) excluding the last element   */


            // VERY VERY IMP: This is same as "excluding the last element" which is represented in DP 
            dp[i][j] = dp[i-1][j]; // the current position[i][j] would be same as previous position.
                                   // the previous position means that SUM is ACHIEVED OR NOT-ACHIEVED
                                   // int the previous position then it will ofcourse be ACHIEVED or NOT-ACHIEVED
                                   // in the current position.


            // VERY VERY IMP: This is same as "including the last element" which is represented in DP 
            // if the column[ sum is represented in column of the matrix i.e this sum exist] > = sum-a[last_index]
            // then decrease the sum
            if(j>=a[i-1])   // i.e sum >= array[last index element]. If it is true then include this last element by
                            // deducting it from the total sum
                dp[i][j] = dp[i][j] || dp[i-1][j-a[i-1]];  // VERY VERY IMP NOTE: Here dp[i][j] on R.H.S represent
                            // dp[i-1][j] which we have assigned in the previous step

        }
    }
    return dp[a.length][sum];
}
/*
Analysis:
    Time Complexity = O(a.length*sum)
    Space Complexity = O(a.length*sum)
*/
}
Purree answered 10/1, 2015 at 20:53 Comment(0)
C
2

Here is my solution is an iterative dp, but with only one dimension: Hope it can help you.

#include <iostream>
#include <cstring>

using namespace std;

const int maxN=1000;
int memo[maxN];
int pi[maxN];

int main(){
    int a[]={7,8,5,1,4};
    memset(memo,-1,sizeof memo);
    memset(pi,-1,sizeof pi);
    int n;
    cin>>n;
    memo[0]=0;
    pi[0]=0;
    for(int i=0;i<(int)sizeof(a)/4;i++){
        for(int num=n;num>=0;num--){
            if(num-a[i]>=0 and memo[num-a[i]]!=-1 and (memo[num]==-1 or memo[num]>1+memo[num-a[i]])){
                memo[num]=1+memo[num-a[i]]; 
                pi[num]=num-a[i];           
            }
        }
    }   
    int N=n;
    while(N!=0){
        cout<<N-pi[N]<<" ";
        N=pi[N];
    }
    cout<<endl;
    cout<<memo[n]<<endl;
    return 0;
}
Chrystalchryste answered 17/9, 2013 at 21:12 Comment(3)
just in case, pi means the 'parent' of the number, and memo stores the minimum amount of numbers to sum the target T. sorry for the C++ code, but I still haven't learned java.Chrystalchryste
I can't compile C++, but all I got was a seg fault :< (when I ran it on codepad.org)Amalberga
Uhm, Try to use this ideone.com and don't forget to insert an input in the stdin, for example 13. In the code I forgot to use the condition if is impossible to reach the target T, just add a conditional if(memo[N]==-1) print('No Solution').Chrystalchryste

© 2022 - 2024 — McMap. All rights reserved.