From eaf87c6e2b2c4563af6ae58961a82b82d0126af8 Mon Sep 17 00:00:00 2001 From: weijiang Date: Sun, 1 Oct 2023 14:22:14 +0200 Subject: [PATCH 1/3] enhance knapsack problem --- knapsack/README.md | 2 +- knapsack/knapsack.py | 59 +++++++++++++++++++-------------- knapsack/tests/test_knapsack.py | 11 +++++- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/knapsack/README.md b/knapsack/README.md index f31e5f591412..686ea929255a 100644 --- a/knapsack/README.md +++ b/knapsack/README.md @@ -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: diff --git a/knapsack/knapsack.py b/knapsack/knapsack.py index 18a36c3bcdda..8980868cba28 100644 --- a/knapsack/knapsack.py +++ b/knapsack/knapsack.py @@ -1,43 +1,54 @@ -""" 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] >>> w = [10, 20, 30] >>> c = len(val) - >>> knapsack(cap, w, val, c) + >>> knapsack(cap, w, val, c, False) 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 - # 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) + Given the repetition is allowed, + tthe result is 300 cause the values of 60*5 (pick 5 times) + which is the limit of the capacity. + """ + @lru_cache() + def knapsack_recur(cap: int, c: int) -> int: + # Base Case + if c == 0 or cap == 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) not included + # (2) nth item included one or more times (0-N), if allow_repetition is true + # nth item included only once (0-1), if allow_repetition is false + if weights[c - 1] > cap: + return knapsack_recur(cap, c - 1) + else: + without_new_value = knapsack_recur(cap, c - 1) + if allow_repetition: + new_value_included = values[c - 1] + knapsack_recur(cap - weights[c - 1], c) + else: + new_value_included = values[c - 1] + knapsack_recur(cap - weights[c - 1], c - 1) + return max(new_value_included, without_new_value) + return knapsack_recur(capacity, counter) if __name__ == "__main__": diff --git a/knapsack/tests/test_knapsack.py b/knapsack/tests/test_knapsack.py index 248855fbce53..ad75e01cd6c3 100644 --- a/knapsack/tests/test_knapsack.py +++ b/knapsack/tests/test_knapsack.py @@ -45,8 +45,17 @@ def test_knapsack(self): val = [60, 100, 120] w = [10, 20, 30] c = len(val) - self.assertEqual(k.knapsack(cap, w, val, c), 220) + self.assertEqual(k.knapsack(cap, w, val, c, False), 220) + def test_knapsack_repetition(self): + """ + test for the knapsack + """ + cap = 50 + val = [60, 100, 120] + w = [10, 20, 30] + c = len(val) + self.assertEqual(k.knapsack(cap, w, val, c, True), 300) if __name__ == "__main__": unittest.main() From 962d71c5566ddcd7aaa2056a74e6948e6bd6fbc6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 13:29:24 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- knapsack/knapsack.py | 18 +++++++++++++++--- knapsack/tests/test_knapsack.py | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/knapsack/knapsack.py b/knapsack/knapsack.py index 8980868cba28..df947015ac48 100644 --- a/knapsack/knapsack.py +++ b/knapsack/knapsack.py @@ -5,7 +5,13 @@ from functools import lru_cache -def knapsack(capacity: int, weights: list[int], values: list[int], counter: int, allow_repetition=False) -> 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 with option to allow repetitive selection of items @@ -27,6 +33,7 @@ def knapsack(capacity: int, weights: list[int], values: list[int], counter: int, tthe result is 300 cause the values of 60*5 (pick 5 times) which is the limit of the capacity. """ + @lru_cache() def knapsack_recur(cap: int, c: int) -> int: # Base Case @@ -44,10 +51,15 @@ def knapsack_recur(cap: int, c: int) -> int: else: without_new_value = knapsack_recur(cap, c - 1) if allow_repetition: - new_value_included = values[c - 1] + knapsack_recur(cap - weights[c - 1], c) + new_value_included = values[c - 1] + knapsack_recur( + cap - weights[c - 1], c + ) else: - new_value_included = values[c - 1] + knapsack_recur(cap - weights[c - 1], c - 1) + new_value_included = values[c - 1] + knapsack_recur( + cap - weights[c - 1], c - 1 + ) return max(new_value_included, without_new_value) + return knapsack_recur(capacity, counter) diff --git a/knapsack/tests/test_knapsack.py b/knapsack/tests/test_knapsack.py index ad75e01cd6c3..15301d8baaab 100644 --- a/knapsack/tests/test_knapsack.py +++ b/knapsack/tests/test_knapsack.py @@ -57,5 +57,6 @@ def test_knapsack_repetition(self): c = len(val) self.assertEqual(k.knapsack(cap, w, val, c, True), 300) + if __name__ == "__main__": unittest.main() From c542f2b69590f8a29bdad90b74e421ac98489c1f Mon Sep 17 00:00:00 2001 From: Jiang15 Date: Sun, 1 Oct 2023 15:58:07 +0200 Subject: [PATCH 3/3] wei/refactor code --- knapsack/knapsack.py | 26 ++++++++++++++++++++------ knapsack/tests/test_knapsack.py | 1 + 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/knapsack/knapsack.py b/knapsack/knapsack.py index 8980868cba28..433b1188a661 100644 --- a/knapsack/knapsack.py +++ b/knapsack/knapsack.py @@ -2,13 +2,21 @@ 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, allow_repetition=False) -> 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 with option to allow repetitive selection of items + whereby each weight w has a specific value val + with option to allow repetitive selection of items >>> cap = 50 >>> val = [60, 100, 120] @@ -24,10 +32,11 @@ def knapsack(capacity: int, weights: list[int], values: list[int], counter: int, 300 Given the repetition is allowed, - tthe result is 300 cause the values of 60*5 (pick 5 times) + the result is 300 cause the values of 60*5 (pick 5 times) which is the limit of the capacity. """ - @lru_cache() + + @lru_cache def knapsack_recur(cap: int, c: int) -> int: # Base Case if c == 0 or cap == 0: @@ -44,10 +53,15 @@ def knapsack_recur(cap: int, c: int) -> int: else: without_new_value = knapsack_recur(cap, c - 1) if allow_repetition: - new_value_included = values[c - 1] + knapsack_recur(cap - weights[c - 1], c) + new_value_included = values[c - 1] + knapsack_recur( + cap - weights[c - 1], c + ) else: - new_value_included = values[c - 1] + knapsack_recur(cap - weights[c - 1], c - 1) + new_value_included = values[c - 1] + knapsack_recur( + cap - weights[c - 1], c - 1 + ) return max(new_value_included, without_new_value) + return knapsack_recur(capacity, counter) diff --git a/knapsack/tests/test_knapsack.py b/knapsack/tests/test_knapsack.py index ad75e01cd6c3..15301d8baaab 100644 --- a/knapsack/tests/test_knapsack.py +++ b/knapsack/tests/test_knapsack.py @@ -57,5 +57,6 @@ def test_knapsack_repetition(self): c = len(val) self.assertEqual(k.knapsack(cap, w, val, c, True), 300) + if __name__ == "__main__": unittest.main()