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
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
- public bool SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(int[] weights, int W)
- {
- this.Validate(weights, W);
- bool[][] DP_Memoization_Cache = new bool[weights.Length + 1][];
- for (int i = 0; i <= weights.Length; i++)
- {
- DP_Memoization_Cache[i] = new bool[W + 1];
- }
- for (int i = 1; i <= weights.Length; i++)
- {
- for (int w = 0; w <= W; w++)
- {
- /// f(i, w) determines if weight 'w' can be accumulated using given 'i' number of weights
- /// f(i, w) = False if i <= 0
- /// OR True if weights[i-1] == w
- /// OR f(i-1, w) if weights[i-1] > w
- ///OR f(i-1, w) || f(i-1, w-weights[i-1])
- if (weights[i - 1] == w)
- {
- DP_Memoization_Cache[i][w] = true;
- }
- else
- {
- DP_Memoization_Cache[i][w] = DP_Memoization_Cache[i - 1][w];
- if (!DP_Memoization_Cache[i][w])
- {
- if (w > weights[i - 1])
- {
- DP_Memoization_Cache[i][w] = DP_Memoization_Cache[i - 1][w - weights[i - 1]];
- }
- }
- }
- }
- }
- return DP_Memoization_Cache[weights.Length][W];
- }
- public bool SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(int[] weights, int W)
- {
- this.Validate(weights, W);
- //note 'row' index represents the number of weights been used
- //note 'colum' index represents the weight that can be achived using given
- //number of weights 'row'
- bool?[][] DP_Memoization_Cache = new bool?[weights.Length + 1][];
- for (int i = 0; i <= weights.Length; i++)
- {
- DP_Memoization_Cache[i] = new bool?[W + 1];
- for (int w = 0; w <= W; w++)
- {
- if (i != 0)
- {
- DP_Memoization_Cache[i][w] = null;
- }
- else
- {
- //can't get to weight 'w' using none of the given weights
- DP_Memoization_Cache[0][w] = false;
- }
- }
- DP_Memoization_Cache[i][0] = false;
- }
- bool f = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(
- weights, w: W, i_rowIndexOfCache: weights.Length, DP_Memoization_Cache: DP_Memoization_Cache);
- Assert.IsTrue(f == DP_Memoization_Cache[weights.Length][W]);
- return f;
- }
- /// <summary>
- /// f(i, w) determines if weight 'w' can be accumulated using given 'i' number of weights
- /// f(i, w) = False if i < 0
- /// OR True if weights[i] == w
- /// OR f(i-1, w) if weights[i] > w
- ///OR f(i-1, w) || f(i-1, w-weights[i])
- /// </summary>
- /// <param name="rowIndexOfCache">
- /// Note, its index of row in the cache
- /// index of given weifhts is indexOfCahce -1 (as it starts from 0)
- /// </param>
- bool SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(int[] weights, int w, int i_rowIndexOfCache, bool?[][] DP_Memoization_Cache)
- {
- if (i_rowIndexOfCache < 0)
- {
- return false;
- }
- if (DP_Memoization_Cache[i_rowIndexOfCache][w].HasValue)
- {
- return DP_Memoization_Cache[i_rowIndexOfCache][w].Value;
- }
- int i_weights_index = i_rowIndexOfCache - 1;
- if (weights[i_weights_index] == w)
- {
- //we can just use current weight, so no need to call other recursive methods
- //just return true
- DP_Memoization_Cache[i_rowIndexOfCache][w] = true;
- return true;
- }
- //see if W, can be achieved without using weights[i]
- bool flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights,
- w, i_rowIndexOfCache - 1);
- DP_Memoization_Cache[i_rowIndexOfCache][w] = flag;
- if (flag)
- {
- return true;
- }
- if (w > weights[i_weights_index])
- {
- //see if W-weight[i] can be achieved with rest of the weights
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights,
- w - weights[i_weights_index], i_rowIndexOfCache - 1);
- DP_Memoization_Cache[i_rowIndexOfCache][w] = flag;
- }
- return flag;
- }
- public bool SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(int[] weights, int W)
- {
- this.Validate(weights, W);
- bool f = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, W,
- i: weights.Length - 1);
- return f;
- }
- /// <summary>
- /// f(i, w) = False if i < 0
- /// OR True if weights[i] == w
- /// OR f(i-1, w) if weights[i] > w
- ///OR f(i-1, w) || f(i-1, w-weights[i])
- /// </summary>
- public bool SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(int[] weights, int W, int i)
- {
- if (i < 0)
- {
- //no more weights to traverse
- return false;
- }
- if (weights[i] == W)
- {
- //we can just use current weight, so no need to call other recursive methods
- //just return true
- return true;
- }
- //see if W, can be achieved without using weights[i]
- bool flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights,
- W, i - 1);
- if (flag)
- {
- return true;
- }
- if (W > weights[i])
- {
- //see if W-weight[i] can be achieved with rest of the weights
- return this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, W - weights[i], i - 1);
- }
- return false;
- }
Code Snippet
- [TestCategory(Constants.DynamicProgramming)]
- [TestMethod]
- public void SubsetSum_NoRepetition_KnapsackSimpliedProblemTests()
- {
- int[] weights = new int[] { 7, 5, 4, 4, 1 };
- List<int> bag = null;
- bool flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 10, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(5));
- Assert.IsTrue(bag.Contains(4));
- Assert.IsTrue(bag.Contains(1));
- Assert.IsTrue(bag.Count == 3);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 3, knapsack: out bag);
- Assert.IsFalse(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 7, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(7));
- Assert.IsTrue(bag.Count == 1);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 1, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(1));
- Assert.IsTrue(bag.Count == 1);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 10);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 3);
- Assert.IsFalse(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 7);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Tabulated_Eager(weights, 1);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 10);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 3);
- Assert.IsFalse(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 7);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_DP_Memoization_Lazy(weights, 1);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 10);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 3);
- Assert.IsFalse(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 7);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplified_OverlappedSubPromblems_OptimalSubstructure(weights, 1);
- Assert.IsTrue(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 10, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(5));
- Assert.IsTrue(bag.Contains(4));
- Assert.IsTrue(bag.Contains(1));
- Assert.IsTrue(bag.Count == 3);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 3, knapsack: out bag);
- Assert.IsFalse(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 7, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(7));
- Assert.IsTrue(bag.Count == 1);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 1, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(1));
- Assert.IsTrue(bag.Count == 1);
- weights = new int[] { 11, 8, 7, 6, 5 };
- flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 20, knapsack: out bag);
- Assert.IsTrue(bag.Contains(8));
- Assert.IsTrue(bag.Contains(7));
- Assert.IsTrue(bag.Contains(5));
- Assert.IsTrue(bag.Count == 3);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 27, knapsack: out bag);
- Assert.IsFalse(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 11, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(11));
- Assert.IsTrue(bag.Count == 1);
- flag = this.SubsetSum_NoRepetition_KnapsackSimplifiedProblemIterativeUsingStack(weights, 5, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(5));
- Assert.IsTrue(bag.Count == 1);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 20, knapsack: out bag);
- Assert.IsTrue(bag.Contains(8));
- Assert.IsTrue(bag.Contains(7));
- Assert.IsTrue(bag.Contains(5));
- Assert.IsTrue(bag.Count == 3);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 27, knapsack: out bag);
- Assert.IsFalse(flag);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 11, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(11));
- Assert.IsTrue(bag.Count == 1);
- flag = this.SubsetSum_NoRepetition_KnapsackRecursive(weights, 5, knapsack: out bag);
- Assert.IsTrue(flag);
- Assert.IsTrue(bag.Contains(5));
- Assert.IsTrue(bag.Count == 1);
- }
No comments:
Post a Comment