From 869f223540169b44b8cb4d5aaaabb01e7f7b4b99 Mon Sep 17 00:00:00 2001 From: Nico Trummer Date: Sun, 28 Jan 2024 11:10:59 +0000 Subject: [PATCH] Implement knapsack --- .coveragerc | 2 +- src/approximative_algorithms/knapsack.py | 63 +++++++++++++++++++ .../test_approximative_algorithms.py | 14 +++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100755 src/approximative_algorithms/knapsack.py create mode 100644 src/approximative_algorithms/test_approximative_algorithms.py diff --git a/.coveragerc b/.coveragerc index 091939e..1a1badb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,5 @@ [run] -omit = test_*.py +omit = /usr/local/* [report] fail_under = 85 \ No newline at end of file diff --git a/src/approximative_algorithms/knapsack.py b/src/approximative_algorithms/knapsack.py new file mode 100755 index 0000000..e1735a6 --- /dev/null +++ b/src/approximative_algorithms/knapsack.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import numpy as np +from typing import List, Tuple + +def knapsack(items: List[Tuple[int, int]], capacity: int): + """ + Knapsack problem solver using dynamic programming. + :param items: list of tuples (weight, value) + :return: tuple (max_value, max_weight) + """ + + # Check if capacity is valid + if capacity < 0: + raise ValueError("Capacity must be positive") + + max_k = len(items) + max_p = sum([p for _, p in items]) + + # Create matrix + S = np.zeros((max_k + 1, max_p + 1)) + + for k in range(max_k + 1): + for p in range(max_p + 1): + if k == 0: + if p == 0: + S[k, p] = 0 + else: + S[k, p] = np.inf + else: + p_k = items[k - 1][1] + s_k = items[k - 1][0] + + if p - p_k >= 0 and \ + S[k - 1, p - p_k] != np.inf and \ + S[k - 1, p - p_k] + s_k <= capacity and \ + S[k - 1, p - p_k] + s_k <= S[k - 1, p]: + S[k, p] = S[k - 1, p - p_k] + s_k + else: + S[k, p] = S[k - 1, p] + + # Find max position that is smaller or equal to capacity + p = max_p + while S[max_k, p] > capacity: + p -= 1 + + return p, S[max_k, p] + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('--items', "-i", help='items string, e.g. "3:1;4:2;2:3', default='3:1;4:2;2:3', required=False) + parser.add_argument('--capacity', "-c", help='knapsack capacity', type=int, default=5, required=False) + + args = parser.parse_args() + + items = [(int(w), int(p)) for w, p in [i.split(':') for i in args.items.split(';')]] + capacity = args.capacity + + max_value, weight = knapsack(items, capacity) + + print("Max value:", max_value) + print("Weight:", weight) diff --git a/src/approximative_algorithms/test_approximative_algorithms.py b/src/approximative_algorithms/test_approximative_algorithms.py new file mode 100644 index 0000000..89fe2d4 --- /dev/null +++ b/src/approximative_algorithms/test_approximative_algorithms.py @@ -0,0 +1,14 @@ +from knapsack import knapsack +import pytest + +def test_knapsack(): + items = [(3, 1), (4, 2), (2, 3)] + capacity = 5 + + max_value, weight = knapsack(items, capacity) + + assert max_value == 4 + assert weight == 5 + + with pytest.raises(ValueError): + knapsack(items, -1)