Thursday, August 7, 2014

SubsetSum (No Repetition) Problem - II (Coin change)

As introduced in one of the previous posts (subset sum problem introduction) subset sum problem can be solved in exponential by brute force method.  Below the problem will be solved using Dynamic programming to get to pseudo polynomial time complexity. Note that coin change algorithm is nothing but one of the variation of subset sum problem.

Variation 1

Checking whether there existing a subset or not, that sums up-to the given sum (target).

Solution

Say, f[ i, w] represents whether up-to elements have a subset that sums up-to "w" using first 'i' elements, then
                f[i, w] = False i <= 0
                              True if weights[i] == w
                              f[i-1, w] if weights[i] > w
                              f[i-1, w] || f[i-1, w-weights[i]]
 
And the above function has optimal substructure property and overlapped sub problems. So, it can be solved by using Dynamic Programming.

Variation 2

What are the total number of subsets that can sum up-to given sum (target)

Solution

Say, f[i, sum] represents the number of subsets that sums up-to given sum (target) using first 'i' elements, then
                    f[i, sum] = 0 if i <= 0
                                   0 if sum < 0
                                   1 if sum == 0
                                   f[i-1, sum] + f[i-1, sum - weights[i]]
 
And above method also has optimal substructure and overlapped sub problems.

Variation 3

Find the minimal elements that required to sums to given target.

Solution

Say, f[i, sum] finds the minimal number of elements that required using up-to 'i' elements that sums up-to "sum"
                   f[i, sum] = 0 if i <= 0
                                     0 if sum < 0
                                     1 if weights[i] == sum
                                     f[i-1, w] if weights[i] > sum, Or f[i-1, w-weights[i]] == 0
                                     f[i-1, w-weights[i]] + 1 if f[i-1, w] == 0
                                     min (f[i-1, w], f[i-1, w-weights[i]] + 1)
                                    
 
Below is the corresponding code and unit tests...
 
