|
1 | 1 | """
|
2 |
| -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. |
| 2 | +Given weights and values of n items, put these items in a knapsack of |
| 3 | + capacity W to get the maximum total value in the knapsack. |
| 4 | +
|
| 5 | +Note that only the integer weights 0-1 knapsack problem is solvable |
| 6 | + using dynamic programming. |
3 | 7 | """
|
4 |
| -def MF_knapsack(i,wt,val,j): |
| 8 | + |
| 9 | + |
| 10 | +def MF_knapsack(i, wt, val, j): |
5 | 11 | '''
|
6 | 12 | This code involves the concept of memory functions. Here we solve the subproblems which are needed
|
7 | 13 | unlike the below example
|
8 | 14 | F is a 2D array with -1s filled up
|
9 | 15 | '''
|
10 | 16 | global F # a global dp table for knapsack
|
11 | 17 | if F[i][j] < 0:
|
12 |
| - if j < wt[i - 1]: |
13 |
| - val = MF_knapsack(i - 1,wt,val,j) |
| 18 | + if j < wt[i-1]: |
| 19 | + val = MF_knapsack(i-1, wt, val, j) |
14 | 20 | else:
|
15 |
| - val = max(MF_knapsack(i - 1,wt,val,j),MF_knapsack(i - 1,wt,val,j - wt[i - 1]) + val[i - 1]) |
| 21 | + val = max(MF_knapsack(i-1, wt, val, j), |
| 22 | + MF_knapsack(i-1, wt, val, j - wt[i-1]) + val[i-1]) |
16 | 23 | F[i][j] = val
|
17 | 24 | return F[i][j]
|
18 | 25 |
|
| 26 | + |
19 | 27 | def knapsack(W, wt, val, n):
|
20 | 28 | dp = [[0 for i in range(W+1)]for j in range(n+1)]
|
21 | 29 |
|
22 | 30 | for i in range(1,n+1):
|
23 |
| - for w in range(1,W+1): |
24 |
| - if(wt[i-1]<=w): |
25 |
| - dp[i][w] = max(val[i-1]+dp[i-1][w-wt[i-1]],dp[i-1][w]) |
| 31 | + for w in range(1, W+1): |
| 32 | + if wt[i-1] <= w: |
| 33 | + dp[i][w] = max(val[i-1] + dp[i-1][w-wt[i-1]], dp[i-1][w]) |
26 | 34 | else:
|
27 | 35 | dp[i][w] = dp[i-1][w]
|
28 | 36 |
|
29 |
| - return dp[n][w] |
| 37 | + return dp[n][W], dp |
| 38 | + |
| 39 | + |
| 40 | +def knapsack_with_example_solution(W: int, wt: list, val:list): |
| 41 | + """ |
| 42 | + Solves the integer weights knapsack problem returns one of |
| 43 | + the several possible optimal subsets. |
| 44 | +
|
| 45 | + Parameters |
| 46 | + --------- |
| 47 | +
|
| 48 | + W: int, the total maximum weight for the given knapsack problem. |
| 49 | + wt: list, the vector of weights for all items where wt[i] is the weight |
| 50 | + of the ith item. |
| 51 | + val: list, the vector of values for all items where val[i] is the value |
| 52 | + of te ith item |
| 53 | +
|
| 54 | + Returns |
| 55 | + ------- |
| 56 | + optimal_val: float, the optimal value for the given knapsack problem |
| 57 | + example_optional_set: set, the indices of one of the optimal subsets |
| 58 | + which gave rise to the optimal value. |
| 59 | +
|
| 60 | + Examples |
| 61 | + ------- |
| 62 | + >>> knapsack_with_example_solution(10, [1, 3, 5, 2], [10, 20, 100, 22]) |
| 63 | + (142, {2, 3, 4}) |
| 64 | + >>> knapsack_with_example_solution(6, [4, 3, 2, 3], [3, 2, 4, 4]) |
| 65 | + (8, {3, 4}) |
| 66 | + >>> knapsack_with_example_solution(6, [4, 3, 2, 3], [3, 2, 4]) |
| 67 | + Traceback (most recent call last): |
| 68 | + ... |
| 69 | + ValueError: The number of weights must be the same as the number of values. |
| 70 | + But got 4 weights and 3 values |
| 71 | + """ |
| 72 | + if not (isinstance(wt, (list, tuple)) and isinstance(val, (list, tuple))): |
| 73 | + raise ValueError("Both the weights and values vectors must be either lists or tuples") |
| 74 | + |
| 75 | + num_items = len(wt) |
| 76 | + if num_items != len(val): |
| 77 | + raise ValueError("The number of weights must be the " |
| 78 | + "same as the number of values.\nBut " |
| 79 | + "got {} weights and {} values".format(num_items, len(val))) |
| 80 | + for i in range(num_items): |
| 81 | + if not isinstance(wt[i], int): |
| 82 | + raise TypeError("All weights must be integers but " |
| 83 | + "got weight of type {} at index {}".format(type(wt[i]), i)) |
| 84 | + |
| 85 | + optimal_val, dp_table = knapsack(W, wt, val, num_items) |
| 86 | + example_optional_set = set() |
| 87 | + _construct_solution(dp_table, wt, num_items, W, example_optional_set) |
| 88 | + |
| 89 | + return optimal_val, example_optional_set |
| 90 | + |
| 91 | + |
| 92 | +def _construct_solution(dp:list, wt:list, i:int, j:int, optimal_set:set): |
| 93 | + """ |
| 94 | + Recursively reconstructs one of the optimal subsets given |
| 95 | + a filled DP table and the vector of weights |
| 96 | +
|
| 97 | + Parameters |
| 98 | + --------- |
| 99 | +
|
| 100 | + dp: list of list, the table of a solved integer weight dynamic programming problem |
| 101 | +
|
| 102 | + wt: list or tuple, the vector of weights of the items |
| 103 | + i: int, the index of the item under consideration |
| 104 | + j: int, the current possible maximum weight |
| 105 | + optimal_set: set, the optimal subset so far. This gets modified by the function. |
| 106 | +
|
| 107 | + Returns |
| 108 | + ------- |
| 109 | + None |
| 110 | +
|
| 111 | + """ |
| 112 | + # for the current item i at a maximum weight j to be part of an optimal subset, |
| 113 | + # the optimal value at (i, j) must be greater than the optimal value at (i-1, j). |
| 114 | + # where i - 1 means considering only the previous items at the given maximum weight |
| 115 | + if i > 0 and j > 0: |
| 116 | + if dp[i - 1][j] == dp[i][j]: |
| 117 | + _construct_solution(dp, wt, i - 1, j, optimal_set) |
| 118 | + else: |
| 119 | + optimal_set.add(i) |
| 120 | + _construct_solution(dp, wt, i - 1, j - wt[i-1], optimal_set) |
| 121 | + |
30 | 122 |
|
31 | 123 | if __name__ == '__main__':
|
32 | 124 | '''
|
33 | 125 | Adding test case for knapsack
|
34 | 126 | '''
|
35 |
| - val = [3,2,4,4] |
36 |
| - wt = [4,3,2,3] |
| 127 | + val = [3, 2, 4, 4] |
| 128 | + wt = [4, 3, 2, 3] |
37 | 129 | n = 4
|
38 | 130 | w = 6
|
39 |
| - F = [[0]*(w + 1)] + [[0] + [-1 for i in range(w + 1)] for j in range(n + 1)] |
40 |
| - print(knapsack(w,wt,val,n)) |
41 |
| - print(MF_knapsack(n,wt,val,w)) # switched the n and w |
42 |
| - |
| 131 | + F = [[0] * (w + 1)] + [[0] + [-1 for i in range(w + 1)] for j in range(n + 1)] |
| 132 | + optimal_solution, _ = knapsack(w,wt,val, n) |
| 133 | + print(optimal_solution) |
| 134 | + print(MF_knapsack(n,wt,val,w)) # switched the n and w |
| 135 | + |
| 136 | + # testing the dynamic programming problem with example |
| 137 | + # the optimal subset for the above example are items 3 and 4 |
| 138 | + optimal_solution, optimal_subset = knapsack_with_example_solution(w, wt, val) |
| 139 | + assert optimal_solution == 8 |
| 140 | + assert optimal_subset == {3, 4} |
| 141 | + print("optimal_value = ", optimal_solution) |
| 142 | + print("An optimal subset corresponding to the optimal value", optimal_subset) |
| 143 | + |
0 commit comments