def MaxActivities(activity_list): # Each activity is a tuple (start, end)
activity_list.sort(key=lambda x: x[1]) # Sort activities by end times
i = activity_list[0] # Current task, always perform the first task
res = [i] # Result
al = activity_list[1:] # Skip first
for j in al: # Loop across rest
if j[0] >= i[1]: # If j starts after i ends
res.append(j) # add j to task list
i =j # j new current task
return res
activity_list = [(1,2),(3,4),(0,6),(5,7),(8,9),(5,9)]
print(MaxActivities(activity_list))
You can see from the example above, that had we sorted
by start time, we wouldve picked the (0,6) event, leaving only the
(8,9) event left after that for a maximum number of 2 events
total. Nowhere near optimal! Let's talk runtime a bit - If our
set of activities isn't sorted, we will need to do that for
\(O(n\log n)\) time initially. But what about actually picking events
in the set? Think about our "worst" case here. We'd have a list
like [(1,2).(2,3),(3,4),(4,5),...] and so on. We would eventually pick
every event in the set! But even so, that's stil O(n) time!
Pretty quick... and a lot faster than just generating all the possible
combinations of events, figuring out if its even valid, then figuring
out if it's optimal from there.
Hopefully it's pretty clear why this works - by picking the earliest ending tasks, we interfere with the fewest of the remaining tasks. In class excercise: Run the greedy algorithm by hand on the following set of activities:
\([(3, 9), (8, 12), (5, 7), (0, 6), (8, 11), (6, 10), (1, 4), (3, 5), (2, 14), (5, 9)]\)
Suppose you have a knapsack with a weight capacity of 7 kg and the following items available:
| Item | Weight (kg) | Value (Utility Score) | Value-to-Weight Ratio |
|---|---|---|---|
| Water | 5.0 | 30 | 6.0 |
| Flour | 2.0 | 10 | 5.0 |
| Sugar | 1.5 | 8 | 5.33 |
| Salt | 0.5 | 4 | 8.0 |
| Ground Pepper | 0.1 | 2 | 20.0 |
The greedy algorithm is to simply to pack as much as you can of the remaining stuff that has the highest value to weight ratio:
Thus, the maximum achievable utility in the knapsack with these items is 47.2.
There are many examples of greedy algorithms. All greedy algorithms have the same structure. The greedy choice must be part of the optimal solution, and the optimal solution of the remaining part of the problem is also part of the optimal solution. Not all problems have a greedy solution....
| Item | Weight (wi) | Value (vi) | Value-to-Weight Ratio (vi/wi) |
|---|---|---|---|
| 1 | 7 | 15 | 2.14 |
| 2 | 5 | 10 | 2.0 |
| 3 | 4 | 8 | 2.0 |