Code Snippet
  1. public bool SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(int[] weights, int W)
  2.         {
  3.             this.Validate(weights, W);
  4.             bool[][] DP_Memoization_Cache = new bool[weights.Length + 1][];
  5.             for (int i = 0; i <= weights.Length; i++)
  6.             {
  7.                 DP_Memoization_Cache[i] = new bool[W + 1];
  8.             }
  9.             for (int i = 1; i <= weights.Length; i++)
  10.             {
  11.                 for (int w = 0; w <= W; w++)
  12.                 {
  13.                     /// f(i, w) determines if weight 'w' can be accumulated using given 'i' number of weights
  14.                     /// f(i, w) = False if i <= 0
  15.                     ///           OR True if weights[i-1] == w
  16.                     ///           OR f(i-1, w) if weights[i-1] > w
  17.                                ///OR f(i-1, w) || f(i-1, w-weights[i-1])
  18.                     if (weights[i - 1] == w)
  19.                     {
  20.                         DP_Memoization_Cache[i][w] = true;
  21.                     }
  22.                     else
  23.                     {
  24.                         DP_Memoization_Cache[i][w] = DP_Memoization_Cache[i - 1][w];
  25.                         if (!DP_Memoization_Cache[i][w])
  26.                         {
  27.                             if (w > weights[i - 1])
  28.                             {
  29.                                 DP_Memoization_Cache[i][w] = DP_Memoization_Cache[i - 1][w - weights[i - 1]];
  30.                             }
  31.                         }
  32.                     }
  33.                 }
  34.             }
  35.             return DP_Memoization_Cache[weights.Length][W];
  36.         }
  37.         public bool SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(int[] weights, int W)
  38.         {
  39.             this.Validate(weights, W);
  40.             //note 'row' index represents the number of weights been used
  41.             //note 'colum' index represents the weight that can be achived using given
  42.             //number of weights 'row'
  43.             bool?[][] DP_Memoization_Cache = new bool?[weights.Length + 1][];
  44.             for (int i = 0; i <= weights.Length; i++)
  45.             {
  46.                 DP_Memoization_Cache[i] = new bool?[W + 1];
  47.                 for (int w = 0; w <= W; w++)
  48.                 {
  49.                     if (i != 0)
  50.                     {
  51.                         DP_Memoization_Cache[i][w] = null;
  52.                     }
  53.                     else
  54.                     {
  55.                         //can't get to weight 'w' using none of the given weights
  56.                         DP_Memoization_Cache[0][w] = false;
  57.                     }
  58.                 }
  59.                 DP_Memoization_Cache[i][0] = false;
  60.             }
  61.             bool f = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(
  62.                 weights, w: W, i_rowIndexOfCache: weights.Length, DP_Memoization_Cache: DP_Memoization_Cache);
  63.             Assert.IsTrue(f == DP_Memoization_Cache[weights.Length][W]);
  64.             return f;
  65.         }
  66.         /// <summary>
  67.         /// f(i, w) determines if weight 'w' can be accumulated using given 'i' number of weights
  68.         /// f(i, w) = False if i < 0
  69.         ///           OR True if weights[i] == w
  70.         ///           OR f(i-1, w) if weights[i] > w
  71.                    ///OR f(i-1, w) || f(i-1, w-weights[i])
  72.         /// </summary>
  73.         /// <param name="rowIndexOfCache">
  74.         /// Note, its index of row in the cache
  75.         /// index of given weifhts is indexOfCahce -1 (as it starts from 0)
  76.         /// </param>
  77.         bool SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(int[] weights, int w, int i_rowIndexOfCache, bool?[][] DP_Memoization_Cache)
  78.         {
  79.             if (i_rowIndexOfCache < 0)
  80.             {
  81.                 return false;
  82.             }
  83.             if (DP_Memoization_Cache[i_rowIndexOfCache][w].HasValue)
  84.             {
  85.                 return DP_Memoization_Cache[i_rowIndexOfCache][w].Value;
  86.             }
  87.             int i_weights_index = i_rowIndexOfCache - 1;
  88.             if (weights[i_weights_index] == w)
  89.             {
  90.                 //we can just use current weight, so no need to call other recursive methods
  91.                 //just return true
  92.                 DP_Memoization_Cache[i_rowIndexOfCache][w] = true;
  93.                 return true;
  94.             }
  95.             //see if W, can be achieved without using weights[i]
  96.             bool flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights,
  97.                 w, i_rowIndexOfCache - 1);
  98.             DP_Memoization_Cache[i_rowIndexOfCache][w] = flag;
  99.             if (flag)
  100.             {
  101.                 return true;
  102.             }
  103.             if (w > weights[i_weights_index])
  104.             {
  105.                 //see if W-weight[i] can be achieved with rest of the weights
  106.                 flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights,
  107.                     w - weights[i_weights_index], i_rowIndexOfCache - 1);
  108.                 DP_Memoization_Cache[i_rowIndexOfCache][w] = flag;
  109.             }
  110.             return flag;
  111.         }
  112.         public bool SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(int[] weights, int W)
  113.         {
  114.             this.Validate(weights, W);
  115.             bool f = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, W,
  116.                 i: weights.Length - 1);
  117.             return f;
  118.         }
  119.         /// <summary>
  120.         /// f(i, w) = False if i < 0
  121.         ///           OR True if weights[i] == w
  122.         ///           OR f(i-1, w) if weights[i] > w
  123.                    ///OR f(i-1, w) || f(i-1, w-weights[i])
  124.         /// </summary>
  125.         public bool SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(int[] weights, int W, int i)
  126.         {
  127.             if (i < 0)
  128.             {
  129.                 //no more weights to traverse
  130.                 return false;
  131.             }
  132.             if (weights[i] == W)
  133.             {
  134.                 //we can just use current weight, so no need to call other recursive methods
  135.                 //just return true
  136.                 return true;
  137.             }
  138.             //see if W, can be achieved without using weights[i]
  139.             bool flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights,
  140.                 W, i - 1);
  141.             if (flag)
  142.             {
  143.                 return true;
  144.             }
  145.             if (W > weights[i])
  146.             {
  147.                 //see if W-weight[i] can be achieved with rest of the weights
  148.                 return this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, W - weights[i], i - 1);
  149.             }
  150.             return false;
  151.         }
