1- """A recursive implementation of 0-N Knapsack Problem
2- https://en.wikipedia.org/wiki/Knapsack_problem
31"""
2+ Recursive and Dynamic Programming implementation of the 0-N Knapsack Problem.
43
5- from __future__ import annotations
4+ References:
5+ https://en.wikipedia.org/wiki/Knapsack_problem
6+ """
67
78from functools import lru_cache
89
@@ -11,58 +12,77 @@ def knapsack(
1112 capacity : int ,
1213 weights : list [int ],
1314 values : list [int ],
14- counter : int ,
15- allow_repetition = False ,
15+ allow_repetition : bool = False ,
16+ method : str = "recursive" ,
1617) -> int :
1718 """
18- Returns the maximum value that can be put in a knapsack of a capacity cap,
19- whereby each weight w has a specific value val
20- with option to allow repetitive selection of items
21-
22- >>> cap = 50
23- >>> val = [60, 100, 120]
24- >>> w = [10, 20, 30]
25- >>> c = len(val)
26- >>> knapsack(cap, w, val, c)
27- 220
28-
29- Given the repetition is NOT allowed,
30- the result is 220 cause the values of 100 and 120 got the weight of 50
31- which is the limit of the capacity.
32- >>> knapsack(cap, w, val, c, True)
33- 300
34-
35- Given the repetition is allowed,
36- the result is 300 cause the values of 60*5 (pick 5 times)
37- got the weight of 10*5 which is the limit of the capacity.
19+ Compute the maximum total value that can be obtained by placing items
20+ in a knapsack of given capacity.
21+
22+ Args:
23+ capacity (int): Maximum weight capacity of the knapsack.
24+ weights (list[int]): List of item weights.
25+ values (list[int]): List of item values corresponding to weights.
26+ allow_repetition (bool): If True, items can be taken multiple times.
27+ method (str): "recursive" (default) or "dp" for bottom-up approach.
28+
29+ Returns:
30+ int: Maximum achievable value.
31+
32+ Examples:
33+ >>> knapsack(50, [10, 20, 30], [60, 100, 120])
34+ 220
35+ >>> knapsack(50, [10, 20, 30], [60, 100, 120], allow_repetition=True)
36+ 300
3837 """
38+ if len (weights ) != len (values ):
39+ raise ValueError ("weights and values must have the same length" )
40+ if capacity < 0 :
41+ raise ValueError ("capacity must be non-negative" )
42+ if method not in ("recursive" , "dp" ):
43+ raise ValueError ("method must be 'recursive' or 'dp'" )
44+
45+ n_items = len (weights )
46+
47+ if method == "dp" :
48+ return _knapsack_dp (capacity , weights , values , allow_repetition )
3949
40- @lru_cache
41- def knapsack_recur (capacity : int , counter : int ) -> int :
42- # Base Case
43- if counter == 0 or capacity == 0 :
50+ @lru_cache (maxsize = None )
51+ def recur (cap : int , idx : int ) -> int :
52+ if idx == 0 or cap == 0 :
4453 return 0
54+ if weights [idx - 1 ] > cap :
55+ return recur (cap , idx - 1 )
56+ include_value = values [idx - 1 ] + recur (
57+ cap - weights [idx - 1 ],
58+ idx if allow_repetition else idx - 1 ,
59+ )
60+ exclude_value = recur (cap , idx - 1 )
61+ return max (include_value , exclude_value )
4562
46- # If weight of the nth item is more than Knapsack of capacity,
47- # then this item cannot be included in the optimal solution,
48- # else return the maximum of two cases:
49- # (1) nth item included only once (0-1), if allow_repetition is False
50- # nth item included one or more times (0-N), if allow_repetition is True
51- # (2) not included
52- if weights [counter - 1 ] > capacity :
53- return knapsack_recur (capacity , counter - 1 )
54- else :
55- left_capacity = capacity - weights [counter - 1 ]
56- new_value_included = values [counter - 1 ] + knapsack_recur (
57- left_capacity , counter - 1 if not allow_repetition else counter
58- )
59- without_new_value = knapsack_recur (capacity , counter - 1 )
60- return max (new_value_included , without_new_value )
61-
62- return knapsack_recur (capacity , counter )
63+ return recur (capacity , n_items )
64+
65+
66+ def _knapsack_dp (capacity : int , weights : list [int ], values : list [int ], allow_repetition : bool ) -> int :
67+ """Iterative dynamic programming version of the knapsack problem."""
68+ n = len (weights )
69+ dp = [0 ] * (capacity + 1 )
70+
71+ if allow_repetition :
72+ # Unbounded knapsack
73+ for cap in range (1 , capacity + 1 ):
74+ for i in range (n ):
75+ if weights [i ] <= cap :
76+ dp [cap ] = max (dp [cap ], dp [cap - weights [i ]] + values [i ])
77+ else :
78+ # 0-1 knapsack
79+ for i in range (n ):
80+ for cap in range (capacity , weights [i ] - 1 , - 1 ):
81+ dp [cap ] = max (dp [cap ], dp [cap - weights [i ]] + values [i ])
82+
83+ return dp [capacity ]
6384
6485
6586if __name__ == "__main__" :
6687 import doctest
67-
6888 doctest .testmod ()
0 commit comments