Thursday, August 7, 2014

Knapsack (0/1, unbound) Problem

Excerpt from wiki (http://en.wikipedia.org/wiki/Knapsack_problem):

The knapsack problem or rucksack problem is a problem in combinatorial optimization: Given a set of items, each with a mass and a value, determine the number of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible. It derives its name from the problem faced by someone who is constrained by a fixed-size knapsack and must fill it with the most valuable items.

Knapsack 0/1

Basically the constraint is an item can be selected at most only once.

say f[i, w] is the maximum value that can be obtained using up-to 'i' elements so that sum of their weights is at most 'w'

f[i, w] = 0 if i <= 0
              0 if w == 0
              f[i-1, w] if weights[i] > w
              max (f[i-1, w], f[i-1, w-weights[i]] + values[i])

Unbound Knapsack

basically there is no bound on the number of times an item can be selected
 
f[i, w] = 0 if i <= 0
              0 if w == 0
              f[i-1, w] if weights[i] > w
              max (f[i-1, w], f[i, w-weights[i]]+ vi)
 
 
 Below is the corresponding code -

Using Memoization


Code Snippet
  1. public int Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy(int[] weights, int[] values, int maxWeight)
  2.         {
  3.             this.ValidataInput_Knapsack_0_1(weights, values, maxWeight);
  4.             int?[][] DP_Memoization_Max_Value_Cache = new int?[values.Length+1][];
  5.             for(int i = 0; i<=values.Length; i++)
  6.             {
  7.                 DP_Memoization_Max_Value_Cache[i] = new int?[maxWeight+1];
  8.                 for(int j = 0; j <= maxWeight; j++)
  9.                 {
  10.                     DP_Memoization_Max_Value_Cache[i][j] = null; //yes, its default - just to be defensive
  11.                     if(i == 0)
  12.                     {
  13.                         DP_Memoization_Max_Value_Cache[0][j] = 0;
  14.                     }
  15.                 }
  16.                 //value for weight zero is zero :)
  17.                 DP_Memoization_Max_Value_Cache[i][0] = 0;
  18.             }
  19.             var v = this.Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy_Recursive(weights, values,
  20.                 index: values.Length, weight: maxWeight,
  21.                 DP_Memoization_Max_Value_Cache: DP_Memoization_Max_Value_Cache);
  22.             Assert.IsTrue(v == DP_Memoization_Max_Value_Cache[values.Length][maxWeight]);
  23.             return v;
  24.         }
  25.         /// <summary>
  26.         /// f(i, w) = f(i-1, w) if Wi > w
  27.                  ///Or, max (f(i-1, w), f(i-1, w-Wi) + Vi
  28.                  ///Or 0 if i < 0
  29.         /// </summary>
  30.         int Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy_Recursive(int[] weights, int[] values,
  31.             int index, int weight, int?[][] DP_Memoization_Max_Value_Cache)
  32.         {
  33.             if (index < 0)
  34.             {
  35.                 return 0;
  36.             }
  37.             Debug.Assert(weight >= 0);
  38. #if DEBUG
  39.             if ((index == 0) || (weight == 0))
  40.             {
  41.                 Debug.Assert(DP_Memoization_Max_Value_Cache[index][weight] == 0);
  42.             }
  43. #endif
  44.             //value is cached, so return
  45.             if(DP_Memoization_Max_Value_Cache[index][weight] != null)
  46.             {
  47.                 return DP_Memoization_Max_Value_Cache[index][weight].Value;
  48.             }
  49.             Debug.Assert(index > 0);
  50.             Debug.Assert(weight > 0);
  51.             int maxValueWithoutIncludingCurrentItem = this.Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy_Recursive
  52.                 (weights, values, index - 1, weight, DP_Memoization_Max_Value_Cache);
  53.             if (weights[index-1] > weight)
  54.             {
  55.                 DP_Memoization_Max_Value_Cache[index][weight] = maxValueWithoutIncludingCurrentItem;
  56.                 //current item weight is more, so we cant include - so, just return
  57.                 return maxValueWithoutIncludingCurrentItem;
  58.             }
  59.             int overallMaxValue = maxValueWithoutIncludingCurrentItem;
  60.             int maxValueIncludingCurrentItem = this.Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy_Recursive
  61.                 (weights, values, index - 1, weight - weights[index-1],
  62.                     DP_Memoization_Max_Value_Cache) + values[index - 1];
  63.             if(maxValueIncludingCurrentItem > overallMaxValue)
  64.             {
  65.                 overallMaxValue = maxValueIncludingCurrentItem;
  66.             }
  67.             DP_Memoization_Max_Value_Cache[index][weight] = overallMaxValue;
  68.             return overallMaxValue;
  69.         }
  70.         

Bottom Up (Tabular) - Eager approach

Code Snippet
  1. public int Knapsack_0_1_DP_Tabular_Bottom_Up(int[] weights, int[] values, int maxWeight)
  2.         {
  3.             this.ValidataInput_Knapsack_0_1(weights, values, maxWeight);
  4.             int[][] DP_Tabular_Eager_Max_Value_Cache = new int[values.Length + 1][];
  5.             for (int i = 0; i <= values.Length; i++)
  6.             {
  7.                 DP_Tabular_Eager_Max_Value_Cache[i] = new int[maxWeight + 1];
  8.                 for (int j = 0; j <= maxWeight; j++)
  9.                 {
  10.                     DP_Tabular_Eager_Max_Value_Cache[i][j] = 0; //yes, its default -
  11.                 }
  12.             }
  13.             /// f(i, w) = f(i-1, w) if Wi > w
  14.                      ///Or, max (f(i-1, w), f(i-1, w-Wi) + Vi
  15.                      ///Or 0 if i < 0
  16.             for(int i = 1; i<=values.Length; i++)
  17.             {
  18.                 for(int w = 1; w <= maxWeight; w++)
  19.                 {
  20.                     //below code can be refined - intentional as i just want it
  21.                     //look similar to other 2 versions (top_to_bottom_momoization
  22.                     //and recursive_without_resuing_subproblem_solution
  23.                     int maxValueWithoutIncludingCurrentItem =
  24.                             DP_Tabular_Eager_Max_Value_Cache[i - 1][w];
  25.                     if (weights[i - 1] > w)
  26.                     {
  27.                         DP_Tabular_Eager_Max_Value_Cache[i][w] = maxValueWithoutIncludingCurrentItem;
  28.                     }
  29.                     else
  30.                     {
  31.                         int maxValueByIncludingCurrentItem =
  32.                             DP_Tabular_Eager_Max_Value_Cache[i - 1][w - weights[i - 1]]
  33.                             + values[i-1];
  34.                         int overAllMax = maxValueWithoutIncludingCurrentItem;
  35.                         if(maxValueByIncludingCurrentItem > overAllMax)
  36.                         {
  37.                             overAllMax = maxValueByIncludingCurrentItem;   
  38.                         }
  39.                         DP_Tabular_Eager_Max_Value_Cache[i][w] = overAllMax;
  40.                     }
  41.                 }
  42.             }
  43.             return DP_Tabular_Eager_Max_Value_Cache[values.Length][maxWeight];
  44.         }

Recursion which has optimal sub structure and overlapping sub problems

Code Snippet
  1. public int Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure(int[] weights, int[] values, int maxWeight)
  2.         {
  3.             this.ValidataInput_Knapsack_0_1(weights, values, maxWeight);
  4.             int v = this.Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure_Recursive(weights, values, index: weights.Length-1, weight: maxWeight);
  5.             return v;
  6.         }
  7.         /// <summary>
  8.         /// f(i, w) = f(i-1, w) if Wi > w
  9.                  ///Or, max (f(i-1, w), f(i-1, w-Wi) + Vi
  10.                  ///Or 0 if i < 0
  11.         /// </summary>
  12.         int Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure_Recursive(int[] weights, int[] values, int index, int weight)
  13.         {
  14.             if (index < 0)
  15.             {
  16.                 return 0;
  17.             }
  18.             Debug.Assert(weight >= 0);
  19.             int maxValueWithoutIncludingCurrentItem = this.Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure_Recursive(weights,
  20.                 values, index - 1, weight);
  21.             if(weights[index] > weight)
  22.             {
  23.                 //current item weight is more, so we cant include - so, just return
  24.                 return maxValueWithoutIncludingCurrentItem;
  25.             }
  26.             int overallMaxValue = maxValueWithoutIncludingCurrentItem;
  27.             int maxValueIncludingCurrentItem = this.Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure_Recursive(weights,
  28.                 values, index - 1, weight - weights[index]) + values[index];
  29.             if(maxValueIncludingCurrentItem > overallMaxValue)
  30.             {
  31.                 overallMaxValue = maxValueIncludingCurrentItem;
  32.             }
  33.             return overallMaxValue;
  34.         }

Another recursive method which is also Brute Force

(but note that this recursive method don't have above properties - optimal substructure and overlapping solutions)

Code Snippet
  1. private int _maxValue = int.MinValue;
  2.         private int[] _valueIndices = null;
  3.         public void Knapsack_0_1_BruteForce_2_Power_N(int[] weights, int[] values, int maxWeight)
  4.         {
  5.             this.ValidataInput_Knapsack_0_1(weights, values, maxWeight);
  6.             this._maxValue = int.MinValue;
  7.             this._valueIndices = null;
  8.             this.Knapsack_0_1_BruteForce_2_Power_N_Rcursive(weights, values, maxWeight, 0, 0, 0, new List<int>());
  9.         }
  10.         private void Knapsack_0_1_BruteForce_2_Power_N_Rcursive(int[] weights, int[] values, int maxWeight, int index, int currentWeight, int currentValue, List<int> currentValueIndices)
  11.         {
  12.             if(currentWeight > maxWeight)
  13.             {
  14.                 return;
  15.             }
  16.             if(currentValue > this._maxValue)
  17.             {
  18.                 this._maxValue = currentValue;
  19.                 this._valueIndices = currentValueIndices.ToArray();
  20.             }
  21.             if(index == weights.Length)
  22.             {
  23.                 return;
  24.             }
  25.             Debug.Assert(index < weights.Length);
  26.             var w = weights[index];
  27.             var v = values[index];
  28.             //see if the current value, conributes to max value
  29.             currentValueIndices.Add(index);
  30.             Knapsack_0_1_BruteForce_2_Power_N_Rcursive(weights, values, maxWeight, index + 1, currentWeight + w,
  31.                 currentValue + v, currentValueIndices);
  32.             currentValueIndices.Remove(index);
  33.             //see if current value, does not contribute to max value
  34.             Knapsack_0_1_BruteForce_2_Power_N_Rcursive(weights, values, maxWeight, index + 1, currentWeight, currentValue,
  35.                 currentValueIndices);
  36.         }
  37.         private void ValidataInput_Knapsack_0_1(int[] weights, int[] values, int maxWeight)
  38.         {
  39.             #region inputvalidations
  40.             weights.ThrowIfNull("weights");
  41.             values.ThrowIfNull("values");
  42.             weights.Throw("weights", w => (w.Length == 0)
  43.                                   || w.Any(wi => wi < 0));
  44.             values.Throw("values", v => (v.Length == 0)
  45.                                   || v.Any(vi => vi < 0)
  46.                                   || (v.Length != weights.Length));
  47.             maxWeight.Throw("maxWeight", mw => mw <= 0);
  48.             #endregion
  49.         }

Unit Tests

Code Snippet
  1. [TestCategory(Constants.DynamicProgramming)]
  2.         [TestMethod]
  3.         public void Knapsack_0_1_Tests()
  4.         {
  5.             int[] benefits = new int[] { 60, 100, 120 };
  6.             int[] weights = new int[] { 10, 20, 30 };
  7.             this.Knapsack_0_1_BruteForce_2_Power_N(weights, values: benefits, maxWeight: 50);
  8.             Assert.IsTrue(this._maxValue == 220);
  9.             int v = this.Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure(weights,
  10.                 values: benefits, maxWeight: 50);
  11.             Assert.IsTrue(v == 220);
  12.             v = this.Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy(weights,
  13.                 values: benefits, maxWeight: 50);
  14.             Assert.IsTrue(v == 220);
  15.             v = this.Knapsack_0_1_DP_Tabular_Bottom_Up(weights,
  16.                 values: benefits, maxWeight: 50);
  17.             Assert.IsTrue(v == 220);
  18.             benefits = new int[] { 3, 4, 5, 8, 10 };
  19.             weights = new int[] { 2, 3, 4, 5, 9 };
  20.             this.Knapsack_0_1_BruteForce_2_Power_N(weights, values: benefits, maxWeight: 20);
  21.             Assert.IsTrue(this._maxValue == 26);
  22.             v = this.Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure(weights, values: benefits,
  23.                 maxWeight: 20);
  24.             Assert.IsTrue(v == 26);
  25.             v = this.Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy(weights,
  26.                 values: benefits, maxWeight: 20);
  27.             Assert.IsTrue(v == 26);
  28.             v = this.Knapsack_0_1_DP_Tabular_Bottom_Up(weights,
  29.                 values: benefits, maxWeight: 20);
  30.             Assert.IsTrue(v == 26);
  31.             benefits = new int[] { 3, 4, 5, 6};
  32.             weights = new int[] { 2, 3, 4, 5 };
  33.             this.Knapsack_0_1_BruteForce_2_Power_N(weights, values: benefits, maxWeight: 5);
  34.             Assert.IsTrue(this._maxValue == 7);
  35.             v = this.Knapsack_0_1_OverlappedSubPromblems_OptimalSubStructure(weights, values: benefits,
  36.                 maxWeight: 5);
  37.             Assert.IsTrue(v == 7);
  38.             v = this.Knapsack_0_1_DP_Memoization_Top_To_Bottom_Lazy(weights,
  39.                 values: benefits, maxWeight: 5);
  40.             Assert.IsTrue(v == 7);
  41.             v = this.Knapsack_0_1_DP_Tabular_Bottom_Up(weights,
  42.                 values: benefits, maxWeight: 5);
  43.             Assert.IsTrue(v == 7);
  44.         }
Code Snippet
  1. [TestCategory(Constants.DynamicProgramming)]
  2.         [TestMethod]
  3.         public void Knapsack_0_1_Brute_Force_Tests()
  4.         {
  5.             int[] benefits = new int[] { 60, 100, 120 };
  6.             int[] weights = new int[] { 10, 20, 30 };
  7.             this.Knapsack_0_1_BruteForce_2_Power_N(weights, values: benefits, maxWeight: 50);
  8.             Assert.IsTrue(this._maxValue == 220);
  9.             Assert.IsTrue(this._valueIndices.Contains(1));
  10.             Assert.IsTrue(this._valueIndices.Contains(2));
  11.             Assert.IsTrue(this._valueIndices.Length == 2);
  12.             this._maxValue = int.MinValue;
  13.             this._valueIndices = null;
  14.             benefits = new int[] { 3, 4, 5, 8, 10 };
  15.             weights = new int[] { 2, 3, 4, 5, 9 };
  16.             this.Knapsack_0_1_BruteForce_2_Power_N(weights, values: benefits, maxWeight: 20);
  17.             Assert.IsTrue(this._maxValue == 26);
  18.             Assert.IsTrue(this._valueIndices.Contains(0));
  19.             Assert.IsTrue(this._valueIndices.Contains(2));
  20.             Assert.IsTrue(this._valueIndices.Contains(3));
  21.             Assert.IsTrue(this._valueIndices.Contains(4));
  22.             Assert.IsTrue(this._valueIndices.Length == 4);
  23.         }

Coin Change (subset sum problem with repetition) - III

This is basically follow-up to previous post where subset sum problem without repetition has been discussed.

1. What are the minimum no. of coins required to get to the given target? (nothing but one of the variations of subset sum problem with repetition)

Say, f[i, sum] finds the minimum no. of coins required to get to 'sum' by using given coins up-to ith index
                  f[i, w] = 0 if i<= 0
                                0 if sum < 0
                                1 if sum == weights[i]
                                f[i-1, w] if weights[i] > w
                                f[i-1, w] if f(i, w-weights[i]) == 0
                                f(i, w-weighs[i]) + 1 if f[i-1, w] == 0
                                min (f(i-1, w), f(i, w-weighs[i]) + 1)

Below is the corresponding code...

Code Snippet
  1. public int DP_CoinChange_GetMinimalDemoninations(int[] coins, int sum)
  2.         {
  3.             coins.ThrowIfNull("coins");
  4.             coins.Throw("coins", c => c.Length == 0 || c.Any(ci => ci <= 0));
  5.             sum.Throw("sum", s => s <= 0);
  6.             int[][] DP_Cache = new int[coins.Length + 1][];
  7.             for (int i = 0; i <= coins.Length; i++)
  8.             {
  9.                 DP_Cache[i] = new int[sum + 1];
  10.             }
  11.             for(int i = 1;i<=coins.Length;i++)
  12.             {
  13.                 for(int s=0;s<=sum;s++)
  14.                 {
  15.                     if (coins[i - 1] == s)
  16.                     {
  17.                         //k, we can get to sum using just the current coin
  18.                         //so, assign to 1, no need to process further
  19.                         DP_Cache[i][s] = 1;
  20.                     }
  21.                     else
  22.                     {
  23.                         //initialize the value withouth the current value
  24.                         int minNoOfCounsWithoutUsingCurrentCoin_I = DP_Cache[i - 1][s];
  25.                         DP_Cache[i][s] = minNoOfCounsWithoutUsingCurrentCoin_I;
  26.                         if ((s > coins[i - 1]) //current coin can particiapte
  27.                             && (DP_Cache[i][s - coins[i - 1]] != 0))
  28.                         {
  29.                             int noOfCoinsUsedIncludingCurrentCoin_I =
  30.                                 DP_Cache[i][s - coins[i - 1]] + 1;
  31.                             if (minNoOfCounsWithoutUsingCurrentCoin_I == 0)
  32.                             {
  33.                                 //so far we couldnt identify coins that sums to 's'
  34.                                 DP_Cache[i][s] = noOfCoinsUsedIncludingCurrentCoin_I;
  35.                             }
  36.                             else
  37.                             {   
  38.                                 int min = this.Min(noOfCoinsUsedIncludingCurrentCoin_I,
  39.                                     minNoOfCounsWithoutUsingCurrentCoin_I);
  40.                                 DP_Cache[i][s] = min;
  41.                             }
  42.                         }
  43.                     }
  44.                 }
  45.             }
  46.             return DP_Cache[coins.Length][sum];
  47.         }

Total number of ways to make the change

Say, f[i, sum] represents total number of ways to make the change by using upto 'i' coins (coins can be used repeatedly) then,

          f[i, sum] = 0   if i<= 0
                            0 if sum < 0
                            1 if sum == 0
                            f[i-1, sum] + f[i, sum-weights[i]]

Another way to define the same is by,
           f[i, sum] = 0 if i <=0
                             f[i-1, sum] + 1 if weights[i] == sum
                             f[i-1, sum] if weights[i] > sum
                             f[i-1, sum] + f[i, sum-weights[i]]

Below is the corresponding code...
 
Code Snippet
  1. public int DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(int[] a, int sum)
  2.         {
  3.             a.ThrowIfNull("a");
  4.             int[][] DP_Memoization = new int[a.Length][];
  5.             for (int i = 0; i < a.Length; i++)
  6.             {
  7.                 DP_Memoization[i] = new int[sum + 1];
  8.                 for (int s = 0; s <= sum; s++)
  9.                 {
  10.                     DP_Memoization[i][s] = -1;
  11.                 }
  12.             }
  13.             var r = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, a.Length-1, sum, DP_Memoization);
  14.             return r;
  15.         }
  16.  
  17.         private int DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(int[] a, int i, int sum, int[][] DP_Memoization)
  18.         {
  19.             if(sum <= 0)
  20.             {
  21.                 return 0;
  22.             }
  23.             if(i < 0)
  24.             {
  25.                 return 0;
  26.             }
  27.             if(DP_Memoization[i][sum] != -1)
  28.             {
  29.                 return DP_Memoization[i][sum];
  30.             }
  31.             int noOfWays = 0;
  32.             //finding the number of ways without including current item
  33.             noOfWays += this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, i - 1, sum, DP_Memoization);
  34.             if(a[i] == sum)
  35.             {
  36.                 noOfWays++;
  37.             }
  38.             if(sum > a[i])
  39.             {
  40.                 noOfWays += this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, i, sum - a[i], DP_Memoization);
  41.             }
  42.             DP_Memoization[i][sum] = noOfWays;
  43.             return noOfWays;
  44.         }

