diff --git a/TD02/INF-TC1-td02.ipynb b/TD02/INF-TC1-td02.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..d8e75a594f09649683ced71f88bb5e191ad22dbe --- /dev/null +++ b/TD02/INF-TC1-td02.ipynb @@ -0,0 +1,1339 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8d6e8e15", + "metadata": {}, + "source": [ + "NAME:" + ] + }, + { + "cell_type": "markdown", + "id": "4079279c", + "metadata": {}, + "source": [ + "# INF TC1 - TD2 (2h) - Structures de données" + ] + }, + { + "cell_type": "markdown", + "id": "74d75def", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "id": "42890ec6-251a-404c-a6f6-39c138db8650", + "metadata": { + "tags": [] + }, + "source": [ + "### IMPORTANT A LIRE (SUR L'UTILISATION DE CE NOTEBOOK)\n", + "\n", + "Le but de votre travail est de répondre aux questions des exercices en **remplissant certaines cellules de ce notebook avec votre solution**. Ces cellules, une foit remplies et lancées au fur et à mesure de vos avancées, permettront de valider des tests écrits dans d'autres cellules de ce notebook. **Il est donc important de bien suivre les instructions et répondre aux questions dans l'ordre**, et ne pas changer le nom des fonctions et/ou les cellules. En particulier :\n", + " \n", + "1) Répondez aux questions dans les cellules en dessous des questions.\n", + "\n", + "2) Votre code devra remplacer le texte suivant : \n", + "erge\n", + "```python\n", + "# YOUR CODE HERE\n", + "raise NotImplementedError()\n", + "```\n", + "\n", + "(vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus).\n", + "\n", + "3) Exécuter enfin les cellules dans leur ordre d'apparition, de haut en bas et si votre code est correct alors les tests (sous forme d'`assert` seront validés (ils ne lanceront pas d'exception du type `AssertionError` ). Vous pouvez lancer plusieurs fois la même cellule, cela ne pose pas de soucis.\n", + " \n", + "4) Vous pouvez créer de nouvelles cellules comme bon vous semble.\n", + "\n", + "**En cas de problème, une solution est de relancer les cellules depuis le début du notebook une par une.** Pensez à bien sauvegarder ce notebook et ne pas le remplacer par un notebook qui a le même nom.\n" + ] + }, + { + "cell_type": "markdown", + "id": "b692d4dd-6dbc-40e9-9c19-0b59e31e5eab", + "metadata": {}, + "source": [ + "## Objectif du TD\n", + "\n", + "Ce TD vous fera manipuler plusieurs structures de données standard en Python (listes, dictionnaires) mais également des structures avancées (piles, files tas) que vous allez créer au moyen de classes. Au final nous allons créer une méthode de tri efficace (tri par tas) et la comparer avec d'autres méthodes de tri de Python." + ] + }, + { + "cell_type": "markdown", + "id": "b6221ad1-379e-4e50-8c22-220994439b6d", + "metadata": {}, + "source": [ + "## Exercice 1 - Chargement et tri d'une liste\n", + "\n", + "Le but de cet exercice est de charger une liste de dictionnaires et réaliser des méthodes de tri. Vous disposez pour cela d'un fichier appelé [`etudiants.txt`](etudiants.txt) où chaque ligne contient des informations sur des étudiants d'un cour. Pour commencer nous allons réaliser des tris simples et les rendre de plus en plus complexes." + ] + }, + { + "cell_type": "markdown", + "id": "985e2dd2-dc05-437c-b3f0-60696fe27e3f", + "metadata": {}, + "source": [ + "### Rappel : Tri de listes\n", + "\n", + "Avant de commencer quelques rappels sur les structures de données de listes et leurs tris. Voici une liste en Python :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "327780ed-4331-43ad-921b-9e42d762f2aa", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "L = [3, 2 , 4]" + ] + }, + { + "cell_type": "markdown", + "id": "01263b26-3983-4073-aaff-1a30d69ff914", + "metadata": { + "tags": [] + }, + "source": [ + "Pour la trier vous pouvez utiliser ```.sort()``` [(doc)](https://docs.python.org/3/howto/sorting.html) qui modifie la liste actuelle :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79f084ad-942d-46c6-bfe7-2722eeeda426", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "L = [3, 2 , 4]\n", + "L.sort()\n", + "L" + ] + }, + { + "cell_type": "markdown", + "id": "f04dbfe7-c000-4c2e-a2c4-e6187fe17ba7", + "metadata": { + "tags": [] + }, + "source": [ + "Soit vous créez une nouvelle liste triée qui ne modifie pas la liste actuelle en utilisant ```sorted``` [(doc)](https://docs.python.org/3/howto/sorting.html) :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9b79862-a7b6-4b74-af5c-3d36f85f7b67", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "L = [3, 2 , 4]\n", + "print(sorted(L))\n", + "L" + ] + }, + { + "cell_type": "markdown", + "id": "4affcb3d-d8ae-4c5e-a7f5-fbc59b1fff76", + "metadata": {}, + "source": [ + "Enfin les fonctions de tri peuvent prendre un [paramètre nommé](https://docs.python.org/3/glossary.html#term-parameter) `key` afin d'indiquer sur quel attribut de la liste réaliser le tri. Ci dessous le tri sera fait sur le premier élément d'une liste de listes à trier :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76f37b8b-69f5-4df0-91ae-b221a372e519", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "L = [[3, \"C\"], [1, \"A\"], [2, \"B\"]]\n", + "L.sort(key=lambda x: x[0])\n", + "L" + ] + }, + { + "cell_type": "markdown", + "id": "c815465f", + "metadata": {}, + "source": [ + "### Chargement d'un fichier en dictionnaire\n", + "\n", + "Le code ci-dessous permet de charger ce fichier dans la variable `students_list`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe36de75-c9c4-49b7-ad79-de78c4f5b3ca", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "students_list = []\n", + "\n", + "with open(\"etudiants.txt\") as f:\n", + " keys = None\n", + " for line in f:\n", + " l = [w.strip() for w in line.split(';')]\n", + " if keys is None:\n", + " keys = l\n", + " else:\n", + " students_list.append({k:v for k, v in zip(keys, l)})" + ] + }, + { + "cell_type": "markdown", + "id": "cfb09dcd-d8eb-474d-9f01-bff6de7b114a", + "metadata": { + "tags": [] + }, + "source": [ + "Un echantillon du jeu de données vous est donné comme suit, il s'agit une liste de dictionnaires [(doc)](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a0ab86a7-ed2a-4265-be05-fabd7a7b7fe5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "students_list[0:2]" + ] + }, + { + "cell_type": "markdown", + "id": "d54fe400", + "metadata": {}, + "source": [ + "**Question 1.1 -** Calculez la moyenne de tous les étudiants disponibles dans la liste d'étudiants `students_list`. Vous nommerez votre fonction `average_grade` et lui donnerez en paramètre la variable `L` qui contient la liste d'étudiant. Conseil : pensez à convertir les variables au bon type de données (par ex. en utilisant `int()`ou `float()`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "084ca248-e638-4416-9fed-32dde916fb5b", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "91158fa1e745aa0e8763ed62e53c76f7", + "grade": false, + "grade_id": "cell-0aa9f72deedd3eb2", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def average_grade(L: list)-> int:\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "66486e51-46fa-4475-a990-9764f1206f21", + "metadata": {}, + "source": [ + "La moyenne attendue de votre fonction est :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6086d5b-1a84-4cee-8bd3-b3acf46d68d3", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "average_grade(students_list)" + ] + }, + { + "cell_type": "markdown", + "id": "65b4a7d1-d0d6-4ec4-a609-b7eb42157cea", + "metadata": {}, + "source": [ + "Le test ci-dessous doit donc être validé (autrement dit aucune `Exception` ne doit être lancée) :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "836938d8-a4c7-44e2-b17b-2159acd11aad", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert average_grade(students_list) == 16.6" + ] + }, + { + "cell_type": "markdown", + "id": "2f986196-2afa-4baf-96a3-3d22280e6c22", + "metadata": { + "tags": [] + }, + "source": [ + "**Question 1.2 -** Trouver la note maximale de manière _récursive_ et comparez avec la fonction `max` (qui peut prendre un argument `key` afin de trier par note) donnée ci-dessous. Attention aux types des données." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45624183-f5e7-43ae-9e41-ab8b456d9360", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "d592b490ddccdfa2028394edb174c41c", + "grade": false, + "grade_id": "cell-103fc5f998c99492", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def find_maximum_recursive(L: list)-> int:\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef350eab", + "metadata": {}, + "outputs": [], + "source": [ + "find_maximum_recursive(students_list)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59684c44-83db-47c3-a192-781041fc3b59", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert find_maximum_recursive(students_list) == int(max(students_list, key=lambda x: int(x[\"note\"]))[\"note\"])" + ] + }, + { + "cell_type": "markdown", + "id": "a9bca555-e31a-4294-bdac-d9e2ff5fa950", + "metadata": {}, + "source": [ + "**Question 1.3 -** Trouver deux étudiants qui ont la même note, et renvoyez leurs noms sous forme de `Tuple`. Conseil : \n", + "\n", + "- parcourez la liste et mémoriser les notes que vous parcourrez;\n", + "- si une note a déjà été parcourue alors renvoyer l'indice du dictionnaire;\n", + "- enfin renvoyez les noms des étudiants" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1dd770ee-1c63-4fcc-b564-158803c665e9", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "089205cb93f662ef5a024c55dc88359a", + "grade": false, + "grade_id": "cell-01928f10678cd675", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def find_same_grade(L: list)-> tuple:\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbfbaf85-01b6-4bbc-ae1e-b9e7cc47489e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "find_same_grade(students_list)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ef24de8-ed61-40e1-ad9a-26f118223c17", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert find_same_grade(students_list) == ('Dupond', 'Dupont')" + ] + }, + { + "cell_type": "markdown", + "id": "0f948f2e", + "metadata": {}, + "source": [ + "**Question 1.4 -** Trier la liste de données par ordre croissant en implémentant un _tri par sélection_ fourni dans le pseudo-code ci-dessous (issu de [cette page](https://fr.wikipedia.org/wiki/Tri_par_s%C3%A9lection)). L'argument `key` permet d'indiquer sur quel attribut réaliser le tri (ici ce sera la note). Voir l'usage de cet attribut dans la cellule de test suivante.\n", + "\n", + "```\n", + " procédure tri_selection(tableau t)\n", + " n ← longueur(t) \n", + " pour i de 0 à n - 2\n", + " min ← i \n", + " pour j de i + 1 à n - 1\n", + " si t[j] < t[min], alors min ← j\n", + " fin pour\n", + " si min ≠ i, alors échanger t[i] et t[min]\n", + " fin pour\n", + " fin procédure\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03e651d7-1143-4a9f-b453-3469d5b75e52", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "461842f0bd10a79d92c3adb61e9e70fe", + "grade": false, + "grade_id": "cell-3ee4eefc84f634bc", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def sort_selection(L: list, key=lambda x: x) -> list:\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "d95d2146-847a-4c21-9a9a-af277d236aea", + "metadata": {}, + "source": [ + "Comparer votre tri avec la méthode `sorted` de Python." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e27a7071-e10b-4801-8d16-aa611f0866f6", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "768c025dd7d7b842b6ac47a7a2b8e107", + "grade": false, + "grade_id": "cell-4deb9caaaa6d474f", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "dae866db", + "metadata": {}, + "source": [ + "## Exercice 2 : Piles et files\n", + "\n", + "Désormais nous allons implémenter de nouvelles structures de manipulation de listes : les Piles et les Files. Et à terme réaliser un tri de plus en plus efficace (avec un Tas). Nous allons commencer avec la Pile dont nous vous fournissons la structure de données, nommée `Stack` et disponible ci-desous:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4f1b0a9", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "class Stack:\n", + " def __init__(self):\n", + " self.items = []\n", + "\n", + " def push(self, item):\n", + " self.items.append(item)\n", + "\n", + " def pop(self):\n", + " if not self.is_empty():\n", + " return self.items.pop()\n", + "\n", + " def peek(self):\n", + " if not self.is_empty():\n", + " return self.items[-1]\n", + "\n", + " def is_empty(self):\n", + " return len(self.items) == 0" + ] + }, + { + "cell_type": "markdown", + "id": "5ee3c79e-be53-4d98-a2fb-3b0e9adadeb8", + "metadata": {}, + "source": [ + "La Pile ci-dessus a son équivalent avec la méthode `LifoQueue` du module `queue` de Pile défini ci-dessous [(doc)](https://docs.python.org/3/library/queue.html). Nous voyons bien que le dépilement renvoie les données dans l'ordre inverse de leur empilement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77dee078-c30f-40a0-bd29-f64c978b3e0b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import queue\n", + "pile = queue.LifoQueue()\n", + "for i in range (5): \n", + " pile.put(i)\n", + "while not pile.empty():\n", + " print(pile.get(), end=\" \")" + ] + }, + { + "cell_type": "markdown", + "id": "bfaae7f4", + "metadata": {}, + "source": [ + "**Question 2.1 -** Utiliser la Pile `Stack` afin d'empiler les données de la liste `students_list`. Maintenant dépilez cette Pile et comparer les résultats avec la méthode `LifoQueue` (avec le `while` fourni dans le code ci-dessous) :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ad4b923-5f23-4921-b02e-f8b9fd22dd55", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "b710d25ada58e842e7bcd7333d141cc3", + "grade": false, + "grade_id": "cell-63bc7e329d9959d4", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "pile = queue.LifoQueue()\n", + "s = Stack()\n", + "# YOUR CODE HERE\n", + "raise NotImplementedError()\n", + "\n", + "while not s.is_empty() and not pile.empty():\n", + " assert s.pop() == pile.get()" + ] + }, + { + "cell_type": "markdown", + "id": "78b1b0fb-41fb-4e2e-8c2b-3a040fbcc5e0", + "metadata": {}, + "source": [ + "**Question 2.2 -** Transformer la structure de Pile `Stack` en une File (que vous nommerez `Queue`) et vérifiez que vous obtenez les mêmes résultats en récupérant les données qu'avec le module `Queue()` de Python." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ac004cd-f59e-4aa8-a31d-961e24acba69", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "084870f7cbddd7dc85c2b1c02c8648d3", + "grade": false, + "grade_id": "cell-a9eea459cf78c621", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "class Queue():\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b588c0e-d8b8-4a57-b54f-6aef316bb5c5", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "414f416a2a624101edaa0bdeb57fee51", + "grade": false, + "grade_id": "cell-a259250b447e39e2", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "file = queue.Queue()\n", + "f = Queue()\n", + "# YOUR CODE HERE\n", + "raise NotImplementedError()\n", + "\n", + "while not f.is_empty() and not file.empty():\n", + " assert f.pop() == file.get()" + ] + }, + { + "cell_type": "markdown", + "id": "5a8722ae-500d-4956-9b8c-18e664e5b09f", + "metadata": {}, + "source": [ + "Nous voyons bien que le dé-filement renvoie les données dans l'ordre de leur empillement afin de respecter le principe de File." + ] + }, + { + "cell_type": "markdown", + "id": "e1f0a957-2c18-4edd-b37b-d766ac1e8557", + "metadata": { + "tags": [] + }, + "source": [ + "**Question 2.3** - Mettez à jour votre File afin de ne pas générer d'exception `IndexError`. On peut par exemple renvoyer une valeur de type `None` si aucune valeur n'est disponible." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49a596f0-fc7c-4158-bab1-e715e1cf6a57", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "file = Queue()\n", + "try:\n", + " assert file.pop() == None # si on renvoie None pour une file vide, pas d'Exception !\n", + "except IndexError:\n", + " print(\"On ne doit pas générer d'exception IndexError !\")" + ] + }, + { + "cell_type": "markdown", + "id": "9ebbd490-abcd-45e4-8296-c311e34ddf2d", + "metadata": { + "tags": [] + }, + "source": [ + "**Question 2.4** - Enfin, transformer la File (classe `Queue`) pour en faire une file de Priorité `FilePriorite`. Pour rappel, une File de Priorité renvoie les éléments selon un critère particulier (par exemple la note minimale des valeurs contenues dans la file). \n", + "\n", + "Conseils :\n", + "\n", + "- garder la liste des valeurs internes constamment triée lors de l'ajout;\n", + "- pour cela inclure la nouvelle valeur avec la méthode `ajoute()` à la bonne place (en conservant l'ordre de la liste interne) avec `.insert(index, valeur)`\n", + "\n", + "Nous vous fournissons aussi le module `PriorityQueue` qui est une file de priorité existante [(doc)](https://docs.python.org/3/library/queue.html) afin de comparer votre code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa163d01-ed34-447e-8b55-6198140c349b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from queue import PriorityQueue \n", + "import random\n", + " \n", + "filep = PriorityQueue()\n", + "list_random = [random.randint(1, 10) for _ in range(5)] # Liste aléatoire\n", + "\n", + "for i in list_random: filep.put(i)\n", + "\n", + "while not filep.empty(): \n", + " print(filep.get(), end=\" \")" + ] + }, + { + "cell_type": "markdown", + "id": "738fe9f0-7885-40b6-a8cf-eb0c7cd8f3aa", + "metadata": {}, + "source": [ + "Remplir le code ci-dessous basé sur la File afin d'en faire une File de Priorité. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b0a1122-5d8f-4f5f-8ef4-460f9896f4b6", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "69949417a33fbf1bbe25be57237c60e8", + "grade": false, + "grade_id": "cell-f5186a99d30ca43e", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "class FilePriorite():\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e608d6b-458b-4836-8ab2-4371d96ae54b", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "a56011d08cd37ac3315bef7a813524ce", + "grade": false, + "grade_id": "cell-76cf4fdc1a271293", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "filep = PriorityQueue()\n", + "f = FilePriorite()\n", + "\n", + "note_list = [student[\"note\"] for student in students_list]\n", + "# YOUR CODE HERE\n", + "raise NotImplementedError()\n", + "\n", + "while not f.is_empty() and not filep.empty():\n", + " assert f.pop() == filep.get()" + ] + }, + { + "cell_type": "markdown", + "id": "77038e6d-0b73-434c-910d-c7da733b643a", + "metadata": {}, + "source": [ + "## Exercice 3 : Arbre binaire complet sous forme de liste" + ] + }, + { + "cell_type": "markdown", + "id": "5f3fc128-2a2e-42bd-924a-d691c09150d6", + "metadata": {}, + "source": [ + "Nous allons maintenant implémenter un **arbre binaire complet**. Cet arbre sera utile pour l'exercice suivant et la création d'un Tas. Cet arbre binaire sera implémenté en utilisant un tableau (car il s'agit d'un arbre _complet_ où tous les niveaux sont remplis, sauf éventuellement le dernier). L'arbre binaire possède des noeuds ayant un index $i$, avec un fils gauche et un fils droit. Le tableau et l'arbre sont reliés de la façon suivante : \n", + "\n", + "- La racine a la position $i = 0$ (cette valeur sera renvoyée par la fonction `get_racine`)\n", + "- Le parent a la position $\\lfloor (i - 1)/ 2 \\rfloor$ (fonction `get_parent`)\n", + "- Le fils gauche a la position $2 \\times i + 1$ (fonction `get_fils_gauche`)\n", + "- Le fils droit a la position $2 \\times i + 2$ (fonction `get_fils_droit`)\n", + "\n", + "```\n", + " 1\n", + " / \\\n", + " 2 5\n", + " / \\ /\n", + " 3 4 6 \n", + " \n", + "\n", + "La liste correspondante :\n", + "[1, 2, 5, 3, 4, 6]\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "id": "ea502a0e-faa7-4bb2-9627-35cd128521a4", + "metadata": {}, + "source": [ + "**Exercice 3.1** - Implémentez un Arbre Binaire sous forme de classe appellée `BinaryTree` (vous pouvez vous inspirer de la File de Priorité) avec les fonctions ci-dessus. En particulier les méthodes `get_parent`, `get_fils_gauche`, `get_fils_droit` renvoient un `Tuple` (valeur, indice). Vous rajouterez une méthode `taille`qui renvoie la taille de l'arbre binaire (longueur de la liste interne)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6648668-c0e3-47d5-bd83-a49785939877", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "c91ff069482e113d672ed13d2baa8f4b", + "grade": false, + "grade_id": "cell-528191aadbf86414", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "class BinaryTree():\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "d0dd4680-92db-4dad-8647-951269330870", + "metadata": {}, + "source": [ + "**Exercice 3.2** - Assurez vous que tous les tests ci-dessous sont validés." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac65be98-9649-4753-8ead-c0deb89f5f0c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# test arbre vide\n", + "tree_empty = BinaryTree()\n", + "assert tree_empty.taille() == 0\n", + "assert tree_empty.get_racine() == None\n", + "assert tree_empty.get_parent()[0] == None\n", + "assert tree_empty.get_fils_gauche(0)[0] == None \n", + "assert tree_empty.get_fils_droit(0)[0] == None " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f548b21a-b2f5-4de5-9261-38d23035e25d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "L = [1, 2, 5, 3, 4, 6]\n", + "tree_values = BinaryTree(L)\n", + "assert tree_values.taille() == len(L) # 6\n", + "assert tree_values.get_racine() == L[0] # 3\n", + "assert tree_values.get_fils_gauche(0)[0] == L[2*0+1] # 2\n", + "assert tree_values.get_fils_droit(0)[0] == L[2*0+2] # 5\n", + "assert tree_values.get_parent(1)[0] == L[0] # 5" + ] + }, + { + "cell_type": "markdown", + "id": "25213313-5058-415a-a3bc-7948c3d98588", + "metadata": {}, + "source": [ + "Cette structure de donnée sera utile pour la question suivante afin de créer un `Tas`." + ] + }, + { + "cell_type": "markdown", + "id": "5f35e9de-025f-4c89-a004-a24af997474a", + "metadata": {}, + "source": [ + "**Exercice 3.3** - L'abre que vous venez de créer sera utilisé dans l'exercice suivant afin de créer un Tas. Pour cela nous devrons nous assurer que l'arbre satisfait la propriété suivante : la valeur de tout noeud est inférieure à celle de ses enfant. Ecrire une fonction de parcours de l'arbre qui renvoie `True` si cette propriété est vérifée, sinon `False`. Conseil : \n", + "\n", + "1. Proposez une approche récursive\n", + "2. Le cas d'arrêt est un noeud vide\n", + "3. L'appel récursif est déclanché en fonction de la valeur du noeud en cours et celle de ses enfants\n", + "4. Au final renvoyer `True` si aucune condition ne renvoie `False`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93924cc3-9d92-40c7-b7d7-96a775e121b1", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "5fb99bc2dc0016315c6e83b11c46632a", + "grade": false, + "grade_id": "cell-47e9cfb25fe2c5e5", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def check_min_tree(T, i=0):\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da01b2ab-4633-443f-97ea-b1022bc714ef", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "check_min_tree(tree_values)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a28bf560-5fed-40cd-b8bf-0389e11fd8bd", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert check_min_tree(BinaryTree([1])) == True\n", + "assert check_min_tree(BinaryTree([1, 2])) == True\n", + "assert check_min_tree(BinaryTree([1, 2, 3])) == True\n", + "assert check_min_tree(BinaryTree([1, 2, 3, 4])) == True\n", + "assert check_min_tree(BinaryTree([5, 6, 4])) == False" + ] + }, + { + "cell_type": "markdown", + "id": "d9f0166d-d12f-42a1-989a-b7014117e73d", + "metadata": { + "tags": [] + }, + "source": [ + "## Exercice 4 (Bonus) : Création d'un tas et tri par tas\n", + "\n", + "Nous allons désormais créer une nouvelle structure de donnée : le `Tas`. Celle-ci permettra à termes de réduire la complexité de manipulation d'une file de priorité, en répartissant le coût de la recherche du plus petit élément (qui sera renvoyé) entre l'ajout et la suppression.\n", + "\n", + "Nous nous baserons pour cela sur l'arbre binaire de la question précédente, qui est dit dans une configuration [min-heap](https://en.wikipedia.org/wiki/Min-max_heaphttps://en.wikipedia.org/wiki/Min-max_heap), où les données sont renvoyées par ordre croissant." + ] + }, + { + "cell_type": "markdown", + "id": "6061d866-51fb-48bc-9f1a-46b1b57a0ec0", + "metadata": {}, + "source": [ + "**Question 4.1 -** Implémentez une structure de `Tas`comme suit :\n", + "\n", + "- Créez une structure de données de `Tas` similaire au `BinaryTree`\n", + " \n", + "- Créez une méthode `inserer` (que l'on utilisera à la place d'`ajoute`) dont le principe est le suivant :\n", + "\n", + " - Chaque nouveau noeud est rajouté comme dernier élément du tableau (à la fin donc)\n", + "\n", + " - Comparez ce noeud à son parent et si il est plus grand que ce parent inversez-le\n", + "\n", + " - Répétez tant que la condition ci-dessus est vraie et que la racine n'est pas atteinte\n", + " \n", + "\n", + "- Créez une méthode `enlever` dont le principe est le suivant :\n", + "\n", + " - Enlever l'élément racine de l'arbre (premier élément du tableau)\n", + "\n", + " - Déplacer le dernier noeud de l'arbre (dernier élément du tableau) à la place de la racine de l'arbre\n", + "\n", + " - Vérifier que la racine conserve la propriété de Tas (qu'elle est inférieur à ses enfants); si ce n'est pas le cas alors implémenter une méthode `descendre` définie par la suite.\n", + "\n", + "- Créez une méthode `descendre` qui : \n", + "\n", + " - Prend le plus petit des enfants\n", + " - Echange sa place avec lui si il est plus petit\n", + " - Répéte cela tant qu'il existe des enfants\n", + " \n", + "Attention : pensez à tester si il existe un fils droit et un fils gauche lors des opération de descente lors de l'insertion." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "301e69f3-4035-4b10-9c85-f4427d92e03b", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "1527573490f338be2ecd33a9df08ceb7", + "grade": false, + "grade_id": "cell-1f833c3f451e29d9", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "class Tas():\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "0b467f58-c800-4ae3-b066-8717732a1095", + "metadata": {}, + "source": [ + "Votre tas doit valider les tests suivants :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c41d4a7-5971-497b-b484-4a42a2480b06", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# test tas vide\n", + "tas_vide = Tas()\n", + "assert tas_vide.taille() == 0\n", + "assert tas_vide.get_racine() == None\n", + "assert tas_vide.get_fils_droit()[0] == None \n", + "assert tas_vide.get_fils_droit()[0] == None \n", + "\n", + "# test tas simple\n", + "tas_simple = Tas()\n", + "tas_simple.inserer(1)\n", + "tas_simple.inserer(2)\n", + "assert tas_simple.taille() == 2\n", + "\n", + "# test tas un peu plus complexe\n", + "tas = Tas()\n", + "liste = [1, 4, 10000, 2, 29, .2, 13, .5, 14, .1, 100]\n", + "liste_triee = sorted(liste)\n", + "for l in liste:\n", + " tas.inserer(l)\n", + "\n", + "assert tas.taille() == len(liste) \n", + "assert tas.get_racine() == liste_triee[0]\n", + "assert tas.get_fils_gauche(0) == (liste_triee[1], 1)\n", + "assert tas.get_fils_droit(0) == (liste_triee[2], 2)\n", + "\n", + "while not tas.est_vide():\n", + " assert tas.enlever(0) == liste_triee.pop(0)\n", + " assert tas.taille() == len(liste_triee)" + ] + }, + { + "cell_type": "markdown", + "id": "ec678229-485e-4f3b-8c24-56febe7bd69e", + "metadata": {}, + "source": [ + "**Question 4.2 -** Implémentez un tri par tas en utilisant la structure de données de `Tas` que vous avez réalisé précédemment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d483588-4153-4614-b969-854846b08f4e", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "812a12d8443aeaac357684ff5a590f6f", + "grade": false, + "grade_id": "cell-3a78d4e48e46d4d9", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def triTas(l: list = []) -> list:\n", + " t = Tas()\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "e7ab0b07-f13c-4b95-b5fa-c38aea9b6ead", + "metadata": {}, + "source": [ + "Comparez à la méthode de tri `sorted`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e9fd04a-910a-4d32-b0dc-0a1502599901", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "liste = [54,26,93,17,77,31,44,55,20]\n", + "l2 = triTas(liste.copy())\n", + "\n", + "assert(l2 == sorted(liste))\n", + "assert([] == triTas([]))\n", + "assert([1] == triTas([1]))\n", + "assert([1, 1] == triTas([1, 1]))" + ] + }, + { + "cell_type": "markdown", + "id": "49bae650-a23a-41f7-8896-8ac502cd71e8", + "metadata": {}, + "source": [ + "Pour information, le module `heapq` contient l'implémentation d'un tas en Python et s'utilise comme les Piles, Files, etc." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5bcc3ebf-749e-4a10-8b90-37cfd2488171", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import heapq\n", + "tas = []\n", + "\n", + "for i in range(5): heapq.heappush(tas, i)\n", + "\n", + "while not len(tas) == 0: \n", + " print(heapq.heappop(tas), end=\" \")\n", + "\n", + "# 0 1 2 3 4" + ] + }, + { + "cell_type": "markdown", + "id": "7f774fa4-c586-4d5c-9cf3-5f9bb899f725", + "metadata": { + "tags": [] + }, + "source": [ + "**Question 4.2 -** Comparez la performance (en temps) des méthodes de tri que vous avez implémenté dans les questions précentes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56e72b68-1d5c-4cea-8e9a-6d9f5f9ae34d", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "4c8bbe5f3d1599e3ca6db336ae4e3603", + "grade": false, + "grade_id": "cell-ca3b537d9dcc8790", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import time\n", + "import random\n", + "import matplotlib.pyplot as plt\n", + "\n", + "nvalues = [100, 500, 1500, 2000, 2500, 3000]\n", + "\n", + "timesSorted = []\n", + "timesSort = []\n", + "timesSelection = []\n", + "timesHeap = []\n", + "\n", + "for i in nvalues:\n", + "\n", + " random.seed()\n", + " p = 12**2\n", + " liste = []\n", + " \n", + " for x in range(i): liste.append(random.randint(0, p))\n", + "\n", + " # tri sorted\n", + " c = liste.copy()\n", + " a=time.perf_counter()\n", + " triSorted = sorted(c)\n", + " b=time.perf_counter()\n", + " timesSorted.append(b-a)\n", + "\n", + " # tri .sort()\n", + " c = liste.copy()\n", + " a=time.perf_counter()\n", + " triSort = c\n", + " triSort.sort()\n", + " b=time.perf_counter()\n", + " timesSort.append(b-a)\n", + "\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()\n", + " \n", + "plt.plot(nvalues, timesSorted, \"g-\", label=\"Tri Sorted\")\n", + "plt.plot(nvalues, timesSort, \"b-\", label=\"Tri .sort()\")\n", + "plt.plot(nvalues, timesSelection, \"r-\", label=\"Tri Selection\")\n", + "plt.plot(nvalues, timesHeap, \"r-\", label=\"Tri Heap\")\n", + "plt.xlabel(\"Taille du jeu de données\")\n", + "plt.ylabel(\"Temps\")\n", + "plt.legend(loc=\"upper left\")\n", + "plt.title(\"Comparaison des performances des algorithmes de tri\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "04456b1e-92fd-4433-b230-8ea4374595a0", + "metadata": {}, + "source": [ + "Pour en savoir plus comment Python réalise le tri, lire la documentation du `TimSort` (doc)[https://en.wikipedia.org/wiki/Timsort] qui est l'algorithme de tri utilisé." + ] + }, + { + "cell_type": "markdown", + "id": "3e925273-2161-4c10-8e79-dabcc9bb38cb", + "metadata": {}, + "source": [ + "## Pour aller plus loin\n", + "\n", + "- Mettez à jour votre file afin de renvoyer [une exception](https://docs.python.org/3/tutorial/errors.html) si on demande une valeur qui n'est pas dans la structure de données (pile, file, etc.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50f77357-df56-486b-a3c0-7d2d950c766e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def popx(self):\n", + " if not self.is_empty():\n", + " return self.items.pop(0)\n", + " else:\n", + " raise IndexError(\"Le tas est vide\")\n", + "Stack.popx = popx\n", + "s = Stack()\n", + "try:\n", + " s.popx()\n", + "except IndexError as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3bf64f58-fbcd-44e8-b827-378e85968285", + "metadata": { + "tags": [] + }, + "source": [ + "- Utilisez un [grand jeu de donnée](https://generatedata.com/) d'étudiants pour les premières questions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca5f3173-89da-4103-ae04-b60f8b4c8065", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}