Code Snippet
  1. [TestCategory(Constants.DynamicProgramming)]
  2.         [TestMethod]
  3.         public void SubsetSum_NoRepetition_KnapsackSimpliedProblemTests()
  4.         {
  5.             int[] weights = new int[] { 7, 5, 4, 4, 1 };
  6.             List<int> bag = null;
  7.             bool flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 10, knapsack: out bag);
  8.             Assert.IsTrue(flag);
  9.             Assert.IsTrue(bag.Contains(5));
  10.             Assert.IsTrue(bag.Contains(4));
  11.             Assert.IsTrue(bag.Contains(1));
  12.             Assert.IsTrue(bag.Count == 3);
  13.             flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 3, knapsack: out bag);
  14.             Assert.IsFalse(flag);
  15.             flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 7, knapsack: out bag);
  16.             Assert.IsTrue(flag);
  17.             Assert.IsTrue(bag.Contains(7));
  18.             Assert.IsTrue(bag.Count == 1);
  19.             flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 1, knapsack: out bag);
  20.             Assert.IsTrue(flag);
  21.             Assert.IsTrue(bag.Contains(1));
  22.             Assert.IsTrue(bag.Count == 1);
  23.  
  24.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 10);
  25.             Assert.IsTrue(flag);
  26.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 3);
  27.             Assert.IsFalse(flag);
  28.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 7);
  29.             Assert.IsTrue(flag);
  30.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 1);
  31.             Assert.IsTrue(flag);
  32.  
  33.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 10);
  34.             Assert.IsTrue(flag);
  35.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 3);
  36.             Assert.IsFalse(flag);
  37.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 7);
  38.             Assert.IsTrue(flag);
  39.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 1);
  40.             Assert.IsTrue(flag);
  41.  
  42.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 10);
  43.             Assert.IsTrue(flag);
  44.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 3);
  45.             Assert.IsFalse(flag);
  46.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 7);
  47.             Assert.IsTrue(flag);
  48.             flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 1);
  49.             Assert.IsTrue(flag);
  50.  
  51.  
  52.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 10, knapsack: out bag);
  53.             Assert.IsTrue(flag);
  54.             Assert.IsTrue(bag.Contains(5));
  55.             Assert.IsTrue(bag.Contains(4));
  56.             Assert.IsTrue(bag.Contains(1));
  57.             Assert.IsTrue(bag.Count == 3);
  58.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 3, knapsack: out bag);
  59.             Assert.IsFalse(flag);
  60.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 7, knapsack: out bag);
  61.             Assert.IsTrue(flag);
  62.             Assert.IsTrue(bag.Contains(7));
  63.             Assert.IsTrue(bag.Count == 1);
  64.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 1, knapsack: out bag);
  65.             Assert.IsTrue(flag);
  66.             Assert.IsTrue(bag.Contains(1));
  67.             Assert.IsTrue(bag.Count == 1);
  68.  
  69.             weights = new int[] { 11, 8, 7, 6, 5 };
  70.             flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 20, knapsack: out bag);
  71.             Assert.IsTrue(bag.Contains(8));
  72.             Assert.IsTrue(bag.Contains(7));
  73.             Assert.IsTrue(bag.Contains(5));
  74.             Assert.IsTrue(bag.Count == 3);
  75.             flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 27, knapsack: out bag);
  76.             Assert.IsFalse(flag);
  77.             flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 11, knapsack: out bag);
  78.             Assert.IsTrue(flag);
  79.             Assert.IsTrue(bag.Contains(11));
  80.             Assert.IsTrue(bag.Count == 1);
  81.             flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 5, knapsack: out bag);
  82.             Assert.IsTrue(flag);
  83.             Assert.IsTrue(bag.Contains(5));
  84.             Assert.IsTrue(bag.Count == 1);
  85.  
  86.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 20, knapsack: out bag);
  87.             Assert.IsTrue(bag.Contains(8));
  88.             Assert.IsTrue(bag.Contains(7));
  89.             Assert.IsTrue(bag.Contains(5));
  90.             Assert.IsTrue(bag.Count == 3);
  91.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 27, knapsack: out bag);
  92.             Assert.IsFalse(flag);
  93.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 11, knapsack: out bag);
  94.             Assert.IsTrue(flag);
  95.             Assert.IsTrue(bag.Contains(11));
  96.             Assert.IsTrue(bag.Count == 1);
  97.             flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 5, knapsack: out bag);
  98.             Assert.IsTrue(flag);
  99.             Assert.IsTrue(bag.Contains(5));
  100.             Assert.IsTrue(bag.Count == 1);
  101.         }
 

No comments:

Post a Comment