forked from TheAlgorithms/Python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfractional_knapsack.py
74 lines (62 loc) · 2.42 KB
/
fractional_knapsack.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from bisect import bisect
from itertools import accumulate
# Reference Link: https://en.wikipedia.org/wiki/Continuous_knapsack_problem
def frac_knapsack(
values: list[int], weights: list[int], capacity: int, max_items: int
) -> float:
"""
This function implements fractional knapsack problem.
Args:
values: List of values of items.
weights: List of weights of items.
capacity: Capacity of the knapsack.
max_items: Number of items.
Returns:
Maximum value of items that can be put into the knapsack.
>>> frac_knapsack([60, 100, 120], [10, 20, 30], 50, 3)
240.0
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 10, 4)
105.0
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, 4)
95.0
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6], 8, 4)
Traceback (most recent call last):
...
ValueError: zip() argument 2 is shorter than argument 1
>>> frac_knapsack([10, 40, 30], [5, 4, 6, 3], 8, 4)
Traceback (most recent call last):
...
ValueError: zip() argument 2 is longer than argument 1
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 0, 4)
0
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, 0)
95.0
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], -8, 4)
0
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, -4)
95.0
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 800, 4)
130
>>> frac_knapsack([10, 40, 30, 50], [5, 4, 6, 3], 8, 400)
95.0
>>> frac_knapsack("ABCD", [5, 4, 6, 3], 8, 400)
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for /: 'str' and 'int'
"""
# sort in descending order of value/weight ratio
r = sorted(
zip(values, weights, strict=True), key=lambda x: x[0] / x[1], reverse=True
)
values, weights = [i[0] for i in r], [i[1] for i in r] # unzip the list
acc = list(accumulate(weights)) # cumulative sum of weights
# find the index of the weight just greater than capacity
k = bisect(acc, capacity)
if k == 0: # no item can be put into the knapsack
return 0
elif k != max_items: # fractional part of the kth item can be put into the knapsack
return sum(values[:k]) + (capacity - acc[k - 1]) * (values[k]) / (weights[k])
return sum(values[:k]) # all items can be put into the knapsack
if __name__ == "__main__":
import doctest
doctest.testmod()