Decision making - finding whether its possible to get the coin change

f[i, sum] = False if i<=0
                  False if sum < 0
                  True if sum == 0
                  f[i-1, sum] || [i, sum-weights[i]]

Or, f[i, sum] = False if i<= 0
                        True if weights[i] == sum
                        f[i-1, w] if weights[i] > sum
                        f[i-1, w] || f[i, w- weights[i]]

Below is the corresponding code...
 
Code Snippet
  1. public bool DP_CoinChange_SubsetSumWithRep(int[] coins, int sum)
  2.         {
  3.             coins.ThrowIfNull("coins");
  4.             coins.Throw("coins", c => c.Length == 0 || c.Any(ci => ci <= 0));
  5.             sum.Throw("sum", s => s <= 0);
  6.             bool[][] DP_Cache = new bool[coins.Length+1][];
  7.             for(int i=0;i<=coins.Length;i++)
  8.             {
  9.                 DP_Cache[i] = new bool[sum + 1];
  10.             }
  11.             for(int i =1;i<=coins.Length;i++)
  12.             {
  13.                 for(int s=0;s<=sum;s++)
  14.                 {
  15.                     DP_Cache[i][s] = (coins[i-1] == s)
  16.                         || DP_Cache[i - 1][s];
  17.                     if(!DP_Cache[i][s]
  18.                         && (s > coins[i-1]))
  19.                     {
  20.                         DP_Cache[i][s] = DP_Cache[i][s - coins[i-1]];
  21.                     }
  22.                 }
  23.             }
  24.             return DP_Cache[coins.Length][sum];
  25.         }
