From 9111d75db7ff221c55ddb56f46f4ec5e83480a80 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 10:34:32 +0530 Subject: [PATCH 01/16] Create fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 100 +++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 greedy_methods/fractional_cover_problem.py diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py new file mode 100644 index 000000000000..4e578a6435db --- /dev/null +++ b/greedy_methods/fractional_cover_problem.py @@ -0,0 +1,100 @@ +# Explanation:- https://en.wikipedia.org/wiki/Set_cover_problem + +from dataclasses import dataclass + + +@dataclass +class Item: + weight: int + value: int + + def __eq__(self, other): + return self.weight == other.weight and self.value == other.value + + +def fractional_cover(items: list[Item], capacity: int) -> float: + """ + Solve the Fractional Cover Problem. + + Args: + items (List[Item]): A list of items, where each item is represented as an + Item object with weight and value attributes. + capacity (int): The maximum weight capacity of the knapsack. + + Returns: + float: The maximum value that can be obtained by selecting fractions of items to + cover the knapsack's capacity. + + Examples: + >>> items = [Item(10, 60), Item(20, 100), Item(30, 120)] + >>> fractional_cover(items, 50) + 240.0 + + >>> items = [Item(20, 100), Item(30, 120), Item(10, 60)] + >>> fractional_cover(items, 25) + 135.0 + + >>> items = [Item(10, 60), Item(20, 100), Item(30, 120)] + >>> fractional_cover(items, 60) + 280.0 + + >>> items = [Item(5, 30), Item(10, 60), Item(15, 90)] + >>> fractional_cover(items, 30) + 180.0 + + >>> items = [] + >>> fractional_cover(items, 50) + 0.0 + + >>> items = [Item(10, 60)] + >>> fractional_cover(items, 5) + 30.0 + + >>> items = [Item(10, 60)] + >>> fractional_cover(items, 1) + 6.0 + + >>> items = [Item(1, 1)] + >>> fractional_cover(items, 0) + 0.0 + + >>> items = [Item(1, 1)] + >>> fractional_cover(items, 1) + 1.0 + + >>> items = [Item(1, 1)] + >>> fractional_cover(items, 2) + 1.0 + + >>> items = [Item(1, 1)] + >>> fractional_cover(items, 0) + 0.0 + """ + # Calculate the value-to-weight ratios for each item + ratios = [(item.value / item.weight, item) for item in items] + + # Sort the items by their value-to-weight ratio in descending order + ratios.sort(key=lambda x: x[0], reverse=True) + + total_value = 0.0 + remaining_capacity = capacity + + for ratio, item in ratios: + if remaining_capacity == 0: + break + + weight_taken = min(item.weight, remaining_capacity) + total_value += weight_taken * ratio + remaining_capacity -= weight_taken + + return total_value + + +if __name__ == "__main__": + import doctest + + result = doctest.testmod().failed + if result == 0: + print("All tests passed") + else: + print(f"{result} test(s) failed") From 18b625fe7d5fd24138e2ff84ebeaf860771716a6 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 10:44:32 +0530 Subject: [PATCH 02/16] Update fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 4e578a6435db..03d9f404a102 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -8,7 +8,16 @@ class Item: weight: int value: int - def __eq__(self, other): + def __eq__(self, other: 'Item') -> bool: + """ + Compare two Item objects for equality based on weight and value attributes. + + Args: + other (Item): Another Item object to compare with. + + Returns: + bool: True if the objects are equal, False otherwise. + """ return self.weight == other.weight and self.value == other.value @@ -54,18 +63,6 @@ def fractional_cover(items: list[Item], capacity: int) -> float: >>> fractional_cover(items, 1) 6.0 - >>> items = [Item(1, 1)] - >>> fractional_cover(items, 0) - 0.0 - - >>> items = [Item(1, 1)] - >>> fractional_cover(items, 1) - 1.0 - - >>> items = [Item(1, 1)] - >>> fractional_cover(items, 2) - 1.0 - >>> items = [Item(1, 1)] >>> fractional_cover(items, 0) 0.0 @@ -74,7 +71,7 @@ def fractional_cover(items: list[Item], capacity: int) -> float: ratios = [(item.value / item.weight, item) for item in items] # Sort the items by their value-to-weight ratio in descending order - ratios.sort(key=lambda x: x[0], reverse=True) + ratios.sort(key=lambda item_ratio: item_ratio[0], reverse=True) total_value = 0.0 remaining_capacity = capacity @@ -89,7 +86,6 @@ def fractional_cover(items: list[Item], capacity: int) -> float: return total_value - if __name__ == "__main__": import doctest From 64e32b3a1d8da0e2e57f7fa961554522a7dbb4cd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 05:15:05 +0000 Subject: [PATCH 03/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- greedy_methods/fractional_cover_problem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 03d9f404a102..f9c565bb8ffe 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -8,7 +8,7 @@ class Item: weight: int value: int - def __eq__(self, other: 'Item') -> bool: + def __eq__(self, other: "Item") -> bool: """ Compare two Item objects for equality based on weight and value attributes. @@ -86,6 +86,7 @@ def fractional_cover(items: list[Item], capacity: int) -> float: return total_value + if __name__ == "__main__": import doctest From 3436d206a585f7f7920ee2f3e8cf425b1cf6a490 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 10:48:18 +0530 Subject: [PATCH 04/16] Update fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index f9c565bb8ffe..4fd71ee6c83a 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -17,6 +17,15 @@ def __eq__(self, other: "Item") -> bool: Returns: bool: True if the objects are equal, False otherwise. + + Examples: + >>> item1 = Item(10, 60) + >>> item2 = Item(10, 60) + >>> item3 = Item(20, 100) + >>> item1 == item2 + True + >>> item1 == item3 + False """ return self.weight == other.weight and self.value == other.value @@ -95,3 +104,4 @@ def fractional_cover(items: list[Item], capacity: int) -> float: print("All tests passed") else: print(f"{result} test(s) failed") + From 0d3bbfb99b28f4038c422757ef8a1bfcd69a6f15 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 05:18:51 +0000 Subject: [PATCH 05/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- greedy_methods/fractional_cover_problem.py | 1 - 1 file changed, 1 deletion(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 4fd71ee6c83a..026636e07095 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -104,4 +104,3 @@ def fractional_cover(items: list[Item], capacity: int) -> float: print("All tests passed") else: print(f"{result} test(s) failed") - From 32b02e3c042d81027d3aebf1fe6581a6f90b3912 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 10:57:13 +0530 Subject: [PATCH 06/16] Update fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 026636e07095..11372baada1d 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -27,6 +27,8 @@ def __eq__(self, other: "Item") -> bool: >>> item1 == item3 False """ + if not isinstance(other, Item): + return NotImplemented return self.weight == other.weight and self.value == other.value From 31017cacb1da90cf2e36060e31ac1cab55d4f9d7 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 11:07:39 +0530 Subject: [PATCH 07/16] Update fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 11372baada1d..8e8fc7df6457 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -8,12 +8,12 @@ class Item: weight: int value: int - def __eq__(self, other: "Item") -> bool: + def __eq__(self, other: object) -> bool: """ Compare two Item objects for equality based on weight and value attributes. Args: - other (Item): Another Item object to compare with. + other (object): Another object to compare with. Returns: bool: True if the objects are equal, False otherwise. @@ -28,7 +28,7 @@ def __eq__(self, other: "Item") -> bool: False """ if not isinstance(other, Item): - return NotImplemented + return False # Return False for non-Item objects return self.weight == other.weight and self.value == other.value @@ -106,3 +106,4 @@ def fractional_cover(items: list[Item], capacity: int) -> float: print("All tests passed") else: print(f"{result} test(s) failed") + From 184b146017905905380bec73d65e4fb75c3126b3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 05:38:11 +0000 Subject: [PATCH 08/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- greedy_methods/fractional_cover_problem.py | 1 - 1 file changed, 1 deletion(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 8e8fc7df6457..410af2cf6726 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -106,4 +106,3 @@ def fractional_cover(items: list[Item], capacity: int) -> float: print("All tests passed") else: print(f"{result} test(s) failed") - From 1d3821601fae35c38f74db32a10706532f4ee9f0 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 11:09:27 +0530 Subject: [PATCH 09/16] Update fractional_cover_problem.py From 32b219f09d65e4dd9992922854a967357bb607c7 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 12:28:47 +0530 Subject: [PATCH 10/16] Update fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 23 +--------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 410af2cf6726..eca262da37ed 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -8,30 +8,9 @@ class Item: weight: int value: int - def __eq__(self, other: object) -> bool: - """ - Compare two Item objects for equality based on weight and value attributes. - - Args: - other (object): Another object to compare with. - - Returns: - bool: True if the objects are equal, False otherwise. - - Examples: - >>> item1 = Item(10, 60) - >>> item2 = Item(10, 60) - >>> item3 = Item(20, 100) - >>> item1 == item2 - True - >>> item1 == item3 - False - """ - if not isinstance(other, Item): - return False # Return False for non-Item objects + def not_really__eq__(self, other: object) -> bool: return self.weight == other.weight and self.value == other.value - def fractional_cover(items: list[Item], capacity: int) -> float: """ Solve the Fractional Cover Problem. From cf108baed48c79a37fdf6c8d2b023d7715e9f6a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 06:59:19 +0000 Subject: [PATCH 11/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- greedy_methods/fractional_cover_problem.py | 1 + 1 file changed, 1 insertion(+) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index eca262da37ed..9690cccc7697 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -11,6 +11,7 @@ class Item: def not_really__eq__(self, other: object) -> bool: return self.weight == other.weight and self.value == other.value + def fractional_cover(items: list[Item], capacity: int) -> float: """ Solve the Fractional Cover Problem. From 3c80327f664d873fef6451be94b62025d5f0c8b7 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 12:34:19 +0530 Subject: [PATCH 12/16] Update fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 9690cccc7697..74c9433459bd 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -8,7 +8,7 @@ class Item: weight: int value: int - def not_really__eq__(self, other: object) -> bool: + def __eq__(self, other: 'Item') -> bool: return self.weight == other.weight and self.value == other.value From a81fa7bd918814dd9966dc5791c1de526b61d702 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 07:04:51 +0000 Subject: [PATCH 13/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- greedy_methods/fractional_cover_problem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 74c9433459bd..1b0be758fd88 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -8,7 +8,7 @@ class Item: weight: int value: int - def __eq__(self, other: 'Item') -> bool: + def __eq__(self, other: "Item") -> bool: return self.weight == other.weight and self.value == other.value From 60d5b767d9fdf672f42ba0ba467361a87bab9ddc Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 7 Oct 2023 09:09:55 +0200 Subject: [PATCH 14/16] Lose __eq__() --- greedy_methods/fractional_cover_problem.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 1b0be758fd88..39045834fa22 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -8,9 +8,6 @@ class Item: weight: int value: int - def __eq__(self, other: "Item") -> bool: - return self.weight == other.weight and self.value == other.value - def fractional_cover(items: list[Item], capacity: int) -> float: """ From 503c3bd14ad922de298813092aaec036d5269f58 Mon Sep 17 00:00:00 2001 From: Pronay Debnath Date: Sat, 7 Oct 2023 20:48:01 +0530 Subject: [PATCH 15/16] Update fractional_cover_problem.py --- greedy_methods/fractional_cover_problem.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 39045834fa22..1c9762ca3217 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -22,6 +22,9 @@ def fractional_cover(items: list[Item], capacity: int) -> float: float: The maximum value that can be obtained by selecting fractions of items to cover the knapsack's capacity. + Raises: + ValueError: If the `capacity` is negative. + Examples: >>> items = [Item(10, 60), Item(20, 100), Item(30, 120)] >>> fractional_cover(items, 50) @@ -54,7 +57,15 @@ def fractional_cover(items: list[Item], capacity: int) -> float: >>> items = [Item(1, 1)] >>> fractional_cover(items, 0) 0.0 + + >>> items = [Item(10, 60)] + >>> fractional_cover(items, -1) + Traceback (most recent call last): + ... + ValueError: Capacity cannot be negative """ + if capacity < 0: + raise ValueError("Capacity cannot be negative") # Calculate the value-to-weight ratios for each item ratios = [(item.value / item.weight, item) for item in items] From b000f15c23ee47c0ab94294e13f0394bcc657dce Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 7 Oct 2023 17:46:14 +0200 Subject: [PATCH 16/16] Define Item property ratio --- greedy_methods/fractional_cover_problem.py | 78 ++++++++++++---------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/greedy_methods/fractional_cover_problem.py b/greedy_methods/fractional_cover_problem.py index 1c9762ca3217..e37c363f1db9 100644 --- a/greedy_methods/fractional_cover_problem.py +++ b/greedy_methods/fractional_cover_problem.py @@ -1,6 +1,7 @@ -# Explanation:- https://en.wikipedia.org/wiki/Set_cover_problem +# https://en.wikipedia.org/wiki/Set_cover_problem from dataclasses import dataclass +from operator import attrgetter @dataclass @@ -8,79 +9,85 @@ class Item: weight: int value: int + @property + def ratio(self) -> float: + """ + Return the value-to-weight ratio for the item. + + Returns: + float: The value-to-weight ratio for the item. + + Examples: + >>> Item(10, 65).ratio + 6.5 + + >>> Item(20, 100).ratio + 5.0 + + >>> Item(30, 120).ratio + 4.0 + """ + return self.value / self.weight + def fractional_cover(items: list[Item], capacity: int) -> float: """ Solve the Fractional Cover Problem. Args: - items (List[Item]): A list of items, where each item is represented as an - Item object with weight and value attributes. - capacity (int): The maximum weight capacity of the knapsack. + items: A list of items, where each item has weight and value attributes. + capacity: The maximum weight capacity of the knapsack. Returns: - float: The maximum value that can be obtained by selecting fractions of items to - cover the knapsack's capacity. + The maximum value that can be obtained by selecting fractions of items to cover + the knapsack's capacity. Raises: - ValueError: If the `capacity` is negative. + ValueError: If capacity is negative. Examples: - >>> items = [Item(10, 60), Item(20, 100), Item(30, 120)] - >>> fractional_cover(items, 50) + >>> fractional_cover((Item(10, 60), Item(20, 100), Item(30, 120)), capacity=50) 240.0 - >>> items = [Item(20, 100), Item(30, 120), Item(10, 60)] - >>> fractional_cover(items, 25) + >>> fractional_cover([Item(20, 100), Item(30, 120), Item(10, 60)], capacity=25) 135.0 - >>> items = [Item(10, 60), Item(20, 100), Item(30, 120)] - >>> fractional_cover(items, 60) + >>> fractional_cover([Item(10, 60), Item(20, 100), Item(30, 120)], capacity=60) 280.0 - >>> items = [Item(5, 30), Item(10, 60), Item(15, 90)] - >>> fractional_cover(items, 30) + >>> fractional_cover(items=[Item(5, 30), Item(10, 60), Item(15, 90)], capacity=30) 180.0 - >>> items = [] - >>> fractional_cover(items, 50) + >>> fractional_cover(items=[], capacity=50) 0.0 - >>> items = [Item(10, 60)] - >>> fractional_cover(items, 5) + >>> fractional_cover(items=[Item(10, 60)], capacity=5) 30.0 - >>> items = [Item(10, 60)] - >>> fractional_cover(items, 1) + >>> fractional_cover(items=[Item(10, 60)], capacity=1) 6.0 - >>> items = [Item(1, 1)] - >>> fractional_cover(items, 0) + >>> fractional_cover(items=[Item(10, 60)], capacity=0) 0.0 - >>> items = [Item(10, 60)] - >>> fractional_cover(items, -1) + >>> fractional_cover(items=[Item(10, 60)], capacity=-1) Traceback (most recent call last): ... ValueError: Capacity cannot be negative """ if capacity < 0: raise ValueError("Capacity cannot be negative") - # Calculate the value-to-weight ratios for each item - ratios = [(item.value / item.weight, item) for item in items] - - # Sort the items by their value-to-weight ratio in descending order - ratios.sort(key=lambda item_ratio: item_ratio[0], reverse=True) total_value = 0.0 remaining_capacity = capacity - for ratio, item in ratios: + # Sort the items by their value-to-weight ratio in descending order + for item in sorted(items, key=attrgetter("ratio"), reverse=True): if remaining_capacity == 0: break weight_taken = min(item.weight, remaining_capacity) - total_value += weight_taken * ratio + total_value += weight_taken * item.ratio remaining_capacity -= weight_taken return total_value @@ -89,8 +96,7 @@ def fractional_cover(items: list[Item], capacity: int) -> float: if __name__ == "__main__": import doctest - result = doctest.testmod().failed - if result == 0: - print("All tests passed") - else: + if result := doctest.testmod().failed: print(f"{result} test(s) failed") + else: + print("All tests passed")