Enhancement of the knapsack algorithm with memorization and generalisation (#9295)

* enhance knapsack problem

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* wei/refactor code

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update test_knapsack.py

* Update knapsack.py

* Update test_knapsack.py

* Update knapsack.py

* Update knapsack.py

* Update knapsack.py

* Update knapsack.py

* Update knapsack.py

* Update test_knapsack.py

---------

Co-authored-by: weijiang <weijiang@weijiangs-MacBook-Pro.local>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Maxim Smolskiy <mithridatus@mail.ru>
This commit is contained in:
Wei Jiang
2025-08-27 00:10:22 +02:00
committed by GitHub
parent 44cf167175
commit 4961b3aa89
3 changed files with 54 additions and 23 deletions

View File

@@ -1,4 +1,4 @@
# A naive recursive implementation of 0-1 Knapsack Problem
# A recursive implementation of 0-N Knapsack Problem
This overview is taken from:

View File

@@ -1,14 +1,23 @@
"""A naive recursive implementation of 0-1 Knapsack Problem
"""A recursive implementation of 0-N Knapsack Problem
https://en.wikipedia.org/wiki/Knapsack_problem
"""
from __future__ import annotations
from functools import lru_cache
def knapsack(capacity: int, weights: list[int], values: list[int], counter: int) -> int:
def knapsack(
capacity: int,
weights: list[int],
values: list[int],
counter: int,
allow_repetition=False,
) -> int:
"""
Returns the maximum value that can be put in a knapsack of a capacity cap,
whereby each weight w has a specific value val.
whereby each weight w has a specific value val
with option to allow repetitive selection of items
>>> cap = 50
>>> val = [60, 100, 120]
@@ -17,28 +26,40 @@ def knapsack(capacity: int, weights: list[int], values: list[int], counter: int)
>>> knapsack(cap, w, val, c)
220
The result is 220 cause the values of 100 and 120 got the weight of 50
Given the repetition is NOT allowed,
the result is 220 cause the values of 100 and 120 got the weight of 50
which is the limit of the capacity.
>>> knapsack(cap, w, val, c, True)
300
Given the repetition is allowed,
the result is 300 cause the values of 60*5 (pick 5 times)
got the weight of 10*5 which is the limit of the capacity.
"""
# Base Case
if counter == 0 or capacity == 0:
return 0
@lru_cache
def knapsack_recur(capacity: int, counter: int) -> int:
# Base Case
if counter == 0 or capacity == 0:
return 0
# If weight of the nth item is more than Knapsack of capacity,
# then this item cannot be included in the optimal solution,
# else return the maximum of two cases:
# (1) nth item included
# (2) not included
if weights[counter - 1] > capacity:
return knapsack(capacity, weights, values, counter - 1)
else:
left_capacity = capacity - weights[counter - 1]
new_value_included = values[counter - 1] + knapsack(
left_capacity, weights, values, counter - 1
)
without_new_value = knapsack(capacity, weights, values, counter - 1)
return max(new_value_included, without_new_value)
# If weight of the nth item is more than Knapsack of capacity,
# then this item cannot be included in the optimal solution,
# else return the maximum of two cases:
# (1) nth item included only once (0-1), if allow_repetition is False
# nth item included one or more times (0-N), if allow_repetition is True
# (2) not included
if weights[counter - 1] > capacity:
return knapsack_recur(capacity, counter - 1)
else:
left_capacity = capacity - weights[counter - 1]
new_value_included = values[counter - 1] + knapsack_recur(
left_capacity, counter - 1 if not allow_repetition else counter
)
without_new_value = knapsack_recur(capacity, counter - 1)
return max(new_value_included, without_new_value)
return knapsack_recur(capacity, counter)
if __name__ == "__main__":

View File

@@ -30,7 +30,7 @@ class Test(unittest.TestCase):
def test_easy_case(self):
"""
test for the base case
test for the easy case
"""
cap = 3
val = [1, 2, 3]
@@ -48,6 +48,16 @@ class Test(unittest.TestCase):
c = len(val)
assert k.knapsack(cap, w, val, c) == 220
def test_knapsack_repetition(self):
"""
test for the knapsack repetition
"""
cap = 50
val = [60, 100, 120]
w = [10, 20, 30]
c = len(val)
assert k.knapsack(cap, w, val, c, True) == 300
if __name__ == "__main__":
unittest.main()