1- # Explanation:- https://en.wikipedia.org/wiki/Set_cover_problem
1+ # https://en.wikipedia.org/wiki/Set_cover_problem
22
33from dataclasses import dataclass
4+ from operator import attrgetter
45
56
67@dataclass
78class Item :
89 weight : int
910 value : int
1011
12+ @property
13+ def ratio (self ) -> float :
14+ """
15+ Return the value-to-weight ratio for the item.
16+
17+ Returns:
18+ float: The value-to-weight ratio for the item.
19+
20+ Examples:
21+ >>> Item(10, 65).ratio
22+ 6.5
23+
24+ >>> Item(20, 100).ratio
25+ 5.0
26+
27+ >>> Item(30, 120).ratio
28+ 4.0
29+ """
30+ return self .value / self .weight
31+
1132
1233def fractional_cover (items : list [Item ], capacity : int ) -> float :
1334 """
1435 Solve the Fractional Cover Problem.
1536
1637 Args:
17- items (List[Item]): A list of items, where each item is represented as an
18- Item object with weight and value attributes.
19- capacity (int): The maximum weight capacity of the knapsack.
38+ items: A list of items, where each item has weight and value attributes.
39+ capacity: The maximum weight capacity of the knapsack.
2040
2141 Returns:
22- float: The maximum value that can be obtained by selecting fractions of items to
23- cover the knapsack's capacity.
42+ The maximum value that can be obtained by selecting fractions of items to cover
43+ the knapsack's capacity.
2444
2545 Raises:
26- ValueError: If the ` capacity` is negative.
46+ ValueError: If capacity is negative.
2747
2848 Examples:
29- >>> items = [Item(10, 60), Item(20, 100), Item(30, 120)]
30- >>> fractional_cover(items, 50)
49+ >>> fractional_cover((Item(10, 60), Item(20, 100), Item(30, 120)), capacity=50)
3150 240.0
3251
33- >>> items = [Item(20, 100), Item(30, 120), Item(10, 60)]
34- >>> fractional_cover(items, 25)
52+ >>> fractional_cover([Item(20, 100), Item(30, 120), Item(10, 60)], capacity=25)
3553 135.0
3654
37- >>> items = [Item(10, 60), Item(20, 100), Item(30, 120)]
38- >>> fractional_cover(items, 60)
55+ >>> fractional_cover([Item(10, 60), Item(20, 100), Item(30, 120)], capacity=60)
3956 280.0
4057
41- >>> items = [Item(5, 30), Item(10, 60), Item(15, 90)]
42- >>> fractional_cover(items, 30)
58+ >>> fractional_cover(items=[Item(5, 30), Item(10, 60), Item(15, 90)], capacity=30)
4359 180.0
4460
45- >>> items = []
46- >>> fractional_cover(items, 50)
61+ >>> fractional_cover(items=[], capacity=50)
4762 0.0
4863
49- >>> items = [Item(10, 60)]
50- >>> fractional_cover(items, 5)
64+ >>> fractional_cover(items=[Item(10, 60)], capacity=5)
5165 30.0
5266
53- >>> items = [Item(10, 60)]
54- >>> fractional_cover(items, 1)
67+ >>> fractional_cover(items=[Item(10, 60)], capacity=1)
5568 6.0
5669
57- >>> items = [Item(1, 1)]
58- >>> fractional_cover(items, 0)
70+ >>> fractional_cover(items=[Item(10, 60)], capacity=0)
5971 0.0
6072
61- >>> items = [Item(10, 60)]
62- >>> fractional_cover(items, -1)
73+ >>> fractional_cover(items=[Item(10, 60)], capacity=-1)
6374 Traceback (most recent call last):
6475 ...
6576 ValueError: Capacity cannot be negative
6677 """
6778 if capacity < 0 :
6879 raise ValueError ("Capacity cannot be negative" )
69- # Calculate the value-to-weight ratios for each item
70- ratios = [(item .value / item .weight , item ) for item in items ]
71-
72- # Sort the items by their value-to-weight ratio in descending order
73- ratios .sort (key = lambda item_ratio : item_ratio [0 ], reverse = True )
7480
7581 total_value = 0.0
7682 remaining_capacity = capacity
7783
78- for ratio , item in ratios :
84+ # Sort the items by their value-to-weight ratio in descending order
85+ for item in sorted (items , key = attrgetter ("ratio" ), reverse = True ):
7986 if remaining_capacity == 0 :
8087 break
8188
8289 weight_taken = min (item .weight , remaining_capacity )
83- total_value += weight_taken * ratio
90+ total_value += weight_taken * item . ratio
8491 remaining_capacity -= weight_taken
8592
8693 return total_value
@@ -89,8 +96,7 @@ def fractional_cover(items: list[Item], capacity: int) -> float:
8996if __name__ == "__main__" :
9097 import doctest
9198
92- result = doctest .testmod ().failed
93- if result == 0 :
94- print ("All tests passed" )
95- else :
99+ if result := doctest .testmod ().failed :
96100 print (f"{ result } test(s) failed" )
101+ else :
102+ print ("All tests passed" )
0 commit comments