Code Snippet
  1. public bool DoesSubsetSumExistWithRepetition(int[] a, int sum)
  2.         {
  3.             a.ThrowIfNull("a");
  4.             bool f = this.DoesSubsetSumExistWithRepetition(a, a.Length - 1, sum);
  5.             return f;
  6.         }
  7.         public bool DoesSubsetSumExistWithRepetition(int[] a, int index, int sum)
  8.         {
  9.             if(index < 0)
  10.             {
  11.                 return false;
  12.             }
  13.             if(a[index] == sum)
  14.             {
  15.                 return true;
  16.             }
  17.             bool f = this.DoesSubsetSumExistWithRepetition(a, index - 1, sum);
  18.             if(f)
  19.             {
  20.                 return f;
  21.             }
  22.             if(sum > a[index])
  23.             {
  24.                 f = this.DoesSubsetSumExistWithRepetition(a, index, sum - a[index]);
  25.                 return f;
  26.             }
  27.             return false;
  28.         }

Getting unique subsets that sums up-to given sum

Code Snippet
  1. public int[][] GetUniqueSubsetSumPathsWithRepetition(int[] a, int sum)
  2.         {
  3.             a.ThrowIfNull("a");
  4.             var r = this.GetUniqueSubsetSumPathsWithRepetition(a, a.Length - 1, sum);
  5.             return r;
  6.         }
  7.         private int[][] GetUniqueSubsetSumPathsWithRepetition(int[] a, int index, int sum)
  8.         {
  9.             if (index < 0)
  10.             {
  11.                 return new int[0][];
  12.             }
  13.             List<int[]> paths = new List<int[]>();
  14.             if (a[index] == sum)
  15.             {
  16.                 paths.Add(new int[1] { index });
  17.             }
  18.             var p1 = this.GetUniqueSubsetSumPathsWithRepetition(a, index - 1, sum);
  19.             paths.AddRange(p1);
  20.             if (sum > a[index])
  21.             {
  22.                 var p2 = this.GetUniqueSubsetSumPathsWithRepetition(a, index, sum - a[index]);
  23.                 paths.AddRange(this.MergePaths(p2, index));
  24.             }
  25.             return paths.ToArray();
  26.         }
  27.         private int[][] MergePaths(int[][] paths,
  28.             int index)
  29.         {
  30.             Assert.IsNotNull(paths);
  31.             int[][] mergedPaths = new int[paths.Length][];
  32.             Assert.IsTrue(paths.All(p => p.Length > 0));
  33.             for (int i = 0; i < paths.Length; i++)
  34.             {
  35.                 List<int> mergedPath = new List<int>();
  36.                 mergedPath.AddRange(paths[i]);
  37.                 mergedPath.Add(index);
  38.                 mergedPaths[i] = mergedPath.ToArray();
  39.             }
  40.             return mergedPaths;            
  41.         }

