Skip to content
Snippets Groups Projects
Commit d9daf73e authored by Romain Vuillemot's avatar Romain Vuillemot
Browse files

Create 04-priority-queues-exercises.ipynb

parent 5ec464e9
Branches
No related tags found
No related merge requests found
%% Cell type:markdown id:994d4d0c tags:
NAME:
%% Cell type:markdown id:651a2e17-2fe1-41c7-be26-a7d3b40f9277 tags:
# UE5 Fundamentals of Algorithms
# Lab 4: Priority queues
%% Cell type:markdown id:dd4f535a-2401-45c6-9d4a-ca8f146aa9c9 tags:
---
%% Cell type:markdown id:615a1282-53cf-4610-bddd-2a3ba105e5bf tags:
<details style="border: 1px">
<summary> How to use those notebooks</summary>
For each of the following questions:
- In the `# YOUR CODE HERE` cell, remove `raise NotImplementedError()` to write your code
- Write an example of use of your code or make sure the given examples and tests pass
- Add extra tests in the `#Tests` cell
</details>
%% Cell type:markdown id:b93b7332-5a40-4a85-b505-83835d68fe67 tags:
## Exercise 1: How to use a priority queue
Your are given a list of elements (`items`) and a matching list of priorities (`priorities`) as follows:
%% Cell type:code id:b91e0762-b158-44b1-850e-2987d7206b2b tags:
``` python
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
priorities = [2, 1, 3, 3, 0, 1, 1, 2]
for item, priority in zip(items, priorities):
print((item, priority))
```
%% Cell type:markdown id:2a348646-7633-4e79-a4ad-9597893c327d tags:
Use the Python [PriorityQueue](https://docs.python.org/3/library/queue.html#queue.PriorityQueue) module to store such values, and then return by _ascending_ order.
%% Cell type:code id:18d02388-dca0-4493-a1c6-9d2c1314683d tags:
``` python
q = PriorityQueue()
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:f44ea802-5fbf-404c-8430-0b931da0bf53 tags:
Write a similar version that returns the `items` by _descending_ order (we assume `priorities` are always integers).
%% Cell type:code id:9bc92f94-97a1-46fa-813c-63fb6abc9f65 tags:
``` python
q = PriorityQueue()
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:c2ab7bd0-a237-4755-9421-a2dee82e77a4 tags:
## Exercise 2: Implement your own priority queue
Implement your own priority queue class and compare it to the Python module `PriorityQueue` use previously. Use the stack (or the queue) we have seen in the previous lab.
%% Cell type:code id:ca96c6ee-54c9-47ba-959e-60c7168256d6 tags:
``` python
class MyPriorityQueue():
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:9e2f1c19-1f64-4de1-b9e8-1ed2e167cf40 tags:
Write comparisons with the `PriorityQueue` module:
%% Cell type:code id:e86176bf-fb09-44a1-8732-5becb06921af tags:
``` python
p1 = PriorityQueue()
p2 = MyPriorityQueue()
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:9f0d41e9-254a-4121-a209-e751e6122e7c tags:
## Exercise 3: Tasks scheduler
In this exercise we want to write an algorithm that will schedule some tasks. Think of a tasks as an event with a name (eg `"Task 1"`, a `start` time and an `end` time). To keep it simple, we will consider times as `int`. The goal will be to write a scheduler that will decide which task to do, with an important condition: tasks cannot overlap (ie at any given moment, only 1 task can be picked). You'll start by defining a `Task` class and then the `TaskScheduler` class that manages the list of tasks you picked and that do not overlap.
Start by writing the `Task` class and in particular include a way to compare tasks by overriding the [comparison operator](https://docs.python.org/3/library/operator.html):
```python
def __lt__(self, other):
return self.start < other.start
```
%% Cell type:code id:44aacc95-2aa6-485b-8759-ac292244f290 tags:
``` python
class Task:
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:6430a61e-879d-4c2d-bc92-a6f288786242 tags:
``` python
task1 = Task("Task 1", 1, 4)
task2 = Task("Task 2", 2, 5)
assert task1 < task2
```
%% Cell type:markdown id:7563c27b-c412-4899-acb3-57c10f2abb8d tags:
Now write the `TaskScheduler` class. You can use the `PriorityQueue` or your own implementation of a priority queue. Tip: write 3 methods:
1. add a task
2. check if overlapping
3. return the non-overlapping tasks
%% Cell type:code id:6e9d6120-ffb9-4a2f-9fb6-e3c0761ecf75 tags:
``` python
from queue import PriorityQueue
class TaskScheduler:
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:558776cd-9f8f-404e-bc7f-803c4fbbae55 tags:
``` python
# Usage example
s = TaskScheduler()
t1 = Task("Task 1", 1, 4)
t2 = Task("Task 2", 2, 5) # overlap
t3 = Task("Task 3", 5, 6)
s.put(t1)
s.put(t2) # cannot add (overlap)
s.put(t3)
for task in s.get():
print(f"{task.name}: [{task.start}, {task.end}]")
```
%% Cell type:code id:14c5196d-d45e-4314-9678-60548fe2f811 tags:
``` python
# Tests
s = TaskScheduler()
assert s.get() == []
```
%% Cell type:markdown id:2911284c-7c5b-4c65-a426-dac028d6bd4f tags:
## Exercise 4: Merge sorted lists (using heaps)
The `heapq` [(doc)](https://docs.python.org/3/library/heapq.html) module provides an efficient implementation of priority queue using the heap sorting algorithm, that use can use instead of the `PriorityQueue` class. Here is an example on how to use the heap. The operations complexity are are:
- `heapify` $O(n)$ as it find the smallest value (only) to return next
- `heappop` $O(log(n))$ as it finds again the smallest value (only) to return next, but in an efficient way
%% Cell type:code id:da1fbbad-7bb6-4f89-a446-bdb308a545ac tags:
``` python
import heapq
nums = [10, 20, 5, 7, 9, 15, 3]
heapq.heapify(nums)
sorted_list = [heapq.heappop(nums) for _ in range(len(nums))]
sorted_list
```
%% Cell type:markdown id:327aa702-5699-42ba-96e1-b75079d31050 tags:
Write a function `merge_sorted_lists(lists)` that takes a list of sorted lists, and uses `heapq` to merge them into a single sorted list as a result.
1. Use `heapq` to store the first element of each list.
2. Store each element in the heap as a tuple `(value, list_index, element_index)`:
- `value` is the element value,
- `list_index` is the index of the list it comes from,
- `element_index` is the position of the element in its original list.
3. At each step, extract the smallest element from the heap and add it to the result list.
4. Insert the next element from the same list into the heap until all lists are empty.
%% Cell type:code id:4b20f11a-47b8-419a-b94a-902943048c76 tags:
``` python
import heapq
def merge(lists):
min_heap = []
merged_list = []
for i, lst in enumerate(lists): # each first element in heap
if lst:
heapq.heappush(min_heap, (lst[0], i, 0)) # (value, list_index, element_index)
while min_heap:
val, list_index, element_index = heapq.heappop(min_heap)
merged_list.append(val)
if element_index + 1 < len(lists[list_index]):
next_val = lists[list_index][element_index + 1]
heapq.heappush(min_heap, (next_val, list_index, element_index + 1))
return merged_list
```
%% Cell type:code id:1b7e1364-1335-4daf-9100-ef933f97b86d tags:
``` python
# Usage example
lists = [
[1, 4, 5],
[1, 3, 4],
[2, 6]
]
merge(lists) # [1, 1, 2, 3, 4, 4, 5, 6]
```
%% Cell type:code id:44600161-32a0-4b84-b152-65a15ba103ae tags:
``` python
# Tests
assert merge([[]]) == []
assert merge([[1, 2, 3]]) == [1, 2, 3]
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment