"# Lab 5: Programming strategies: Brute force and greedy"
]
},
{
"cell_type": "markdown",
"id": "691b3c38-0e83-4bb2-ac90-ef76d2dd9a7a",
"metadata": {},
"source": [
"---"
]
},
{
"cell_type": "markdown",
"id": "3a1dbf76-c34e-4859-ab46-6ff8ba5d4f19",
"metadata": {},
"source": [
"<details style=\"border: 1px\">\n",
"<summary> How to use those notebooks</summary>\n",
" \n",
"For each of the following questions:\n",
"- In the `# YOUR CODE HERE` cell, remove `raise NotImplementedError()` to write your code\n",
"- Write an example of use of your code or make sure the given examples and tests pass\n",
"- Add extra tests in the `#Tests` cell\n",
" \n",
"</details>"
]
},
{
"cell_type": "markdown",
"id": "8f128fee-a1d6-4106-b997-0ed27b5ed91d",
"metadata": {
"tags": []
},
"source": [
"# Exercice 1: Knapsack problem\n",
"\n",
"The goal of the knapsack problem is to select the maximum number of items (each with weights, coming from a limited set) that can be packed in a knapsack, which has a limited capacity (eg a total weight). The problem can be formulated as follows:\n",
"\n",
"$\\max \\sum_{i=1}^n v_i x_i$\n",
"\n",
"- **$x_i ∈{0,1}$** the binary variable to pick item $i$ from $n$ weights\n",
"assert check_knapsack_solution(weights, capacity, selected_items) # it fits"
]
},
{
"cell_type": "markdown",
"id": "e1ea24d2-df15-4b8a-923b-4006d0911a81",
"metadata": {
"tags": []
},
"source": [
"## 1.2 Brute force approach"
]
},
{
"cell_type": "markdown",
"id": "d8552dac-ea9d-4ded-a57f-da86dbcfff47",
"metadata": {},
"source": [
"A brute force solution to the Knapsack problem is trying **all possible combinations of items** and check if they fit. You may implement such solution as follows:\n",
"\n",
"- Calculate the combination of items selections and their total weight\n",
"- Check if each combination is within the knapsack capacity\n",
"- Return the best (valid) combination that fits within the capacity\n",
"\n",
"You may use the Cartesian product ([doc](https://docs.python.org/3/library/itertools.html#itertools.product)) to create the list of all possible selections by permutting $x_i$. This means to create an array of permutation of selected items (`0` meaning the item is not selected, `1` it is selected). So `(0, 0, 0, 0)` means we do not pick any item, and `(1, 1, 1, 1)` we pick them all. Here is the code for the product:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e688c41f-ef8c-413b-b86a-c116c457d8b1",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from itertools import product\n",
"list(product([0, 1], repeat=len(weights)))"
]
},
{
"cell_type": "markdown",
"id": "aa8969ef-8ec2-4765-979a-d0f3a363943f",
"metadata": {},
"source": [
"The brute force function will return a tuple with both the sum and the list of $x_i$ as a `Tuple`."
"As you can see the brute force outperfoms the greedy for this particular scenario. "
]
},
{
"cell_type": "markdown",
"id": "e7602801-5fb5-4904-bafa-810d9787c6f5",
"metadata": {
"nbgrader": {
"grade": false,
"grade_id": "cell-9e7356aa23ccfb4c",
"locked": false,
"schema_version": 3,
"solution": false,
"task": false
},
"tags": []
},
"source": [
"## 1.4 Greedy with limited availability\n",
"\n",
"Now write a new version of the Greedy function but with a limit `max_quantity` variable that limits the quantity of items that can be picked. Eg with `max_quantity = 2` each item can only be picked twice."
"Write a greedy algorithm that returns a list of time slots that do not overlap. The time slots are provided using the `(start, end)` format where start/end are integers (instead of time). "
]
},
{
"cell_type": "markdown",
"id": "2698e92a-188d-4dbf-8e76-f7b7374db2b0",
"metadata": {},
"source": [
"Let's begin by writing a function that checks that a given schedule is OK:\n",
"\n",
"- Each time slot has `start < end` and `duration > 0`\n",
"assert not check_schedule_solution([(0, 3), (2, 4)])"
]
},
{
"cell_type": "markdown",
"id": "b8219a51-e70c-4885-941f-287bff3eadfc",
"metadata": {},
"source": [
"Now write the greedy algorithm that picks time slots without overlaps. In this question you may prioritize the ones that end last, so you may sort by [reverse order](https://docs.python.org/3/howto/sorting.html). Eg:\n",
The goal of the knapsack problem is to select the maximum number of items (each with weights, coming from a limited set) that can be packed in a knapsack, which has a limited capacity (eg a total weight). The problem can be formulated as follows:
$\max \sum_{i=1}^n v_i x_i$
-**$x_i ∈{0,1}$** the binary variable to pick item $i$ from $n$ weights
A brute force solution to the Knapsack problem is trying **all possible combinations of items** and check if they fit. You may implement such solution as follows:
- Calculate the combination of items selections and their total weight
- Check if each combination is within the knapsack capacity
- Return the best (valid) combination that fits within the capacity
You may use the Cartesian product ([doc](https://docs.python.org/3/library/itertools.html#itertools.product)) to create the list of all possible selections by permutting $x_i$. This means to create an array of permutation of selected items (`0` meaning the item is not selected, `1` it is selected). So `(0, 0, 0, 0)` means we do not pick any item, and `(1, 1, 1, 1)` we pick them all. Here is the code for the product:
Now write a new version of the Greedy function but with a limit `max_quantity` variable that limits the quantity of items that can be picked. Eg with `max_quantity = 2` each item can only be picked twice.
Write a greedy algorithm that returns a list of time slots that do not overlap. The time slots are provided using the `(start, end)` format where start/end are integers (instead of time).
Now write the greedy algorithm that picks time slots without overlaps. In this question you may prioritize the ones that end last, so you may sort by [reverse order](https://docs.python.org/3/howto/sorting.html). Eg: