From 5d81c55a5695503cb61bc3fca90b3a0133015102 Mon Sep 17 00:00:00 2001 From: Maxwell Aladago Date: Sun, 18 Aug 2019 17:20:05 -0400 Subject: [PATCH 1/6] function for the knapsack problem which returns one of the optimal subsets --- dynamic_programming/knapsack.py | 92 ++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index 27d1cfed799b..ea0080dd79ea 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -1,7 +1,8 @@ """ Given weights and values of n items, put these items in a knapsack of capacity W to get the maximum total value in the knapsack. """ -def MF_knapsack(i,wt,val,j): + +def MF_knapsack(i, wt, val, j): ''' This code involves the concept of memory functions. Here we solve the subproblems which are needed unlike the below example @@ -12,10 +13,12 @@ def MF_knapsack(i,wt,val,j): if j < wt[i - 1]: val = MF_knapsack(i - 1,wt,val,j) else: - val = max(MF_knapsack(i - 1,wt,val,j),MF_knapsack(i - 1,wt,val,j - wt[i - 1]) + val[i - 1]) + val = max(MF_knapsack(i - 1, wt, val, j), + MF_knapsack(i - 1, wt, val, j - wt[i - 1]) + val[i - 1]) F[i][j] = val return F[i][j] + def knapsack(W, wt, val, n): dp = [[0 for i in range(W+1)]for j in range(n+1)] @@ -26,17 +29,90 @@ def knapsack(W, wt, val, n): else: dp[i][w] = dp[i-1][w] - return dp[n][w] + return dp[n][W], dp + + +def knapsack_with_example_solution(W, wt, val): + """ + Solves the integer weights knapsack problem returns one of + the several possible optimal subsets. + + Parameters + --------- + + W: int, the total maximum weight for the given knapsack problem. + wt: list, the vector of weights for all items where wt[i] is the weight + of the ith item. + val: list, the vector of values for all items where val[i] is the value + of te ith item + + Returns + ------- + optimal_val: float, the optimal value for the given knapsack problem + example_optional_set: set, the indices of one of the optimal subsets + which gave rise to the optimal value. + + Examples + ------- + >>> knapsack_with_example_solution(10, [1, 3, 5, 2], [10, 20, 100, 22]) + (142, {2, 3, 4}) + >>> knapsack_with_example_solution(6, [4, 3, 2, 3], [3, 2, 4, 4]) + (8, {3, 4}) + >>> knapsack_with_example_solution(6, [4, 3, 2, 3], [3, 2, 4]) + Traceback (most recent call last): + ... + ValueError: The number of weights must be the same as the number of values. + But got 4 weights and 3 values + """ + if not (isinstance(wt, (list, tuple)) and isinstance(val, (list, tuple))): + raise ValueError("Both the weights and values vectors must be either lists or tuples") + + num_items = len(wt) + if num_items != len(val): + raise ValueError("The number of weights must be the " + "same as the number of values.\nBut " + "got {} weights and {} values".format(num_items, len(val))) + for i in range(num_items): + if not isinstance(wt[i], int): + raise TypeError("All weights must be integers but " + "got weight of type {} at index {}".format(type(wt[i]), i)) + + optimal_val, dp_table = knapsack(W, wt, val, num_items) + example_optional_set = set() + _construct_solution(dp_table, wt, num_items, W, example_optional_set) + + return optimal_val, example_optional_set + + +def _construct_solution(dp, wt, i, j, optimal_set): + if i > 0 and j > 0: + if dp[i - 1][j] == dp[i][j]: + _construct_solution(dp, wt, i - 1, j, optimal_set) + else: + # item i is certainly part of the solution + optimal_set.add(i) + _construct_solution(dp, wt, i - 1, j - wt[i-1], optimal_set) + if __name__ == '__main__': ''' Adding test case for knapsack ''' - val = [3,2,4,4] - wt = [4,3,2,3] + val = [3, 2, 4, 4] + wt = [4, 3, 2, 3] n = 4 w = 6 F = [[0]*(w + 1)] + [[0] + [-1 for i in range(w + 1)] for j in range(n + 1)] - print(knapsack(w,wt,val,n)) - print(MF_knapsack(n,wt,val,w)) # switched the n and w - + + optimal_solution, _ = knapsack(w,wt,val, n) + print(optimal_solution) + print(MF_knapsack(n,wt,val,w)) # switched the n and w + + # testing the dynamic programming problem with example + # the optimal subset for the above example are items 3 and 4 + optimal_solution, optimal_subset = knapsack_with_example_solution(w, wt, val) + assert optimal_solution == 8 + assert optimal_subset == {3, 4} + print("optimal_value = ", optimal_solution) + print("An optimal subset corresponding to the optimal value", optimal_subset) + From f2994cfa895ff037b20a4be60a3ec8f5aa89a39e Mon Sep 17 00:00:00 2001 From: Maxwell Aladago Date: Sun, 18 Aug 2019 17:30:30 -0400 Subject: [PATCH 2/6] function for the knapsack problem which returns one of the optimal subsets --- dynamic_programming/knapsack.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index ea0080dd79ea..d419325f80a5 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -85,11 +85,32 @@ def knapsack_with_example_solution(W, wt, val): def _construct_solution(dp, wt, i, j, optimal_set): + """ + Recursively reconstructs one of the optimal subsets given + a filled DP table and the vector of weights + + Parameters + --------- + + dp: list of list, the table of a solved integer weight dynamic programming problem + + wt: list or tuple, the vector of weights of the items + i: int, the index of the item under consideration + j: int, the current possible maximum weight + optimal_set: set, the optimal subset so far. This gets modified by the function. + + Returns + ------- + None + + """ + # for the current item i at a maximum weight j to be part of an optimal subset, + # the optimal value at (i, j) must be greater than the optimal value at (i-1, j). + # where i - 1 means considering only the previous items at the given maximum weight if i > 0 and j > 0: if dp[i - 1][j] == dp[i][j]: _construct_solution(dp, wt, i - 1, j, optimal_set) else: - # item i is certainly part of the solution optimal_set.add(i) _construct_solution(dp, wt, i - 1, j - wt[i-1], optimal_set) From 85b9c59c6ec8de0452aaaae990d0a86bf6219dd0 Mon Sep 17 00:00:00 2001 From: Maxwell Aladago Date: Sun, 18 Aug 2019 17:34:05 -0400 Subject: [PATCH 3/6] function for the knapsack problem which returns one of the optimal subsets --- dynamic_programming/knapsack.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index d419325f80a5..10770ffcc11b 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -1,8 +1,12 @@ """ Given weights and values of n items, put these items in a knapsack of capacity W to get the maximum total value in the knapsack. + +Note that only the integer weights 0-1 knapsack problem is solvable using dynamic programming. """ +from __future__ import print_function, division, absolute_import + -def MF_knapsack(i, wt, val, j): +def MF_knapsack(i,wt,val,j): ''' This code involves the concept of memory functions. Here we solve the subproblems which are needed unlike the below example @@ -13,8 +17,7 @@ def MF_knapsack(i, wt, val, j): if j < wt[i - 1]: val = MF_knapsack(i - 1,wt,val,j) else: - val = max(MF_knapsack(i - 1, wt, val, j), - MF_knapsack(i - 1, wt, val, j - wt[i - 1]) + val[i - 1]) + val = max(MF_knapsack(i - 1,wt,val,j),MF_knapsack(i - 1,wt,val,j - wt[i - 1]) + val[i - 1]) F[i][j] = val return F[i][j] @@ -123,8 +126,7 @@ def _construct_solution(dp, wt, i, j, optimal_set): wt = [4, 3, 2, 3] n = 4 w = 6 - F = [[0]*(w + 1)] + [[0] + [-1 for i in range(w + 1)] for j in range(n + 1)] - + F = [[0] * (w + 1)] + [[0] + [-1 for i in range(w + 1)] for j in range(n + 1)] optimal_solution, _ = knapsack(w,wt,val, n) print(optimal_solution) print(MF_knapsack(n,wt,val,w)) # switched the n and w From 43fefa196d0fa2623bc8190f6684978fabe37506 Mon Sep 17 00:00:00 2001 From: Maxwell Aladago Date: Sun, 18 Aug 2019 18:03:42 -0400 Subject: [PATCH 4/6] function for the knapsack problem which returns one of the optimal subsets --- dynamic_programming/knapsack.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index 10770ffcc11b..04a6c0630ea2 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -3,8 +3,7 @@ Note that only the integer weights 0-1 knapsack problem is solvable using dynamic programming. """ -from __future__ import print_function, division, absolute_import - +from typing import Union def MF_knapsack(i,wt,val,j): ''' @@ -35,7 +34,7 @@ def knapsack(W, wt, val, n): return dp[n][W], dp -def knapsack_with_example_solution(W, wt, val): +def knapsack_with_example_solution(W: int, wt: list, val:list): """ Solves the integer weights knapsack problem returns one of the several possible optimal subsets. @@ -87,7 +86,7 @@ def knapsack_with_example_solution(W, wt, val): return optimal_val, example_optional_set -def _construct_solution(dp, wt, i, j, optimal_set): +def _construct_solution(dp:list, wt:list, i:int, j:int, optimal_set:set): """ Recursively reconstructs one of the optimal subsets given a filled DP table and the vector of weights From 47f76ebe22b533817a846a1c21a37b4bede246aa Mon Sep 17 00:00:00 2001 From: Maxwell Aladago Date: Sun, 18 Aug 2019 18:51:48 -0400 Subject: [PATCH 5/6] function for the knapsack problem which returns one of the optimal subsets --- dynamic_programming/knapsack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index 04a6c0630ea2..70c6d3eca78d 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -3,7 +3,6 @@ Note that only the integer weights 0-1 knapsack problem is solvable using dynamic programming. """ -from typing import Union def MF_knapsack(i,wt,val,j): ''' From 5e828fcab9912c45b86a3504af54556f28cc21ef Mon Sep 17 00:00:00 2001 From: Maxwell Aladago Date: Mon, 19 Aug 2019 01:19:48 -0400 Subject: [PATCH 6/6] some pep8 cleanup too --- dynamic_programming/knapsack.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index 70c6d3eca78d..488059d6244d 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -1,10 +1,13 @@ """ -Given weights and values of n items, put these items in a knapsack of capacity W to get the maximum total value in the knapsack. +Given weights and values of n items, put these items in a knapsack of + capacity W to get the maximum total value in the knapsack. -Note that only the integer weights 0-1 knapsack problem is solvable using dynamic programming. +Note that only the integer weights 0-1 knapsack problem is solvable + using dynamic programming. """ -def MF_knapsack(i,wt,val,j): + +def MF_knapsack(i, wt, val, j): ''' This code involves the concept of memory functions. Here we solve the subproblems which are needed unlike the below example @@ -12,10 +15,11 @@ def MF_knapsack(i,wt,val,j): ''' global F # a global dp table for knapsack if F[i][j] < 0: - if j < wt[i - 1]: - val = MF_knapsack(i - 1,wt,val,j) + if j < wt[i-1]: + val = MF_knapsack(i-1, wt, val, j) else: - val = max(MF_knapsack(i - 1,wt,val,j),MF_knapsack(i - 1,wt,val,j - wt[i - 1]) + val[i - 1]) + val = max(MF_knapsack(i-1, wt, val, j), + MF_knapsack(i-1, wt, val, j - wt[i-1]) + val[i-1]) F[i][j] = val return F[i][j] @@ -24,9 +28,9 @@ def knapsack(W, wt, val, n): dp = [[0 for i in range(W+1)]for j in range(n+1)] for i in range(1,n+1): - for w in range(1,W+1): - if(wt[i-1]<=w): - dp[i][w] = max(val[i-1]+dp[i-1][w-wt[i-1]],dp[i-1][w]) + for w in range(1, W+1): + if wt[i-1] <= w: + dp[i][w] = max(val[i-1] + dp[i-1][w-wt[i-1]], dp[i-1][w]) else: dp[i][w] = dp[i-1][w]