Unit Tests

Code Snippet
  1. [TestCategory(Constants.DynamicProgramming)]
  2.         [TestMethod]
  3.         public void SubsetSumWithRepetition_CoinChange_Tests()
  4.         {
  5.             int[] a = new int[] { 2, 3, 6, 7 };
  6.             bool f = this.DoesSubsetSumExistWithRepetition(a, 7);
  7.             Assert.IsTrue(f);
  8.             f = this.DP_CoinChange_SubsetSumWithRep(a, 7);
  9.             Assert.IsTrue(f);
  10.             var p = this.GetUniqueSubsetSumPathsWithRepetition(a, 7);
  11.             Assert.AreEqual(2, p.Length);
  12.             int noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 7);
  13.             Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
  14.             int min = this.DP_CoinChange_GetMinimalDemoninations(a, 7);
  15.             Assert.AreEqual(1, min);
  16.             a = new int[] { 1, 2, 5 };
  17.             f = this.DoesSubsetSumExistWithRepetition(a, 7);
  18.             Assert.IsTrue(f);
  19.             f = this.DP_CoinChange_SubsetSumWithRep(a, 7);
  20.             Assert.IsTrue(f);
  21.             p = this.GetUniqueSubsetSumPathsWithRepetition(a, 7);
  22.             Assert.AreEqual(6, p.Length);
  23.             noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 7);
  24.             Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
  25.             min = this.DP_CoinChange_GetMinimalDemoninations(a, 7);
  26.             Assert.AreEqual(2, min);
  27.             a = new int[] { 1, 2 };
  28.             f = this.DoesSubsetSumExistWithRepetition(a, 6);
  29.             Assert.IsTrue(f);
  30.             f = this.DP_CoinChange_SubsetSumWithRep(a, 6);
  31.             Assert.IsTrue(f);
  32.             p = this.GetUniqueSubsetSumPathsWithRepetition(a, 6);
  33.             Assert.AreEqual(4, p.Length);
  34.             noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 6);
  35.             Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
  36.             a = new int[] { 4, 3, 2, 2, 1, 1 };
  37.             a = a.Distinct().ToArray();
  38.             f = this.DoesSubsetSumExistWithRepetition(a, 4);
  39.             Assert.IsTrue(f);
  40.             f = this.DP_CoinChange_SubsetSumWithRep(a, 4);
  41.             Assert.IsTrue(f);
  42.             p = this.GetUniqueSubsetSumPathsWithRepetition(a, 4);
  43.             Assert.AreEqual(5, p.Length);
  44.             noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 4);
  45.             Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
  46.             min = this.DP_CoinChange_GetMinimalDemoninations(a, 4);
  47.             Assert.AreEqual(1, min);
  48.             a = new int[] { 11, 12, 13 };
  49.             f = this.DoesSubsetSumExistWithRepetition(a, 4);
  50.             Assert.IsFalse(f);
  51.             f = this.DP_CoinChange_SubsetSumWithRep(a, 4);
  52.             Assert.IsFalse(f);
  53.             min = this.DP_CoinChange_GetMinimalDemoninations(a, 7);
  54.             Assert.AreEqual(0, min);
  55.             min = this.DP_CoinChange_GetMinimalDemoninations(a, 13);
  56.             Assert.AreEqual(1, min);
  57.             min = this.DP_CoinChange_GetMinimalDemoninations(a, 37);
  58.             Assert.AreEqual(3, min);
  59.             a = new int[] { 4, 3, 1 };
  60.             min = this.DP_CoinChange_GetMinimalDemoninations(a, 6);
  61.             Assert.AreEqual(2, min);
  62.             a = new int[] { 1, 2, 3 };
  63.             min = this.DP_CoinChange_GetMinimalDemoninations(a, 4);
  64.             Assert.AreEqual(2, min);
  65.             a = new int[] { 1, 2, 4 };
  66.             noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 5);
  67.             Assert.AreEqual(4, noOfWaysToGetCoinChange);
  68.         }