Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • rvuillem/inf-tc1
  • hwei/inf-tc1
  • nmbengue/inf-tc1
  • fernanda/inf-tc1
  • mleger/inf-tc1
  • lmeng/inf-tc1
  • gferryla/inf-tc1
  • jconso/inf-tc1
  • smaghsou/inf-tc1
  • emarquet/inf-tc1
  • ecluzel/inf-tc1
  • aaudeoud/inf-tc1
  • tsegond/inf-tc1
  • aetienne/inf-tc1
  • djoly/inf-tc1
  • bcampeas/inf-tc1
  • dnovarez/inf-tc1
  • ruetm/inf-tc1
  • cchenu/inf-tc1
  • cguiotdu/inf-tc1
  • mclouard/inf-tc1
  • gwachowi/inf-tc1
  • qbaalaou/inf-tc1
  • sbrocas/inf-tc1
  • ppupion/inf-tc1
  • kinty/inf-tc1
  • hadomo/inf-tc1
  • tgicquel/inf-tc1
  • rhahn/inf-tc1
  • cguyau/inf-tc1
  • mpairaul/inf-tc1
  • rmuller/inf-tc1
  • rlecharp/inf-tc1
  • asebasty/inf-tc1
  • qmaler/inf-tc1
  • aoussaid/inf-tc1
  • kcherigu/inf-tc1
  • sgu/inf-tc1
  • malcalat/inf-tc1
  • afalourd/inf-tc1
  • phugues/inf-tc1
  • lsteunou/inf-tc1
  • llauschk/inf-tc1
  • langloia/inf-tc1
  • aboucard/inf-tc1
  • wmellali/inf-tc1
  • ifaraidi/inf-tc1
  • lir/inf-tc1
  • ynedjar/inf-tc1
  • schneidl/inf-tc1
  • zprandi/inf-tc1
  • acoradid/inf-tc1
  • amarcq/inf-tc1
  • dcombet/inf-tc1
  • gplaud/inf-tc1
  • mkernoaj/inf-tc1
  • ldiciocc/inf-tc1
  • gbichot/inf-tc1
  • tdutille/inf-tc1
59 results
Select Git revision
Show changes
Commits on Source (21)
Showing
with 1086 additions and 206 deletions
No preview for this file type
%% Cell type:markdown id:6e217066 tags: %% Cell type:markdown id:c8809a88 tags:
NAME: NAME:
%% Cell type:markdown id:4079279c tags: %% Cell type:markdown id:4079279c tags:
# INF TC1 - TD2 (2h) - Structures de données # INF TC1 - TD2 (2h) - Structures de données
%% Cell type:markdown id:74d75def tags: %% Cell type:markdown id:74d75def tags:
--- ---
%% Cell type:markdown id:42890ec6-251a-404c-a6f6-39c138db8650 tags: %% Cell type:markdown id:42890ec6-251a-404c-a6f6-39c138db8650 tags:
### IMPORTANT A LIRE (SUR L'UTILISATION DE CE NOTEBOOK) ### IMPORTANT A LIRE (SUR L'UTILISATION DE CE NOTEBOOK)
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 : 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 des cellules. En particulier :
1) Répondez aux questions dans les cellules en dessous des questions. 1) Répondez aux questions dans les cellules en dessous des questions.
2) Votre code devra remplacer le texte suivant : 2) Votre code devra remplacer le texte suivant (que vous pouvez effacer) :
erge
```python ```python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
(vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus). 3) Exécutez enfin les cellules dans leur ordre d'apparition, de haut en bas et si votre code est correct. Ainsi les tests (sous forme d'`assert` seront validés et ne lanceront pas d'exception du type `AssertionError`. Vous pouvez lancer plusieurs fois la même cellule, cela ne pose pas de soucis.
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.
4) Vous pouvez créer de nouvelles cellules comme bon vous semble. 4) Vous pouvez créer de nouvelles cellules comme bon vous semble.
**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. **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.
%% Cell type:markdown id:b692d4dd-6dbc-40e9-9c19-0b59e31e5eab tags: %% Cell type:markdown id:b692d4dd-6dbc-40e9-9c19-0b59e31e5eab tags:
## Objectif du TD ## Objectif du TD
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. Ce TD vous fera manipuler plusieurs structures de données standard en Python (listes `list`, dictionnaires `dict`) 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 tags: %% Cell type:markdown id:b6221ad1-379e-4e50-8c22-220994439b6d tags:
## Exercice 1 - Chargement et tri d'une liste ## Exercice 1 - Chargement et tri d'une liste
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. 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 tags: %% Cell type:markdown id:985e2dd2-dc05-437c-b3f0-60696fe27e3f tags:
### Rappel : Tri de listes ### Rappel : Tri de listes
Avant de commencer quelques rappels sur les structures de données de listes et leurs tris. Voici une liste en Python : Avant de commencer quelques rappels sur les structures de données de listes et leurs tris. Voici une liste en Python :
%% Cell type:code id:327780ed-4331-43ad-921b-9e42d762f2aa tags: %% Cell type:code id:327780ed-4331-43ad-921b-9e42d762f2aa tags:
``` python ``` python
L = [3, 2 , 4] L1 = [3, 2 , 4]
``` ```
%% Cell type:markdown id:01263b26-3983-4073-aaff-1a30d69ff914 tags: %% Cell type:markdown id:01263b26-3983-4073-aaff-1a30d69ff914 tags:
Pour la trier vous pouvez utiliser ```.sort()``` [(doc)](https://docs.python.org/3/howto/sorting.html) qui modifie la liste actuelle : Pour la trier vous pouvez utiliser ```.sort()``` [(doc)](https://docs.python.org/3/howto/sorting.html) qui modifie la liste actuelle :
%% Cell type:code id:79f084ad-942d-46c6-bfe7-2722eeeda426 tags: %% Cell type:code id:79f084ad-942d-46c6-bfe7-2722eeeda426 tags:
``` python ``` python
L = [3, 2 , 4] L1 = [3, 2 , 4]
L.sort() L1.sort()
L L1
``` ```
%% Cell type:markdown id:f04dbfe7-c000-4c2e-a2c4-e6187fe17ba7 tags: %% Cell type:markdown id:f04dbfe7-c000-4c2e-a2c4-e6187fe17ba7 tags:
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) : 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 id:c9b79862-a7b6-4b74-af5c-3d36f85f7b67 tags: %% Cell type:code id:c9b79862-a7b6-4b74-af5c-3d36f85f7b67 tags:
``` python ``` python
L = [3, 2 , 4] L2 = [3, 2 , 4]
print(sorted(L)) L2 = sorted(L2)
L L2
```
%% Cell type:markdown id:f06e9236-898c-460e-bb23-3a4fed30ad1a tags:
..Et les deux tris sont identiques :
%% Cell type:code id:45b395c0-192f-47db-833d-4087d8244dea tags:
``` python
L1 == L2
``` ```
%% Cell type:markdown id:4affcb3d-d8ae-4c5e-a7f5-fbc59b1fff76 tags: %% Cell type:markdown id:4affcb3d-d8ae-4c5e-a7f5-fbc59b1fff76 tags:
Enfin les fonctions de tri peuvent prendre un paramètre `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 : 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 appliqué sur le premier élément d'une liste de listes à trier :
%% Cell type:code id:76f37b8b-69f5-4df0-91ae-b221a372e519 tags: %% Cell type:code id:76f37b8b-69f5-4df0-91ae-b221a372e519 tags:
``` python ``` python
L = [[3, "C"], [1, "A"], [2, "B"]] L = [[3, "C"], [1, "A"], [2, "B"]]
L.sort(key=lambda x: x[0]) L.sort(key=lambda x: x[0])
L L
``` ```
%% Cell type:markdown id:c815465f tags: %% Cell type:markdown id:c815465f tags:
### Chargement d'un fichier en dictionnaire ### Chargement d'un fichier en dictionnaire
Le code ci-dessous permet de charger ce fichier dans la variable `students_list`. Le code ci-dessous permet de charger ce fichier dans la variable `students_list`.
%% Cell type:code id:fe36de75-c9c4-49b7-ad79-de78c4f5b3ca tags: %% Cell type:code id:fe36de75-c9c4-49b7-ad79-de78c4f5b3ca tags:
``` python ``` python
students_list = [] students_list = []
with open("etudiants.txt") as f: with open("etudiants.txt") as f:
keys = None keys = None
for line in f: for line in f:
l = [w.strip() for w in line.split(';')] l = [w.strip() for w in line.split(';')]
if keys is None: if keys is None:
keys = l keys = l
else: else:
students_list.append({k:v for k, v in zip(keys, l)}) students_list.append({k:v for k, v in zip(keys, l)})
``` ```
%% Cell type:markdown id:cfb09dcd-d8eb-474d-9f01-bff6de7b114a tags: %% Cell type:markdown id:cfb09dcd-d8eb-474d-9f01-bff6de7b114a tags:
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) : Un échantillon 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 id:a0ab86a7-ed2a-4265-be05-fabd7a7b7fe5 tags: %% Cell type:code id:a0ab86a7-ed2a-4265-be05-fabd7a7b7fe5 tags:
``` python ``` python
students_list[0:2] students_list[0:2]
``` ```
%% Cell type:markdown id:d54fe400 tags: %% Cell type:markdown id:d54fe400 tags:
**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()`). **Question 1.1 -** Calculez la moyenne des dans la liste `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 id:084ca248-e638-4416-9fed-32dde916fb5b tags: %% Cell type:code id:084ca248-e638-4416-9fed-32dde916fb5b tags:
``` python ``` python
def average_grade(L: list)-> int: def average_grade(L: list)-> int:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:66486e51-46fa-4475-a990-9764f1206f21 tags: %% Cell type:markdown id:66486e51-46fa-4475-a990-9764f1206f21 tags:
La moyenne attendue de votre fonction est : La moyenne attendue de votre fonction est :
%% Cell type:code id:e6086d5b-1a84-4cee-8bd3-b3acf46d68d3 tags: %% Cell type:code id:e6086d5b-1a84-4cee-8bd3-b3acf46d68d3 tags:
``` python ``` python
average_grade(students_list) average_grade(students_list)
``` ```
%% Cell type:markdown id:65b4a7d1-d0d6-4ec4-a609-b7eb42157cea tags: %% Cell type:markdown id:65b4a7d1-d0d6-4ec4-a609-b7eb42157cea tags:
Le test ci-dessous doit donc être validé (autrement dit aucune `Exception` ne doit être lancée) : Le test ci-dessous doit donc être validé (autrement dit aucune `Exception` ne doit être lancée) :
%% Cell type:code id:836938d8-a4c7-44e2-b17b-2159acd11aad tags: %% Cell type:code id:836938d8-a4c7-44e2-b17b-2159acd11aad tags:
``` python ``` python
assert average_grade(students_list) == 16.6 assert average_grade(students_list) == 16.6
``` ```
%% Cell type:markdown id:2f986196-2afa-4baf-96a3-3d22280e6c22 tags: %% Cell type:markdown id:2f986196-2afa-4baf-96a3-3d22280e6c22 tags:
**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. **Question 1.2 -** Trouvez 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 encore au type des données.
%% Cell type:code id:45624183-f5e7-43ae-9e41-ab8b456d9360 tags: %% Cell type:code id:45624183-f5e7-43ae-9e41-ab8b456d9360 tags:
``` python ``` python
def find_maximum_recursive(L: list)-> str: def find_maximum_recursive(L: list)-> int:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:ef350eab tags:
``` python
find_maximum_recursive(students_list)
```
%% Cell type:code id:59684c44-83db-47c3-a192-781041fc3b59 tags: %% Cell type:code id:59684c44-83db-47c3-a192-781041fc3b59 tags:
``` python ``` python
assert find_maximum_recursive(students_list) == int(max(students_list, key=lambda x: int(x["note"]))["note"]) 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 tags: %% Cell type:markdown id:a9bca555-e31a-4294-bdac-d9e2ff5fa950 tags:
**Question 1.3 -** Trouver deux étudiants qui ont la même note, et renvoyez leurs noms sous forme de `Tuple`. Conseil : **Question 1.3 -** Ecrivez une fonction de recherche d'une pair d'étudiants qui ont la même note, et renvoyez leurs noms sous forme de `Tuple`. Conseil :
- parcourez la liste et mémoriser les notes que vous parcourrez; - parcourez la liste et mémoriser les notes que vous parcourrez;
- si une note a déjà été parcourue alors renvoyer l'indice du dictionnaire; - si une note a déjà été visitée alors renvoyer l'indice du dictionnaire;
- enfin renvoyez les noms des étudiants - enfin renvoyez les noms des étudiants.
%% Cell type:code id:1dd770ee-1c63-4fcc-b564-158803c665e9 tags: %% Cell type:code id:1dd770ee-1c63-4fcc-b564-158803c665e9 tags:
``` python ``` python
def find_same_grade(L: list)-> tuple: def find_same_grade(L: list)-> tuple:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:dbfbaf85-01b6-4bbc-ae1e-b9e7cc47489e tags: %% Cell type:code id:dbfbaf85-01b6-4bbc-ae1e-b9e7cc47489e tags:
``` python ``` python
find_same_grade(students_list) find_same_grade(students_list)
``` ```
%% Cell type:code id:5ef24de8-ed61-40e1-ad9a-26f118223c17 tags: %% Cell type:code id:5ef24de8-ed61-40e1-ad9a-26f118223c17 tags:
``` python ``` python
assert find_same_grade(students_list) == ('Dupond', 'Dupont') assert find_same_grade(students_list) == ('Dupond', 'Dupont')
``` ```
%% Cell type:markdown id:0f948f2e tags: %% Cell type:markdown id:0f948f2e tags:
**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. **Question 1.4 (BONUS si vous avez du temps) -** Triez la liste d'étudiants 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` permettra d'indiquer sur quel attribut réaliser le tri (par exemple la note). Voir l'usage de cet attribut dans la cellule de test suivante.
``` ```
procédure tri_selection(tableau t) procédure tri_selection(tableau t)
n ← longueur(t) n ← longueur(t)
pour i de 0 à n - 2 pour i de 0 à n - 2
min ← i min ← i
pour j de i + 1 à n - 1 pour j de i + 1 à n - 1
si t[j] < t[min], alors min ← j si t[j] < t[min], alors min ← j
fin pour fin pour
si min ≠ i, alors échanger t[i] et t[min] si min ≠ i, alors échanger t[i] et t[min]
fin pour fin pour
fin procédure fin procédure
``` ```
%% Cell type:code id:03e651d7-1143-4a9f-b453-3469d5b75e52 tags: %% Cell type:code id:03e651d7-1143-4a9f-b453-3469d5b75e52 tags:
``` python ``` python
def sort_selection(L: list, key=lambda x: x) -> list: def sort_selection(L: list, key=lambda x: x) -> list:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:d95d2146-847a-4c21-9a9a-af277d236aea tags: %% Cell type:markdown id:d95d2146-847a-4c21-9a9a-af277d236aea tags:
Comparer votre tri avec la méthode `sorted` de Python. Comparer votre tri avec la méthode `sorted` de Python.
%% Cell type:code id:e27a7071-e10b-4801-8d16-aa611f0866f6 tags: %% Cell type:code id:e27a7071-e10b-4801-8d16-aa611f0866f6 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:dae866db tags: %% Cell type:markdown id:dae866db tags:
## Exercice 2 : Piles et files ## Exercice 2 : Piles et files
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: 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 id:c4f1b0a9 tags: %% Cell type:code id:c4f1b0a9 tags:
``` python ``` python
class Stack: class Stack:
def __init__(self): def __init__(self):
self.items = [] self.items = []
def push(self, item): def push(self, item):
self.items.append(item) self.items.append(item)
def pop(self): def pop(self):
if not self.is_empty(): if not self.is_empty():
return self.items.pop() return self.items.pop()
def peek(self): def peek(self):
if not self.is_empty(): if not self.is_empty():
return self.items[-1] return self.items[-1]
def is_empty(self): def is_empty(self):
return len(self.items) == 0 return len(self.items) == 0
``` ```
%% Cell type:markdown id:5ee3c79e-be53-4d98-a2fb-3b0e9adadeb8 tags: %% Cell type:markdown id:5ee3c79e-be53-4d98-a2fb-3b0e9adadeb8 tags:
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 empillement. La Pile ci-dessus a son équivalent avec la méthode `LifoQueue` du module `queue` de Python 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 id:77dee078-c30f-40a0-bd29-f64c978b3e0b tags: %% Cell type:code id:77dee078-c30f-40a0-bd29-f64c978b3e0b tags:
``` python ``` python
import queue import queue
pile = queue.LifoQueue() pile = queue.LifoQueue()
for i in range (5): for i in range (5):
pile.put(i) pile.put(i)
while not pile.empty(): while not pile.empty():
print(pile.get(), end=" ") print(pile.get(), end=" ")
``` ```
%% Cell type:markdown id:bfaae7f4 tags: %% Cell type:markdown id:bfaae7f4 tags:
**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` afin de vérifier que vous obtenez les mêmes résultats (avec le `while` fourni dans le code ci-dessous) : **Question 2.1 -** Utilisez 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 id:3ad4b923-5f23-4921-b02e-f8b9fd22dd55 tags: %% Cell type:code id:3ad4b923-5f23-4921-b02e-f8b9fd22dd55 tags:
``` python ``` python
pile = queue.LifoQueue() pile = queue.LifoQueue()
s = Stack() s = Stack()
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
while not s.is_empty() and not pile.empty(): while not s.is_empty() and not pile.empty():
assert s.pop() == pile.get() assert s.pop() == pile.get()
``` ```
%% Cell type:markdown id:78b1b0fb-41fb-4e2e-8c2b-3a040fbcc5e0 tags: %% Cell type:markdown id:78b1b0fb-41fb-4e2e-8c2b-3a040fbcc5e0 tags:
**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. **Question 2.2 -** Transformer la structure de Pile `Stack` en une **File** (que vous nommerez `File`) 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 id:7ac004cd-f59e-4aa8-a31d-961e24acba69 tags: %% Cell type:code id:7ac004cd-f59e-4aa8-a31d-961e24acba69 tags:
``` python ``` python
class Queue(): class File():
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:9b588c0e-d8b8-4a57-b54f-6aef316bb5c5 tags: %% Cell type:code id:9b588c0e-d8b8-4a57-b54f-6aef316bb5c5 tags:
``` python ``` python
file = queue.Queue() file = queue.Queue()
f = Queue() f = File()
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
while not f.is_empty() and not file.empty(): while not f.is_empty() and not file.empty():
assert f.pop() == file.get() assert f.pop() == file.get()
``` ```
%% Cell type:markdown id:5a8722ae-500d-4956-9b8c-18e664e5b09f tags: %% Cell type:markdown id:5a8722ae-500d-4956-9b8c-18e664e5b09f tags:
Nous voyons bien que le défilement renvoie les données dans l'ordre de leur empillement afin de respecter le principe de File. 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 tags: %% Cell type:markdown id:e1f0a957-2c18-4edd-b37b-d766ac1e8557 tags:
**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. **Question 2.3** - Mettez à jour votre File afin de ne pas générer d'[exception](https://docs.python.org/3/tutorial/errors.html) `IndexError`. On peut par exemple renvoyer une valeur de type `None` si aucune valeur n'est disponible. Ci-dessous un exemple de capture d'exception en Python sur lequel vous pouvez vous baser :
%% Cell type:code id:49a596f0-fc7c-4158-bab1-e715e1cf6a57 tags: %% Cell type:code id:49a596f0-fc7c-4158-bab1-e715e1cf6a57 tags:
``` python ``` python
file = Queue() file = File()
try: try:
assert file.pop() == None # si on renvoie None pour une file vide, pas d'Exception ! assert file.pop() == None # si on renvoie None pour une file vide, pas d'Exception !
except IndexError: except IndexError:
print("On ne doit pas générer d'exception IndexError !") print("On ne doit pas générer d'exception IndexError !")
``` ```
%% Cell type:markdown id:9ebbd490-abcd-45e4-8296-c311e34ddf2d tags: %% Cell type:markdown id:9ebbd490-abcd-45e4-8296-c311e34ddf2d tags:
**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). **Question 2.4** - Enfin, transformez la File (classe `Queue`) pour en faire une file de Priorité nommée `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).
Conseil Conseils :
- garder la liste des valeurs internes constamment triée lors de l'ajout; - garderzla liste des valeurs internes constamment triée lors de l'ajout de nouvelles valeurs;
- 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)` - 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)`
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. 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 le comportement de votre code.
%% Cell type:code id:fa163d01-ed34-447e-8b55-6198140c349b tags: %% Cell type:code id:fa163d01-ed34-447e-8b55-6198140c349b tags:
``` python ``` python
from queue import PriorityQueue from queue import PriorityQueue
import random import random
filep = PriorityQueue() filep = PriorityQueue()
list_random = [random.randint(1, 10) for _ in range(5)] # Liste aléatoire list_random = [random.randint(1, 10) for _ in range(5)] # Liste aléatoire
for i in list_random: filep.put(i) for i in list_random: filep.put(i)
while not filep.empty(): while not filep.empty():
print(filep.get(), end=" ") print(filep.get(), end=" ")
``` ```
%% Cell type:markdown id:738fe9f0-7885-40b6-a8cf-eb0c7cd8f3aa tags: %% Cell type:markdown id:738fe9f0-7885-40b6-a8cf-eb0c7cd8f3aa tags:
Remplir le code ci-dessous basé sur la file afin d'en faire une file de priorité. Remplir le code ci-dessous basé sur la File afin d'en faire une File de Priorité.
%% Cell type:code id:3b0a1122-5d8f-4f5f-8ef4-460f9896f4b6 tags: %% Cell type:code id:3b0a1122-5d8f-4f5f-8ef4-460f9896f4b6 tags:
``` python ``` python
class FilePriorite(): class FilePriorite():
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:4e608d6b-458b-4836-8ab2-4371d96ae54b tags: %% Cell type:code id:4e608d6b-458b-4836-8ab2-4371d96ae54b tags:
``` python ``` python
filep = PriorityQueue() filep = PriorityQueue()
f = FilePriorite() f = FilePriorite()
note_list = [student["note"] for student in students_list] note_list = [student["note"] for student in students_list]
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
while not f.is_empty() and not filep.empty(): while not f.is_empty() and not filep.empty():
assert f.pop() == filep.get() assert f.pop() == filep.get()
``` ```
%% Cell type:markdown id:014b7c3e-cfaf-4e80-9aa8-aff4e2ca821d tags:
La complexité de la structure de données de file de priorité est $O(n)$ en ajout et de $O(1)$ en lecture.
%% Cell type:markdown id:77038e6d-0b73-434c-910d-c7da733b643a tags: %% Cell type:markdown id:77038e6d-0b73-434c-910d-c7da733b643a tags:
## Exercice 3 : Arbre binaire complet sous forme de liste ## Exercice 3 : Arbre binaire complet sous forme de liste
%% Cell type:markdown id:5f3fc128-2a2e-42bd-924a-d691c09150d6 tags: %% Cell type:markdown id:5f3fc128-2a2e-42bd-924a-d691c09150d6 tags:
Nous allons maintenant implémenter **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 : Nous allons maintenant implémenter un **arbre binaire complet**. Cet arbre sera utile pour l'exercice suivant et la création d'une nouvelle structure de données : le 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 :
- La racine a la position $i = 0$ (cette valeur sera renvoyée par la fonction `get_racine`) - La racine a la position $i = 0$ (cette valeur sera renvoyée par la fonction `get_racine`)
- Le parent a la position $\lfloor (i - 1)/ 2 \rfloor$ (fonction `get_parent`) - Le parent a la position $\lfloor (i - 1)/ 2 \rfloor$ (fonction `get_parent`)
- Le fils gauche a la position $2 \times i + 1$ (fonction `get_fils_gauche`) - Le fils gauche a la position $2 \times i + 1$ (fonction `get_fils_gauche`)
- Le fils droit a la position $2 \times i + 2$ (fonction `get_fils_droit`) - Le fils droit a la position $2 \times i + 2$ (fonction `get_fils_droit`)
``` ```
1 1
/ \ / \
2 5 2 5
/ \ / / \ /
3 4 6 3 4 6
La liste correspondante : La liste correspondante :
[1, 2, 5, 3, 4, 6] [1, 2, 5, 3, 4, 6]
``` ```
%% Cell type:markdown id:ea502a0e-faa7-4bb2-9627-35cd128521a4 tags: %% Cell type:markdown id:ea502a0e-faa7-4bb2-9627-35cd128521a4 tags:
**Exercice 3.1** - Implémentez un arbre binaire sous forme de classe appellée `BinaryTree` (basée sur la file de priorité) avec les fonctions ci-dessus (`get_racine`, `get_parent`, `get_fils_gauche`, `get_fils_droit`). Vous rajouterez une méthode `taille`qui renvoie la taille de l'arbre binaire (longueur de la liste interne). **Exercice 3.1** - Implémentez un arbre binaire sous forme de classe appellée `BinaryTree` (vous pouvez vous inspirer de la classe de File de Priorité). 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 id:d6648668-c0e3-47d5-bd83-a49785939877 tags: %% Cell type:code id:d6648668-c0e3-47d5-bd83-a49785939877 tags:
``` python ``` python
class BinaryTree(): class BinaryTree():
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:d0dd4680-92db-4dad-8647-951269330870 tags: %% Cell type:markdown id:d0dd4680-92db-4dad-8647-951269330870 tags:
**Exercice 3.2** - Assurez vous que tous les tests ci-dessous sont validés. **Exercice 3.2** - Assurez vous que tous les tests ci-dessous sont validés.
%% Cell type:code id:ac65be98-9649-4753-8ead-c0deb89f5f0c tags: %% Cell type:code id:ac65be98-9649-4753-8ead-c0deb89f5f0c tags:
``` python ``` python
# test arbre vide # test arbre vide
tree_empty = BinaryTree() tree_empty = BinaryTree()
assert tree_empty.taille() == 0 assert tree_empty.taille() == 0
assert tree_empty.get_racine() == None assert tree_empty.get_racine() == None
assert tree_empty.get_fils_gauche()[0] == None assert tree_empty.get_parent()[0] == None
assert tree_empty.get_fils_droit()[0] == None assert tree_empty.get_fils_gauche(0)[0] == None
assert tree_empty.get_fils_droit(0)[0] == None
``` ```
%% Cell type:code id:f548b21a-b2f5-4de5-9261-38d23035e25d tags: %% Cell type:code id:f548b21a-b2f5-4de5-9261-38d23035e25d tags:
``` python ``` python
L = [1, 2, 5, 3, 4, 6] L = [1, 2, 5, 3, 4, 6]
tree_values = BinaryTree(L) tree_values = BinaryTree(L)
assert tree_values.taille() == len(L) # 6 assert tree_values.taille() == len(L) # 6
assert tree_values.get_racine() == L[0] # 3 assert tree_values.get_racine() == L[0] # 3
assert tree_values.get_fils_gauche()[0] == L[2*0+1] # 2 assert tree_values.get_fils_gauche(0)[0] == L[2*0+1] # 2
assert tree_values.get_fils_droit()[0] == L[2*0+2] # 5 assert tree_values.get_fils_droit(0)[0] == L[2*0+2] # 5
assert tree_values.get_parent(1)[0] == L[0] # 5
``` ```
%% Cell type:markdown id:25213313-5058-415a-a3bc-7948c3d98588 tags: %% Cell type:markdown id:25213313-5058-415a-a3bc-7948c3d98588 tags:
Cette structure de donnée sera utile pour la question suivante afin de créer un `Tas`. 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 tags: %% Cell type:markdown id:5f35e9de-025f-4c89-a004-a24af997474a tags:
**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 atisfait 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 : **Exercice 3.3** - Ecrivez une fonction de parcours de l'arbre qui renvoie `True` si un noeud est inférieur à ses enfants, sinon `False`. Conseil :
1. Proposez une approche récursive 1. Proposez une approche récursive
2. Le cas d'arrêt est un noeud vide 2. Le cas d'arrêt est un noeud vide
3. L'appel récursif est déclanché en fonction de la valeur du noeud en cours et celle de ses enfants 3. L'appel récursif est déclanché en fonction de la valeur du noeud en cours et celle de ses enfants
4. Au final renvoyer `True` si aucune condition ne renvoie `False`
%% Cell type:code id:11065fbe-7209-4e87-8b9c-04d19d14fcee tags: %% Cell type:code id:93924cc3-9d92-40c7-b7d7-96a775e121b1 tags:
``` python ``` python
def check_min_tree(T, i=0): def check_min_tree(T, i=0):
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:da01b2ab-4633-443f-97ea-b1022bc714ef tags: %% Cell type:code id:da01b2ab-4633-443f-97ea-b1022bc714ef tags:
``` python ``` python
assert check_min_tree(tree_values) == True check_min_tree(tree_values)
```
%% Cell type:code id:a28bf560-5fed-40cd-b8bf-0389e11fd8bd tags:
``` python
assert check_min_tree(BinaryTree([1])) == True
assert check_min_tree(BinaryTree([1, 2])) == True
assert check_min_tree(BinaryTree([1, 2, 3])) == True
assert check_min_tree(BinaryTree([1, 2, 3, 4])) == True
assert check_min_tree(BinaryTree([5, 6, 4])) == False
``` ```
%% Cell type:markdown id:d9f0166d-d12f-42a1-989a-b7014117e73d tags: %% Cell type:markdown id:d9f0166d-d12f-42a1-989a-b7014117e73d tags:
## Exercice 4 (Bonus) : Création d'un tas et tri par tas ## Exercice 4 (Bonus) : Création d'un tas et tri par tas
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. 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. Pour rappel la file créée précédemment avant une complexité de $O(n)$ en ajout et de $O(1)$ en lecture.
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. 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. Cette structure de données sera plus efficace en $O(log(n)$ pour l'ajout d'une nouvelle valeur (mais aussi pour le retrait).
%% Cell type:markdown id:6061d866-51fb-48bc-9f1a-46b1b57a0ec0 tags: %% Cell type:markdown id:6061d866-51fb-48bc-9f1a-46b1b57a0ec0 tags:
**Question 4.1 -** Implémentez une structure de `Tas`comme suit : **Question 4.1 -** Implémentez une structure de `Tas`comme suit :
- Créez une structure de données de `Tas` similaire au `BinaryTree` 1. Créez une structure de données de `Tas` similaire au `BinaryTree`
- Créez une méthode `inserer` (que l'on utilisera à la place d'`ajoute`) dont le principe est le suivant : 2. Créez une méthode `inserer`, que l'on utilisera à la place d'`ajoute`, dont le principe est le suivant :
- Chaque nouveau noeud est rajouté comme dernier élément du tableau (à la fin donc) - Chaque nouveau noeud est rajouté comme dernier élément du tableau (à la fin donc)
- Comparez ce noeud à son parent et si il est plus grand que ce parent inversez-le - Comparez ce noeud à son parent et si il est plus grand que ce parent inversez-le
- Répétez tant que la condition ci-dessus est vraie et que la racine n'est pas atteinte - Répétez tant que la condition ci-dessus est vraie et que la racine n'est pas atteinte
3. Créez une méthode `enlever` dont le principe est le suivant :
- Créez une méthode `enlever` dont le principe est le suivant :
- Enlever l'élément racine de l'arbre (premier élément du tableau) - Enlever l'élément racine de l'arbre (premier élément du tableau)
- Déplacer le dernier noeud de l'arbre (dernier élément du tableau) à la place de la racine de l'arbre - Déplacer le dernier noeud de l'arbre (dernier élément du tableau) à la place de la racine de l'arbre
- 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. - 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.
- Créez une méthode `descendre` qui : 4. Créez une méthode `descendre` qui :
- Prend le plus petit des enfants - Prend le plus petit des enfants
- Echange sa place avec lui si il est plus petit - Echange sa place avec lui si il est plus petit
- Répéte cela tant qu'il existe des enfants - Répéte cela tant qu'il existe des enfants
Attention : pensez à tester si il existe un fils droit et un fils gauche lors des opération de descente lors de l'insertion. 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 id:301e69f3-4035-4b10-9c85-f4427d92e03b tags: %% Cell type:code id:301e69f3-4035-4b10-9c85-f4427d92e03b tags:
``` python ``` python
class Tas(): class Tas():
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:0b467f58-c800-4ae3-b066-8717732a1095 tags: %% Cell type:markdown id:0b467f58-c800-4ae3-b066-8717732a1095 tags:
Votre tas doit valider les tests suivants : Votre tas doit valider les tests suivants :
%% Cell type:code id:3c41d4a7-5971-497b-b484-4a42a2480b06 tags: %% Cell type:code id:3c41d4a7-5971-497b-b484-4a42a2480b06 tags:
``` python ``` python
# test tas vide # test tas vide
tas_vide = Tas() tas_vide = Tas()
assert tas_vide.taille() == 0 assert tas_vide.taille() == 0
assert tas_vide.get_racine() == None assert tas_vide.get_racine() == None
assert tas_vide.get_fils_droit()[0] == None assert tas_vide.get_fils_droit()[0] == None
assert tas_vide.get_fils_droit()[0] == None assert tas_vide.get_fils_droit()[0] == None
# test tas simple # test tas simple
tas_simple = Tas() tas_simple = Tas()
tas_simple.inserer(1) tas_simple.inserer(1)
tas_simple.inserer(2) tas_simple.inserer(2)
assert tas_simple.taille() == 2 assert tas_simple.taille() == 2
# test tas un peu plus complexe # test tas un peu plus complexe
tas = Tas() tas = Tas()
liste = [1, 4, 10000, 2, 29, .2, 13, .5, 14, .1, 100] liste = [1, 4, 10000, 2, 29, .2, 13, .5, 14, .1, 100]
liste_triee = sorted(liste) liste_triee = sorted(liste)
for l in liste: for l in liste:
tas.inserer(l) tas.inserer(l)
assert tas.taille() == len(liste) assert tas.taille() == len(liste)
assert tas.get_racine() == liste_triee[0] assert tas.get_racine() == liste_triee[0]
assert tas.get_fils_gauche(0) == (liste_triee[1], 1) assert tas.get_fils_gauche(0) == (liste_triee[1], 1)
assert tas.get_fils_droit(0) == (liste_triee[2], 2) assert tas.get_fils_droit(0) == (liste_triee[2], 2)
while not tas.est_vide(): while not tas.est_vide():
assert tas.enlever(0) == liste_triee.pop(0) assert tas.enlever(0) == liste_triee.pop(0)
assert tas.taille() == len(liste_triee) assert tas.taille() == len(liste_triee)
``` ```
%% Cell type:markdown id:ec678229-485e-4f3b-8c24-56febe7bd69e tags: %% Cell type:markdown id:ec678229-485e-4f3b-8c24-56febe7bd69e tags:
**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. **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 id:1d483588-4153-4614-b969-854846b08f4e tags: %% Cell type:code id:1d483588-4153-4614-b969-854846b08f4e tags:
``` python ``` python
def triTas(l: list = []) -> list: def triTas(l: list = []) -> list:
t = Tas() t = Tas()
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:e7ab0b07-f13c-4b95-b5fa-c38aea9b6ead tags: %% Cell type:markdown id:e7ab0b07-f13c-4b95-b5fa-c38aea9b6ead tags:
Comparez à la méthode de tri `sorted`. Comparez à la méthode de tri `sorted`.
%% Cell type:code id:4e9fd04a-910a-4d32-b0dc-0a1502599901 tags: %% Cell type:code id:4e9fd04a-910a-4d32-b0dc-0a1502599901 tags:
``` python ``` python
liste = [54,26,93,17,77,31,44,55,20] liste = [54,26,93,17,77,31,44,55,20]
l2 = triTas(liste.copy()) l2 = triTas(liste.copy())
assert(l2 == sorted(liste)) assert(l2 == sorted(liste))
assert([] == triTas([])) assert([] == triTas([]))
assert([1] == triTas([1])) assert([1] == triTas([1]))
assert([1, 1] == triTas([1, 1])) assert([1, 1] == triTas([1, 1]))
``` ```
%% Cell type:markdown id:49bae650-a23a-41f7-8896-8ac502cd71e8 tags: %% Cell type:markdown id:49bae650-a23a-41f7-8896-8ac502cd71e8 tags:
Pour information, le module `heapq` contient l'implémentation d'un tas en Python et s'utilise comme les Piles, Files, etc. 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 id:5bcc3ebf-749e-4a10-8b90-37cfd2488171 tags: %% Cell type:code id:5bcc3ebf-749e-4a10-8b90-37cfd2488171 tags:
``` python ``` python
import heapq import heapq
tas = [] tas = []
for i in range(5): heapq.heappush(tas, i) for i in range(5): heapq.heappush(tas, i)
while not len(tas) == 0: while not len(tas) == 0:
print(heapq.heappop(tas), end=" ") print(heapq.heappop(tas), end=" ")
# 0 1 2 3 4 # 0 1 2 3 4
``` ```
%% Cell type:markdown id:7f774fa4-c586-4d5c-9cf3-5f9bb899f725 tags: %% Cell type:markdown id:7f774fa4-c586-4d5c-9cf3-5f9bb899f725 tags:
**Question 4.2 -** Comparez la performance (en temps) des méthodes de tri que vous avez implémenté dans les questions précentes. **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 id:56e72b68-1d5c-4cea-8e9a-6d9f5f9ae34d tags: %% Cell type:code id:56e72b68-1d5c-4cea-8e9a-6d9f5f9ae34d tags:
``` python ``` python
import time import time
import random import random
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
nvalues = [100, 500, 1500, 2000, 2500, 3000] nvalues = [100, 500, 1500, 2000, 2500, 3000]
timesSorted = [] timesSorted = []
timesSort = [] timesSort = []
timesSelection = [] timesSelection = []
timesHeap = [] timesHeap = []
for i in nvalues: for i in nvalues:
random.seed() random.seed()
p = 12**2 p = 12**2
liste = [] liste = []
for x in range(i): liste.append(random.randint(0, p)) for x in range(i): liste.append(random.randint(0, p))
# tri sorted # tri sorted
c = liste.copy() c = liste.copy()
a=time.perf_counter() a=time.perf_counter()
triSorted = sorted(c) triSorted = sorted(c)
b=time.perf_counter() b=time.perf_counter()
timesSorted.append(b-a) timesSorted.append(b-a)
# tri .sort() # tri .sort()
c = liste.copy() c = liste.copy()
a=time.perf_counter() a=time.perf_counter()
triSort = c triSort = c
triSort.sort() triSort.sort()
b=time.perf_counter() b=time.perf_counter()
timesSort.append(b-a) timesSort.append(b-a)
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
plt.plot(nvalues, timesSorted, "g-", label="Tri Sorted") plt.plot(nvalues, timesSorted, "g-", label="Tri Sorted")
plt.plot(nvalues, timesSort, "b-", label="Tri .sort()") plt.plot(nvalues, timesSort, "b-", label="Tri .sort()")
plt.plot(nvalues, timesSelection, "r-", label="Tri Selection") plt.plot(nvalues, timesSelection, "r-", label="Tri Selection")
plt.plot(nvalues, timesHeap, "r-", label="Tri Heap") plt.plot(nvalues, timesHeap, "r-", label="Tri Heap")
plt.xlabel("Taille du jeu de données") plt.xlabel("Taille du jeu de données")
plt.ylabel("Temps") plt.ylabel("Temps")
plt.legend(loc="upper left") plt.legend(loc="upper left")
plt.title("Comparaison des performances des algorithmes de tri") plt.title("Comparaison des performances des algorithmes de tri")
plt.show() plt.show()
``` ```
%% Cell type:markdown id:04456b1e-92fd-4433-b230-8ea4374595a0 tags: %% Cell type:markdown id:04456b1e-92fd-4433-b230-8ea4374595a0 tags:
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é. 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 tags: %% Cell type:markdown id:3e925273-2161-4c10-8e79-dabcc9bb38cb tags:
## Pour aller plus loin ## Pour aller plus loin
- 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.) - 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.)
- Utilisez un [grand jeu de donnée](https://generatedata.com/) d'étudiants pour les premières questions.
%% Cell type:code id:50f77357-df56-486b-a3c0-7d2d950c766e tags:
``` python
def popx(self):
if not self.is_empty():
return self.items.pop(0)
else:
raise IndexError("Le tas est vide")
Stack.popx = popx
s = Stack()
try:
s.popx()
except IndexError as e:
print(e)
```
%% Cell type:markdown id:3bf64f58-fbcd-44e8-b827-378e85968285 tags:
- Utilisez un [grand jeu de donnée](https://generatedata.com/) d'étudiants pour les premières questions. Réaliser une analyse de complexité avec ce jeu de données.
%% Cell type:code id:ca5f3173-89da-4103-ae04-b60f8b4c8065 tags:
``` python
```
......
nom;prenom;filiere;note;absences nom;prenom;filiere;note;absences
Dupond;Pierre;MP;19;7 Dupond;Pierre;MP;19;7
Dupond;Jeanne;MP;19;5 Dupont;Jeanne;MP;19;5
Clavier;Christian;PSI;14;1 Clavier;Christian;PSI;14;1
Gilles;Eric;PC;16;3 Gilles;Eric;PC;16;3
Arthaud;Nathalie;MP;15;0 Arthaud;Nathalie;MP;15;0
\ No newline at end of file
%% Cell type:markdown id:c71aca63 tags: %% Cell type:markdown id:39a8f123 tags:
NAME: NAME:
%% Cell type:markdown id:e1098061-ab50-4ba8-aa59-0b76ec1049a2 tags: %% Cell type:markdown id:e1098061-ab50-4ba8-aa59-0b76ec1049a2 tags:
# INF TC1 - TD3 (2h) - Arbres binaires # INF TC1 - TD3 (2h) - Arbres binaires
%% Cell type:markdown id:fc6c7558-96c4-435b-a841-e38c5d14bc9f tags: %% Cell type:markdown id:fc6c7558-96c4-435b-a841-e38c5d14bc9f tags:
--- ---
%% Cell type:markdown id:b0997389-5a87-4e8d-9600-29ed76e01759 tags: %% Cell type:markdown id:b0997389-5a87-4e8d-9600-29ed76e01759 tags:
<details style="border: 1px"> <details style="border: 1px">
<summary> RAPPELS SUR L'UTILISATION DES NOTEBOOKS</summary> <summary> RAPPELS SUR L'UTILISATION DES NOTEBOOKS</summary>
### Comment utiliser ces notebooks ? ### Comment utiliser ces notebooks ?
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 : 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 :
1) Répondez aux questions dans les cellules en dessous des questions. 1) Répondez aux questions dans les cellules en dessous des questions.
2) Votre code devra remplacer le texte suivant : 2) Votre code devra remplacer le texte suivant :
```python ```python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
(vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus). (vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus).
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. 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.
4) Vous pouvez créer de nouvelles cellules comme bon vous semble. 4) Vous pouvez créer de nouvelles cellules comme bon vous semble.
**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. **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.
</details> </details>
%% Cell type:code id:818565ab-9088-4729-94f9-601ca50de254 tags:
``` python
import graphviz
from graphviz import Digraph
from IPython.display import display
def visualize_oop(root):
def build(node, dot=None):
if dot is None:
dot = graphviz.Digraph(format='png')
if node is not None:
dot.node(str(node.value))
if node.left is not None:
dot.edge(str(node.value), str(node.left.value))
build(node.left, dot)
if node.right is not None:
dot.edge(str(node.value), str(node.right.value))
build(node.right, dot)
return dot
return build(root)
```
%% Cell type:markdown id:de645c99-52cf-4cff-9c6b-b156101ad47c tags: %% Cell type:markdown id:de645c99-52cf-4cff-9c6b-b156101ad47c tags:
## Objectif du TD ## Objectif du TD
Ce TD vous fera manipuler les arbres binaires, qui sont une structure de donnée efficace afin de trier des listes mais aussi de réaliser des opérations plus avancées grace à des méthodes de parcours en largeur et en profondeur. Ce TD vous fera manipuler les Arbres Binaires, qui sont une structure de données permettant de stocker des informations de manière hierarchique, et de les récupérer grace à des méthodes de parcours.
%% Cell type:markdown id:abde77ea-e21d-434e-b72e-a62ac464c793 tags: %% Cell type:markdown id:abde77ea-e21d-434e-b72e-a62ac464c793 tags:
## Exercice 1 : Introduction aux arbres binaires ## Exercice 1 : Introduction aux Arbres Binaires
Dans ce exercice nous allons créer et parcourir un arbre binaire. Un arbre binaire est un arbre qui a les propriétés suivantes : Dans ce exercice vous allez créer et parcourir un Arbre Binaire. Un Arbre Binaire est un Arbre qui a les propriétés suivantes :
- il comporte des noeuds ayant au _maximum deux enfants_ - il est dit _binaire_ car il comporte des noeuds ayant au _maximum_ deux enfants
- il est _complet_ si tous les noeuds de tous les niveaux ont deux enfants - il est dit _complet_ si tous les niveaux de l'arbre sont remplis (sauf éventuellement le dernier), et dans lequel les feuilles sont alignées à gauche
- il est _équilibré_ si l'arbre est complet sauf pour le dernier niveau. - il est dit _équilibré_ si la différence de hauteur entre les sous-arbres gauche et droit de chaque nœud est au plus 1
Voici un exemple d'arbre binaire : Voici un exemple d'Arbre Binaire :
``` ```
1 1
/ \ / \
2 3 2 3
``` ```
Dans cet exercice nous stockeront l'arbre avec une structure de donnée _explicite_ en POO (comme ci-dessous) qui contient des valeurs entières (et uniques, à savoir deux noeuds n'auront pas la même valeur `value`) dans chaque noeud : Dans cet exercice nous stockeront l'Arbre Binaire avec une structure de donnée _explicite_ (en POO comme ci-dessous, et non plus dans un tableau comme pour le tas dans le TD 2) qui contient des valeurs entières (et uniques, à savoir deux noeuds n'auront pas la même valeur `value`) dans chaque noeud :
%% Cell type:code id:9318f291-f289-4eb6-a7f7-94cbc4e3f459 tags: %% Cell type:code id:9318f291-f289-4eb6-a7f7-94cbc4e3f459 tags:
``` python ``` python
class Node: class Node:
def __init__(self, value : int, left = None, right = None): def __init__(self, value : int, left = None, right = None):
self.value = value self.value = value
self.left = left self.left = left
self.right = right self.right = right
``` ```
%% Cell type:markdown id:31993aa6-7361-4a58-856d-32f3a3fcec36 tags: %% Cell type:markdown id:31993aa6-7361-4a58-856d-32f3a3fcec36 tags:
**Question 1.1** - Utilisez la structure de donnée `Node` ci-dessus afin d'implémenter l'arbre donné en introduction. Votre arbre sera stocké dans la variable `root`. Vous pouvez rajouter des noeuds supplémentaires à cet arbre (mais il doit rester binaire). **Question 1.1** - Utilisez la structure de donnée `Node` ci-dessus afin d'implémenter l'arbre donné en introduction. Votre arbre sera stocké dans la variable `root`. Vous pouvez rajouter des noeuds supplémentaires à cet arbre (mais il doit rester binaire).
%% Cell type:code id:48a9b647-260e-40a5-a3da-c11ed77a9e62 tags: %% Cell type:code id:48a9b647-260e-40a5-a3da-c11ed77a9e62 tags:
``` python ``` python
root = Node(1) # a modifier root = Node(1) # a modifier
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:0a4461c4-09b8-4f9f-ad70-6b953dc4b7e6 tags: %% Cell type:markdown id:0a4461c4-09b8-4f9f-ad70-6b953dc4b7e6 tags:
Les tests suivant doivent être validés (vous pouvez rajouter d'autres tests) : Les tests suivant doivent être validés (rajoutez d'autres tests) :
%% Cell type:code id:c08d3050-ccba-4b51-8a01-1d2689455e96 tags: %% Cell type:code id:c08d3050-ccba-4b51-8a01-1d2689455e96 tags:
``` python ``` python
assert root.value == 1 assert root.value == 1
assert root.left.value == 2 assert root.left.value == 2
assert root.right.value == 3 assert root.right.value == 3
``` ```
%% Cell type:markdown id:2fc5d004-bed5-4e6f-adf9-649d8f1543fb tags:
Vous pouvez visualiser et comparer votre résultat avec la méthode `visualize_oop` comme ci-dessous:
%% Cell type:code id:1edd4902-6ce9-4ffa-90c9-2d5ce45052be tags:
``` python
visualize_oop(root)
```
%% Cell type:markdown id:24670502-ecd5-4d86-a32c-1d7d43eb04fe tags: %% Cell type:markdown id:24670502-ecd5-4d86-a32c-1d7d43eb04fe tags:
**Question 1.2.** Proposer une méthode `bfs` de _parcours en largeur_ de l'arbre afin d'afficher la valeur des noeuds dans l'ordre croissant. Pour cela vous utiliserez une structure de données de File (sous forme de liste, cela sera suffisant). Une méthode possible pour mener cela à bien consiste à : **Question 1.2.** Proposez une méthode `bfs` de _parcours en largeur_ de l'Arbre afin d'afficher la valeur des noeuds dans l'ordre croissant. Pour cela vous utiliserez une structure de données de File (sous forme de liste, cela sera suffisant). Une méthode possible pour mener cela à bien consiste à :
1. Intialiser la file avec la racine de l'arbre 1. intialiser la File avec la racine de l'Arbre;
2. Dé-filer une valeur et la stocker dans une liste de résultat 2. dé-filer une valeur et la stocker dans une liste de résultats;
3. En-filer ses enfants (si il y en a) dans la file 3. en-filer ses enfants (si il y en a) dans la File;
4. Répéter l'étape 2 jusqu'à ce que la file soit vide, renvoyer le résultat 4. répéter l'étape 2 jusqu'à ce que la File soit vide, renvoyer le résultat.
%% Cell type:code id:1f92e83b-ba51-4fa4-a127-2aa970a96a26 tags: %% Cell type:code id:1f92e83b-ba51-4fa4-a127-2aa970a96a26 tags:
``` python ``` python
def bfs(node: Node): def bfs(node: Node):
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:c1b198c7-8387-4552-a163-7a70a70c61a3 tags: %% Cell type:code id:c1b198c7-8387-4552-a163-7a70a70c61a3 tags:
``` python ``` python
assert bfs(root) == [1, 2, 3] assert bfs(root) == [1, 2, 3]
``` ```
%% Cell type:markdown id:675c8c46-ed69-4a2e-a9cf-57301112e3ab tags: %% Cell type:markdown id:675c8c46-ed69-4a2e-a9cf-57301112e3ab tags:
**Question 1.3** - Écrire une fonction `depth` de calcul de la _profondeur_ d'un noeud d'un arbre. La profondeur est défini comme la distance entre ce noeud et la racine (celle-ci aura une profondeur de `0`). Cette fonction prendra la racine de l'arbre `root`en paramètre, ainsi que le noeud dont on veut calculer la profondeur avec le paramètre `target_value`. **Question 1.3** - Écrivez une fonction `depth` de calcul de la _profondeur_ d'un noeud d'un Arbre. La profondeur est défini comme la distance entre ce noeud et la racine (celle-ci aura une profondeur de `0`). Autrement dit la profondeur correspond au nombre d'arêtes entre un noeud et la racine de l'arbre. Cette fonction prendra en paramètre la racine de l'arbre `root`, ainsi que le noeud dont on veut calculer la profondeur avec le paramètre `target_value`.
Conseils :
Conseil : s'inspirer de la fonction précédente en incluant la profondeur de chaque noeud parcouru lors de son ajout dans la file (autrement dit rajouter un tuple `(noeud, profondeur)` au lieu du noeud parcouru seulement. - s'inspirer de la fonction précédente de parcours en largeur en incluant la profondeur de chaque noeud parcouru lors de son ajout dans la file;
- autrement dit rajouter un tuple `(noeud, profondeur)` au lieu du simple noeud parcouru afin de mémoriser la profondeur des noeuds parcourus.
%% Cell type:code id:6c21f3f7-0f21-47ad-a4ce-a2af7b9743d4 tags: %% Cell type:code id:6c21f3f7-0f21-47ad-a4ce-a2af7b9743d4 tags:
``` python ``` python
def depth(root: Node, target_value: int) -> int: def depth(root: Node, target_value: int) -> int:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:37a27a50-68f4-486b-a0da-b0905b8db8dd tags: %% Cell type:code id:37a27a50-68f4-486b-a0da-b0905b8db8dd tags:
``` python ``` python
assert depth(root, 1) == 0 assert depth(root, 1) == 0
assert depth(root, 2) == 1 assert depth(root, 2) == 1
assert depth(root, 3) == 1 assert depth(root, 3) == 1
``` ```
%% Cell type:markdown id:f8abb2f4-24dc-4346-9081-4400df30e3af tags: %% Cell type:markdown id:f8abb2f4-24dc-4346-9081-4400df30e3af tags:
**Question 1.4** - Écrire une fonction `height` de calcul de la _hauteur_ d'un arbre définie comme la prodondeur maximale possible dans un arbe. Vous pouvez l'implémenter comme la profondeur maximale des noeuds de l'arbre, ou bien de manière récursive. **Question 1.4** - Écrivez une fonction `height` de calcul de la _hauteur_ d'un Arbre définie comme la prodondeur maximale possible dans un Arbe. Vous pouvez l'implémenter comme la profondeur maximale des noeuds de l'Arbre, ou bien de manière récursive.
%% Cell type:code id:4944b0bb-5f59-41c2-b5d9-052a0d5386dd tags: %% Cell type:code id:4944b0bb-5f59-41c2-b5d9-052a0d5386dd tags:
``` python ``` python
def height(root: Node) -> int: def height(root: Node) -> int:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:2d11935c-2212-4ec3-ba57-5303ec05b274 tags: %% Cell type:code id:2d11935c-2212-4ec3-ba57-5303ec05b274 tags:
``` python ``` python
assert height(root) == 2 assert height(root) == 1
``` ```
%% Cell type:markdown id:29fca7e0-aaf9-495b-822d-f20e1a672092 tags: %% Cell type:markdown id:29fca7e0-aaf9-495b-822d-f20e1a672092 tags:
**Question 1.5** - Valider si l'arbre est bien équilibré, autrement dit si il n'y a pas de différence de profondeur suppérieur à 1 entre les feuilles d'un arbre. **Question 1.5** - Ecrivez une fonction de validation afin de déterminer si un Arbre est bien équilibré, autrement dit si il n'y a pas de différence de profondeur suppérieur à 1 entre les feuilles d'un arbre. Conseil : utilisez une approche récursive.
%% Cell type:code id:3b4f56e8-e4bf-4d27-97eb-b24995d741fe tags: %% Cell type:code id:3b4f56e8-e4bf-4d27-97eb-b24995d741fe tags:
``` python ``` python
def est_equlibre(root: Node) -> bool: def est_equlibre(root: Node) -> bool:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:69b03c9b-2092-4343-b9e4-7d9904a445b0 tags: %% Cell type:code id:69b03c9b-2092-4343-b9e4-7d9904a445b0 tags:
``` python ``` python
assert est_equlibre(root) assert est_equlibre(root)
``` ```
%% Cell type:markdown id:9e29fddc-c249-4b2a-a9d3-26e56f5474ab tags: %% Cell type:markdown id:9e29fddc-c249-4b2a-a9d3-26e56f5474ab tags:
## Exercice 2 : Arbres syntaxiques ## Exercice 2 : Arbres syntaxiques
Un arbre _syntaxique_ permet le stockage d'une expression structurée, par exemple une équation. Dans cet exercice nous allons modéliser un tel arbre sous forme d'arbre binaire (exemple ci-dessous à gauche) afin de réaliser un calcul arithmétique simple à partir de l'expression fournie de manière textuelle : Un Arbre _syntaxique_ permet le stockage d'une expression structurée, par exemple une équation. Dans cet exercice nous allons modéliser un tel arbre sous forme d'arbre binaire (exemple ci-dessous à gauche) afin de réaliser un calcul arithmétique simple à partir de l'expression fournie de manière textuelle :
Expression : `(3-2) * (7+(10/2)` Expression : `(3-2) * (7+(10/2))`
Résultat : `12.0` Résultat : `12.0`
Nous ferons l'hypothèse que les opérations sont limitées à `+ - / *`, seront toujours binaires et porteront sur des valeurs numériques entières (mais le résultat peut ne pas être un entier). Nous ferons l'hypothèse que les opérations sont limitées à `+ - / *`, seront toujours binaires et porteront sur des valeurs numériques entières (mais le résultat peut ne pas être un entier).
``` ```
* *
/ \ / \
- + - +
/ \ / \ / \ / \
3 2 7 / 3 2 7 /
/ \ / \
10 2 10 2
``` ```
Vous utiliserez la structure d'arbre binaire ci-dessous afin de le stocker : Vous utiliserez la structure d'arbre binaire ci-dessous afin de stocker l'arbre comme pour l'abre binaire précédent :
%% Cell type:code id:e78ec913-cd5f-4304-8c8a-438758d909f4 tags: %% Cell type:code id:e78ec913-cd5f-4304-8c8a-438758d909f4 tags:
``` python ``` python
class Noeud: class Noeud:
def __init__(self, v = None, g = None, d = None) -> None: def __init__(self, v = None, g = None, d = None) -> None:
self.valeur = v self.valeur = v
self.gauche = g self.gauche = g
self.droit = d self.droit = d
``` ```
%% Cell type:markdown id:88fbc62f-ae5a-4765-bf8f-bb127f5d5a13 tags: %% Cell type:markdown id:88fbc62f-ae5a-4765-bf8f-bb127f5d5a13 tags:
**Question 2.1** - Utilisez la structure de données d'arbre ci-dessus afin de stocker l'arbre syntaxique donné en exemple. **Question 2.1** - Stockez l'Arbre syntaxique donné en exemple graphique en utilisant la structure de données d'arbre binaire ci-dessus (il s'agit d'écrire une instance d'arbre dans la variable `arbre`, et non une fonction).
%% Cell type:code id:4661ca46-489c-4330-9e72-64fe25e4b49c tags: %% Cell type:code id:4661ca46-489c-4330-9e72-64fe25e4b49c tags:
``` python ``` python
arbre = None # à changer arbre = None # à changer
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:cee8c8d3-f4ab-4dcc-931e-f0b8681b225c tags: %% Cell type:code id:cee8c8d3-f4ab-4dcc-931e-f0b8681b225c tags:
``` python ``` python
assert arbre.valeur == "*" assert arbre.valeur == "*"
assert arbre.gauche.valeur == "-" assert arbre.gauche.valeur == "-"
assert arbre.droit.valeur == "+" assert arbre.droit.valeur == "+"
``` ```
%% Cell type:markdown id:0be8aab9-1aba-4340-9235-eb3d1cc7fd29 tags: %% Cell type:markdown id:0be8aab9-1aba-4340-9235-eb3d1cc7fd29 tags:
**Question 2.2** - Implémenter désormais une méthode d'évaluation (autrement dit de calcul) automatique d'un arbre syntaxique tel que vous l'avez stocké dans la variable `arbre` ci-dessus. **Question 2.2** - Implémentez désormais une méthode d'évaluation (autrement dit de calcul) automatique d'un arbre syntaxique tel que vous l'avez stocké dans la variable `arbre` ci-dessus.
Conseil : proposer une solution récursive avec un cas d'arrêt et des appels récursifs comme suit : Conseil : proposez une solution récursive avec un cas d'arrêt et des appels récursifs comme suit :
- Si la valeur du noeud en cours est un opérateur, l'appliquer sur les deux sous-branches - si la valeur du noeud en cours est un opérateur, l'appliquer sur les deux sous-branches;
- Si c'est une valeur numérique, renvoyer cette valeur (cas d'arrêt car c'est une feuille de l'arbre) - si il s'agit d'une valeur numérique, renvoyez cette valeur (cas d'arrêt car c'est une feuille de l'arbre).
%% Cell type:code id:dc899d17-67fd-4cc9-bf92-f5f32e08f89e tags: %% Cell type:code id:dc899d17-67fd-4cc9-bf92-f5f32e08f89e tags:
``` python ``` python
def eval(r: Noeud) -> float: def eval(r: Noeud) -> float:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:00eb3e33-64b1-486f-97c8-d87de80f3a1a tags: %% Cell type:code id:00eb3e33-64b1-486f-97c8-d87de80f3a1a tags:
``` python ``` python
eval(arbre) eval(arbre)
``` ```
%% Cell type:markdown id:de5c394d-df35-4d7b-b057-164620b0b38d tags: %% Cell type:markdown id:de5c394d-df35-4d7b-b057-164620b0b38d tags:
**Question 2.3** - Écrire une méthode permettant de construire l'arbre à partir d'une expression fournie sous forme de chaîne de caractère en entrée comme `( ( 3 - 2 ) * ( 7 + ( 10 / 2 ) ) )"`. Les espaces sont nécessaires et vous permettront de faire un `.split(" ")}`. **Question 2.3** - Écrivez une méthode permettant de construire l'Arbre à partir d'une expression fournie sous forme de chaîne de caractère en entrée comme `( ( 3 - 2 ) * ( 7 + ( 10 / 2 ) ) )`. Les espaces sont nécessaires et vous permettront de faire un `.split(" ")`.
Conseil : Conseil :
- Parcourez caractère par caractère l'expression textuelle - parcourez caractère par caractère l'expression textuelle transformée en liste;
- Et utilisez une Pile permettant la bonne construction de l'arbre au fur et à mesure de son parcours - créez au fur et à mesure l'arbre initialisé par une racine et une variable stockage le noeud courant en cours de construction;
- identifiez les 4 cas possibles : début d'une expression, valeurs numériques, opérateur, fin d'une expression :
- si une expression commence, créez un nouveau fils gauche au noeud courant et stockez le noeud courant dans une Pile
- si une expression finie, considérez comme noeud courant l'élément que vous dé-Pilerez
- si un opérateur, stockez sa valeur dans le noeud courant, empilez ce noeud mais avant lui rajoute un fils droit qui devient le noeud courant
- si une valeur numérique, la stocker dans le noeud courant et considérez comme noeud courant l'élément que vous dé-Pilerez
%% Cell type:code id:1c33faaa-2289-498f-9ef1-5afeb4da4628 tags: %% Cell type:code id:1c33faaa-2289-498f-9ef1-5afeb4da4628 tags:
``` python ``` python
def construit_arbre(exp: str) -> int: def construit_arbre(exp: str) -> int:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:b56d0304-5d23-4e1c-91fb-09b511465ad5 tags: %% Cell type:code id:b56d0304-5d23-4e1c-91fb-09b511465ad5 tags:
``` python ``` python
r = construit_arbre("( ( 3 - 2 ) * ( 7 + ( 10 / 2 ) )") r = construit_arbre("( ( 3 - 2 ) * ( 7 + ( 10 / 2 ) )")
``` ```
%% Cell type:markdown id:89a9015c-dd4e-409d-91b2-9b64b55288e9 tags: %% Cell type:markdown id:89a9015c-dd4e-409d-91b2-9b64b55288e9 tags:
Enfin vérifiez que vous obtenez la bonne valeur en évaluant l'expression. Enfin vérifiez que vous obtenez la bonne valeur en évaluant l'expression.
%% Cell type:code id:e5c49c1f-75e0-4872-8578-40a532c7cea1 tags: %% Cell type:code id:e5c49c1f-75e0-4872-8578-40a532c7cea1 tags:
``` python ``` python
assert eval(r) == 12.0 assert eval(r) == 12.0
``` ```
%% Cell type:markdown id:012a5378-a2b7-498f-8115-e35f55c529ce tags: %% Cell type:markdown id:012a5378-a2b7-498f-8115-e35f55c529ce tags:
**Question 2.4 (Bonus) -** - Ecrire une fonction qui renvoie `True` ou `False` si l'arbre est bien un arbre syntaxique tel que défini en introduction. Autrement dit qu'il est binaire, et comporte des opérateurs partout sauf aux feuilles. **Question 2.4 (Bonus) -** Ecrire une fonction qui renvoie `True` ou `False` si l'Arbre est bien un arbre syntaxique tel que défini en introduction. Autrement dit qu'il est binaire, et comporte des opérateurs partout sauf aux feuilles.
Proposez une méthode itérative : Proposez d'abord une méthode itérative :
%% Cell type:code id:823d29e7-66c3-41a0-bdad-da6862a5e420 tags: %% Cell type:code id:823d29e7-66c3-41a0-bdad-da6862a5e420 tags:
``` python ``` python
def valide_ite(r: Noeud) -> bool: def valide_ite(r: Noeud) -> bool:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:06e73b57-959a-436f-bd7a-e12c615c805c tags: %% Cell type:code id:06e73b57-959a-436f-bd7a-e12c615c805c tags:
``` python ``` python
assert valide_ite(arbre) assert valide_ite(arbre)
``` ```
%% Cell type:markdown id:dfdf1d42-60bb-4a7e-9cd4-e6cb72b8130a tags: %% Cell type:markdown id:dfdf1d42-60bb-4a7e-9cd4-e6cb72b8130a tags:
Proposez une méthode récursive : Proposez ensuite une méthode récursive :
%% Cell type:code id:df0033e9-c076-4cde-ab6f-982a3677ce05 tags: %% Cell type:code id:df0033e9-c076-4cde-ab6f-982a3677ce05 tags:
``` python ``` python
def valide_rec(r: Noeud) -> bool: def valide_rec(r: Noeud) -> bool:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:9998c852-ec74-4225-9f01-4368899e303f tags: %% Cell type:code id:9998c852-ec74-4225-9f01-4368899e303f tags:
``` python ``` python
assert valide_rec(arbre) assert valide_rec(arbre)
``` ```
%% Cell type:markdown id:3503e6a5-2bc1-46d2-bef0-d1f1ceb6827e tags:
Montrez que les deux méthodes ont un comportement identique :
%% Cell type:code id:81e1b468-5d22-4a25-a2c3-17ab165627e5 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:81842c6c-1784-432b-8322-f3793d633e03 tags:
**Question 2.5 (Bonus) -** Créez une table de hachage afin de mémoriser les opérations déja réalisées pour l'Arbre syntaxique. Conseil : utilisez les informations sur le noeud comme clé (valeurs, opérateur).
%% Cell type:code id:fb91bda6-7076-44be-9014-ceb5507eb01c tags:
``` python
def eval_hash(r: Noeud, memo=None) -> float:
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:22979a11-3455-4525-b816-a84b2164291c tags:
``` python
assert eval_hash(arbre) == eval(arbre)
```
%% Cell type:markdown id:85c6a3e6-dea0-4e86-b7d9-b0c5735c3630 tags: %% Cell type:markdown id:85c6a3e6-dea0-4e86-b7d9-b0c5735c3630 tags:
## Pour aller plus loin ## Pour aller plus loin
- Rajouter des tests dans les exemples ci-dessus avec des arbres plus complexes, des cas particuliers, etc. - Rajouter des tests dans les exemples ci-dessus avec des arbres plus complexes, des cas particuliers, etc.
- Inclure des Exceptions dans votre code afin de gérer par exemple l'accès à un index de liste inexistant, etc. - Inclure des [Exceptions](https://docs.python.org/3/tutorial/errors.html) dans votre code afin de gérer par exemple l'accès à un index de liste inexistant, etc.
- Créer une table de hachage afin de mémoriser les opérations déja réalisées pour l'arbre syntaxique - Modulariser votre code afin de créer des fonctions auxiliaires (par exemple pour vérifier la valeur des noeuds de l'Arbre syntaxique).
%% Cell type:markdown id:08c48261 tags:
## Visualiser avec Graphviz
Graphviz est une bibliothèque de visualisation de graphe. Elle peut être très pratique afin de comprendre la structure de votre instance de graphe.
Cependant, la bibliothèque pose quelques soucis d'installation en Python, voici des étapes éventuellement à suivre.
Tout d'abord vérifiez que le module est bien installé :
%% Cell type:code id:c8c95b89 tags:
``` python
!pip install graphviz
```
%% Cell type:markdown id:5a9ebd95 tags:
Si oui (vous avez le message du type `Requirement already satisfied:`) testez l'import du module :
%% Cell type:code id:e897987f tags:
``` python
import graphviz
from graphviz import Digraph
```
%% Cell type:markdown id:2bf9b677 tags:
Si vous avez des erreurs, essayer l'installation du module à partir des exécutables
%% Cell type:code id:bd3cfb66 tags:
``` python
https://graphviz.org/
```
%% Cell type:markdown id:9373f3bc tags:
Un autre source d'erreur est l'installation à partir de la mauvaise version de Python qu'il faut utiliser avec pip.
%% Cell type:code id:504eff4a tags:
``` python
!python --version
```
%% Cell type:code id:5eb4ab77 tags:
``` python
from IPython.display import display
def visualize_oop(root):
def build(node, dot=None):
if dot is None:
dot = graphviz.Digraph(format='png')
if node is not None:
dot.node(str(node.value))
if node.left is not None:
dot.edge(str(node.value), str(node.left.value))
build(node.left, dot)
if node.right is not None:
dot.edge(str(node.value), str(node.right.value))
build(node.right, dot)
return dot
return build(root)
```
%% Cell type:code id:ded5b1fe tags:
``` python
visualize_oop(root)
```
......
%% Cell type:markdown id:54466d2f tags: %% Cell type:markdown id:f6c54511 tags:
NAME: NAME:
%% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags: %% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags:
# INF TC1 - TD4 (2h) - Images # INF TC1 - TD4 (2h) - Images
%% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags: %% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags:
--- ---
%% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags: %% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags:
<details style="border: 1px"> <details style="border: 1px">
<summary> RAPPELS SUR L'UTILISATION DES NOTEBOOKS</summary> <summary> RAPPELS SUR L'UTILISATION DES NOTEBOOKS</summary>
### Comment utiliser ces notebooks ? ### Comment utiliser ces notebooks ?
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 : 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 :
1) Répondez aux questions dans les cellules en dessous des questions. 1) Répondez aux questions dans les cellules en dessous des questions.
2) Votre code devra remplacer le texte suivant : 2) Votre code devra remplacer le texte suivant :
```python ```python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
(vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus). (vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus).
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. 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.
4) Vous pouvez créer de nouvelles cellules comme bon vous semble. 4) Vous pouvez créer de nouvelles cellules comme bon vous semble.
**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. **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.
</details> </details>
%% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags: %% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags:
## Objectif du TD ## Objectif du TD
Ce TD vous fera manipuler des images en Python, et réaliser un algorithme de remplissage basé sur le contenu. Nous verrons en particulier la structure de données matricielle et les méthodes de parcours associées. Enfin ce TD sera une préparation au TD 5 qui fera l'objet d'un rendu à réaliser à partir des concepts et du code abordés. Ce TD vous fera manipuler des images en Python, et réaliser un algorithme de remplissage basé sur leur contenu (par exemple leur couleur). Nous verrons en particulier la structure de données matricielle et les méthodes de parcours associées. Enfin ce TD sera une préparation au TD 5 qui fera l'objet d'un rendu à réaliser à partir des concepts abordés dans ce TD.
IMPORTANT : Dans le cadre de ce TD, nous n'autorisons pas l'utilisation des modules [OpenCV](https://docs.opencv.org/4.x/) ou [NumPy](https://numpy.org/) (ou toute fonction de [Pillow](https://pillow.readthedocs.io/en/stable/) sauf celles indiquées). IMPORTANT : Dans le cadre de ce TD 4 (mais aussi le prchain TD 5), nous n'autorisons pas l'utilisation de [OpenCV](https://docs.opencv.org/4.x/), [NumPy](https://numpy.org/) ou tout autre module permettant la manipulation et le traitement d'imag. Sauf le module [Pillow](https://pillow.readthedocs.io/en/stable/) mais dont seules quelques fonctions seront autorisées (cela sera indiqué dans les questions).
%% Cell type:markdown id:647cb3f9-cd85-4bf3-8a18-18262f58041c tags: %% Cell type:markdown id:647cb3f9-cd85-4bf3-8a18-18262f58041c tags:
## Exercice 1 : Charger une image et dessiner ## Exercice 1 : Charger une image et dessiner
Une image en informatique est stockée sous forme d'une matrice de pixels qui contiennent les couleurs. Le model classique de couleurs est dit "RGB" (Red, Green, Blue) [(doc)](https://fr.wikipedia.org/wiki/Rouge-vert-bleu) où chaque pixel contient une information colorimétrique encodée sous forme de triplets `(r, g, b)` (red, green, blue). Les valeurs de couleur peuvent varier de 0 à 255 pour chaque composante de couleur. Par exemple le rouge est encodé en `(255, 0, 0)`, le gris en `(128, 128, 128)`, etc. Ces couleurs sont organisées en matrice de dimension égale à celle de l'image, les couleurs sont indépendantes les unes des autres. Une image en informatique est stockée sous forme de matrice de pixels qui contiennent les couleurs pour chaque cellule. Le modèle classique de couleurs est dit "RGB" (Red, Green, Blue) [(doc)](https://fr.wikipedia.org/wiki/Rouge-vert-bleu) où chaque pixel contient une information colorimétrique encodée sous forme de triplets `(r, g, b)` (red, green, blue). Les valeurs de couleur peuvent varier de 0 à 255 pour chacune de ces composantes. Par exemple le rouge <span style="display:inline-block;width:15px;height:15px;background-color:rgb(255,0,0);"></span> est encodé en `(255, 0, 0)`, le gris <span style="display:inline-block;width:15px;height:15px;background-color:rgb(128,128,128);"></span> en `(128, 128, 128)`, etc. Ces couleurs sont organisées en une matrice de dimension égale à celle de l'image. Les couleurs sont indépendantes les unes des autres.
Dans ce TD nous allons utiliser un module Python appelé PIL (Pillow [doc](https://pillow.readthedocs.io/en/stable/)). Ce module permettra également de créer des images. Il est possible de l'initialiser comme suit pour charger une image dans une variable `px` dite d'accès de pixel `PixelAccess` [(doc)](https://pillow.readthedocs.io/en/stable/reference/PixelAccess.html). Le module Pillow est normalement installé, si ce n'est pas le cas, vous devez exécuter la commande suivante dans une fenêtre de terminal Anaconda (Menu Démarrer / Anaconda 64bit / Anaconda PowerShell Prompt) : `pip3 install Pillow`. Dans ce TD nous allons utiliser un module Python appelé PIL (Pillow [doc](https://pillow.readthedocs.io/en/stable/)) pour créer et manipuler des images. Il est possible de l'initialiser avec le code ci-dessous. Une fois le module importé, vous pourrez charger une image dans une variable `px` dite d'accès de pixel `PixelAccess` [(doc)](https://pillow.readthedocs.io/en/stable/reference/PixelAccess.html). Le module Pillow est normalement installé, si ce n'est pas le cas, vous devez exécuter la commande suivante dans une fenêtre de terminal Anaconda (Menu Démarrer / Anaconda 64bit / Anaconda PowerShell Prompt) : `pip install Pillow`. Si vous l'installer dans un notebook même utilisez la commande `!pip install Pillow`.
Pour tester sur le module est présent sur votre ordinateur, charger une image comme suit : Pour tester si le module est présent sur votre ordinateur, charger une image comme suit :
%% Cell type:code id:5a97d177-3289-451f-bb64-3d97ad023cd4 tags: %% Cell type:code id:5a97d177-3289-451f-bb64-3d97ad023cd4 tags:
``` python ``` python
from PIL import Image from PIL import Image
from IPython.display import display from IPython.display import display
im = Image.open("lyon.png") im = Image.open("lyon.png")
im = im.convert("RGB") # important pour bien avoir 3 couleurs im = im.convert("RGB") # important pour bien avoir 3 couleurs
px = im.load() px = im.load()
W, H = im.size # taille de l'image W, H = im.size # taille de l'image
r, g, b = px[10, 20] # on récupère un pixel r, g, b = px[10, 20] # on récupère un pixel
px[10, 21] = r, g, b # on change un pixel px[10, 21] = r, g, b # on change un pixel
im = im.resize((W//2, H//2)) im = im.resize((W//2, H//2))
display(im) # on affiche l'image dans la cellule display(im) # on affiche l'image dans la cellule
# im.show() # on affiche l'image si vous n'utilisez pas de notebook # im.show() # on affiche l'image si vous n'utilisez pas de notebook
``` ```
%% Cell type:markdown id:c2a6f43f-7d5d-46ff-85cb-37d0fb717c85 tags: %% Cell type:markdown id:c2a6f43f-7d5d-46ff-85cb-37d0fb717c85 tags:
Vous pouvez également créer une **nouvelle image** `im2` vide (noire) de taille identique à `im` : Vous pouvez également créer une **nouvelle image** `im2` vide (noire) de taille identique à `im` :
%% Cell type:code id:11e98fc1-9f60-476b-909b-d632d802f3f4 tags: %% Cell type:code id:11e98fc1-9f60-476b-909b-d632d802f3f4 tags:
``` python ``` python
im2 = Image.new('RGB', (im.width, im.height)) im2 = Image.new('RGB', (im.width, im.height))
px2 = im2.load() px2 = im2.load()
display(im2) display(im2)
``` ```
%% Cell type:markdown id:482f0b25-342e-403c-975b-65a42283e1cc tags: %% Cell type:markdown id:482f0b25-342e-403c-975b-65a42283e1cc tags:
**Question 1.1 -** Définissez une fonction de lecture qui renvoie la couleur d'un pixel à la position $(x, y)$ d'une image donnée. Inspirez vous du code précédent ou de la documentation Pillow [(doc)](https://pillow.readthedocs.io/en/stable/)). **Question 1.1 -** Ecrivez une fonction de lecture d'une image donnée, qui renvoie la couleur d'un pixel à la position $(x, y)$. Inspirez vous du code précédent ou de la documentation Pillow [(doc)](https://pillow.readthedocs.io/en/stable/).
%% Cell type:code id:567ca46f-9a29-44da-93a0-9f3992f39b03 tags: %% Cell type:code id:567ca46f-9a29-44da-93a0-9f3992f39b03 tags:
``` python ``` python
def getPixel(x: int, y: int, px) -> tuple: def getPixel(x: int, y: int, px) -> tuple:
"""Get the color of a pixel. """Get the color of a pixel
Args: Args:
x (int): pixel x coordinate x (int): pixel x coordinate
y (int): pixel y coordinate y (int): pixel y coordinate
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
Returns: Returns:
tuple: color of the pixel tuple: color of the pixel
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:08846407-027f-4b9e-86c7-43cce4234cd6 tags: %% Cell type:code id:08846407-027f-4b9e-86c7-43cce4234cd6 tags:
``` python ``` python
assert getPixel(0, 0, px) == (69, 119, 170) # bleu assert getPixel(0, 0, px) == (69, 119, 170) # bleu
assert getPixel(0, 0, px2) == (0, 0, 0) # noir assert getPixel(0, 0, px2) == (0, 0, 0) # noir
``` ```
%% Cell type:markdown id:49116049-3a09-4415-84c7-e71c6ea85c55 tags: %% Cell type:markdown id:49116049-3a09-4415-84c7-e71c6ea85c55 tags:
Afin de vérifier visuellement votre résultat, nous vous fournissons la fonction `draw_rectangle` qui permet de dessiner la couleur d'un pixel dans une cellule : Afin de vérifier visuellement votre résultat, nous vous fournissons la fonction `draw_rectangle` qui permet de dessiner la couleur d'un pixel dans une cellule :
%% Cell type:code id:b2098f50-57c2-4c4b-9a07-b695103590fa tags: %% Cell type:code id:b2098f50-57c2-4c4b-9a07-b695103590fa tags:
``` python ``` python
from IPython.display import display, HTML from IPython.display import display, HTML
def draw_rectangle(rgb_color): def draw_rectangle(rgb_color):
color = f'rgb({rgb_color[0]}, {rgb_color[1]}, {rgb_color[2]})' color = f'rgb({rgb_color[0]}, {rgb_color[1]}, {rgb_color[2]})'
html = f'<svg width="100" height="100"><rect width="100" height="100" fill="{color}" /></svg>' html = f'<svg width="100" height="100"><rect width="100" height="100" fill="{color}" /></svg>'
display(HTML(html)) display(HTML(html))
# utilisation : draw_rectangle((69, 119, 170)) # utilisation : draw_rectangle((69, 119, 170))
``` ```
%% Cell type:code id:c02ced7f-22bb-41c0-83d8-e91485dc4c5c tags: %% Cell type:code id:c02ced7f-22bb-41c0-83d8-e91485dc4c5c tags:
``` python ``` python
draw_rectangle(getPixel(0, 0, px)) # bleu draw_rectangle(getPixel(0, 0, px)) # bleu
``` ```
%% Cell type:code id:aa9391e7-8623-4c7e-9865-99ce35dc8683 tags: %% Cell type:code id:aa9391e7-8623-4c7e-9865-99ce35dc8683 tags:
``` python ``` python
draw_rectangle(getPixel(0, 0, px2)) # noir draw_rectangle(getPixel(0, 0, px2)) # noir
``` ```
%% Cell type:markdown id:69c1f988-3e28-45a7-a6c2-e0a7075c19f6 tags: %% Cell type:markdown id:69c1f988-3e28-45a7-a6c2-e0a7075c19f6 tags:
**Question 1.2 -** Définissez une fonction d'écriture d'un pixel à une position $(x, y)$ d'une image avec une couleur donnée en argument sous forme de `tuple` $(r, g, b)$. **Question 1.2 -** Ecrivez une fonction de coloriage d'un pixel à une position $(x, y)$ dans une image avec une couleur donnée en argument sous forme de `tuple` $(r, g, b)$.
%% Cell type:code id:df54ba88-6681-4eaa-9608-20c8d8db67ae tags: %% Cell type:code id:df54ba88-6681-4eaa-9608-20c8d8db67ae tags:
``` python ``` python
def setPixel(x: int, y:int, color: tuple, px) -> None: def setPixel(x: int, y:int, color: tuple, px) -> None:
"""Set the color of a pixel. """Set the color of a pixel
Args: Args:
x (int): pixel x coordinate x (int): pixel x coordinate
y (int): pixel y coordinate y (int): pixel y coordinate
color (tuple): color to set color (tuple): color to set
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:ab0b00f6-5a33-4da0-a97e-5ce74e827122 tags: %% Cell type:code id:ab0b00f6-5a33-4da0-a97e-5ce74e827122 tags:
``` python ``` python
r, g, b = (0, 0, 0) r, g, b = (0, 0, 0)
setPixel(0, 0, (r, g, b), px2) setPixel(0, 0, (r, g, b), px2) # on écrit un pixel dans le coin en haut à gauche
assert getPixel(0, 0, px2) == (r, g, b) assert getPixel(0, 0, px2) == (r, g, b) # on vérifie que la couleur a bien changé
``` ```
%% Cell type:markdown id:a46817a7-f6be-4f1b-b79f-0347669844cc tags: %% Cell type:markdown id:a46817a7-f6be-4f1b-b79f-0347669844cc tags:
**Question 1.3 -** Écrire une fonction permettant de peindre un rectangle de l'image avec une même couleur. Coloriez avec la couleur moyenne de cette région (et donc définir une fonction qui calcule cette couleur `moyenne`). **Question 1.3 -** Écrivez une fonction permettant de remplir une région de l'image avec une même couleur qui sera la moyenne des couleurs. Dans un premier temps définissez cette fonction de calcule de `moyenne` d'une région donnée.
%% Cell type:code id:1583490b-8d8a-4dab-94df-05281ebc668c tags: %% Cell type:code id:1583490b-8d8a-4dab-94df-05281ebc668c tags:
``` python ``` python
def moyenne(corner_x, corner_y, region_w, region_h, px) -> tuple: def moyenne(corner_x: int, corner_y: int, region_w: int, region_h: int, px: int) -> tuple:
"""Compute the average color of a region. """Compute the average color of a region
Args: Args:
corner_x (int): top left corner x coordinate corner_x (int): top left corner x coordinate
corner_y (int): top left corner y coordinate corner_y (int): top left corner y coordinate
region_w (int): region width region_w (int): region width
region_h (int): region height region_h (int): region height
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
Returns: Returns:
tuple: average color of the region tuple: average color of the region
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:bcb59088-8939-429c-9c82-a989d78922d4 tags:
``` python
assert moyenne(0, 0, 1, 1, px2) == (0, 0, 0) # le coin en haut à gauche est noir
```
%% Cell type:markdown id:913c742d-04ba-4220-adce-861bf39d999d tags: %% Cell type:markdown id:913c742d-04ba-4220-adce-861bf39d999d tags:
Dans la fonction de dessin de région `setRegion`la variable `color` contient le triplet de couleurs à utiliser. Ensuite écrivez la fonction `setRegion` qui permet de remplir une région donnée avec une couleur (par exemple la couleur moyenne de cette région.
%% Cell type:code id:a7ba398a-bf70-4c70-83d2-28c55bdf2762 tags: %% Cell type:code id:a7ba398a-bf70-4c70-83d2-28c55bdf2762 tags:
``` python ``` python
def setRegion(x, y, w, h, color, px) -> None: def setRegion(x: int, y: int, w: int, h: int, color: tuple, px) -> None:
"""Set the color of a region. """Set the color of a region
Args: Args:
x (int): top left corner x coordinate x (int): top left corner x coordinate
y (int): top left corner y coordinate y (int): top left corner y coordinate
w (int): region width w (int): region width
h (int): region height h (int): region height
color (tuple): color to set color (tuple): color to set
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:02374e5a-e9d5-4689-88dd-b2d53738c13d tags:
``` python
assert moyenne(0, 0, 1, 1, px2) == (0.0, 0.0, 0.0) # région noire
```
%% Cell type:markdown id:6e7ce82a-7fd9-4c4d-b76d-17f96da87cd5 tags: %% Cell type:markdown id:6e7ce82a-7fd9-4c4d-b76d-17f96da87cd5 tags:
Le code ci-dessous doit dessiner un rectangle blanc au milieu d'une image noire. Le code ci-dessous doit dessiner un rectangle blanc au milieu d'une image noire.
%% Cell type:code id:6b9fbf24-ac6a-4b08-a633-fd9a6bc4708f tags: %% Cell type:code id:6b9fbf24-ac6a-4b08-a633-fd9a6bc4708f tags:
``` python ``` python
im2 = Image.new('RGB', (im.width, im.height)) im2 = Image.new('RGB', (im.width, im.height))
px2 = im2.load() px2 = im2.load()
W, H = im.size W, H = im.size
setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px2) setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px2)
display(im2) display(im2)
``` ```
%% Cell type:markdown id:38f3a987-f93a-4119-a1fa-d87eaf55f9d1 tags: %% Cell type:markdown id:38f3a987-f93a-4119-a1fa-d87eaf55f9d1 tags:
Nous allons maintenant réaliser un remplissage un peu plus intéressant de l'image à partir de son contenu (dans notre cas nous nous baserons sur les couleurs contenues dans l'image). Nous allons maintenant réaliser un remplissage un peu plus avancé de l'image à partir de son contenu (dans notre cas nous nous baserons sur les couleurs contenues dans l'image). Avant de commencer, nous avons besoin d'une fonction de calcul de distance entre deux couleurs.
%% Cell type:markdown id:daa5c6f4-eabc-4f00-8d32-86d5695af06b tags: %% Cell type:markdown id:daa5c6f4-eabc-4f00-8d32-86d5695af06b tags:
**Question 1.4 -** Ecrire une fonction de calcul de [distance Euclidienne](https://fr.wikipedia.org/wiki/Distance_(math%C3%A9matiques)) entre deux couleurs RGB (Red, Green, Blue) $C_1$ et $C_2$ comme suit : **Question 1.4 -** Ecrivez une fonction de calcul de [distance Euclidienne](https://fr.wikipedia.org/wiki/Distance_(math%C3%A9matiques)) entre deux couleurs RGB (Red, Green, Blue) $C_1$ et $C_2$ comme suit :
$d_{\text{euclidienne}} = \sqrt{(R_2 - R_1)^2 + (G_2 - G_1)^2 + (B_2 - B_1)^2}$ $d_{\text{euclidienne}} = \sqrt{(R_2 - R_1)^2 + (G_2 - G_1)^2 + (B_2 - B_1)^2}$
Attention il s'agit de distances _entre_ les couleurs à comparer et non pas la distance entre les positions des pixels. Attention il s'agit de distances _entre_ les couleurs à comparer et non pas la distance entre les positions des pixels.
%% Cell type:code id:9be2d008-74fd-4f97-bdaf-d88525142f95 tags: %% Cell type:code id:9be2d008-74fd-4f97-bdaf-d88525142f95 tags:
``` python ``` python
from math import sqrt
def distance(c1: tuple, c2: tuple) -> float: def distance(c1: tuple, c2: tuple) -> float:
"""Compute the distance between two colors. """Compute the distance between two colors
Args: Args:
c1 (tuple): first color c1 (tuple): first color
c2 (tuple): second color c2 (tuple): second color
Returns: Returns:
float: distance between the two colors float: distance between the two colors
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:f673184f-81f9-4172-ac4f-58e6cccceffc tags: %% Cell type:code id:f673184f-81f9-4172-ac4f-58e6cccceffc tags:
``` python ``` python
assert distance((0, 0, 0), (0, 0, 0)) == 0.0 assert distance((0, 0, 0), (0, 0, 0)) == 0.0
``` ```
%% Cell type:markdown id:3e09490e-9955-4639-b149-53c16855e8df tags: %% Cell type:markdown id:3e09490e-9955-4639-b149-53c16855e8df tags:
**Question 1.5 -** Nous allons désormais travailler sur une méthode de remplissage de région basée sur l'homogénéité des couleurs dans la région. Pour cela nous allons ré-utiliser les méthodes ci-dessus en particulier la distance Euclidenne, en utilisant l'algorithme dit de _flood fill_ [(doc)](https://en.wikipedia.org/wiki/Flood_fill) et qui fonctionne comme suit : **Question 1.5 -** Nous allons désormais travailler sur une méthode de remplissage de région basée sur son contenu, en utilisant un critère d'homogénéité des couleurs dans la région. Pour cela nous allons ré-utiliser les méthodes ci-dessus en particulier la distance Euclidenne, mais également les piles vues précédemment. Nous utiliserons l'algorithme dit de _flood fill_ [(doc)](https://en.wikipedia.org/wiki/Flood_fill) et qui fonctionne comme suit :
1. Charger une image et initialiser deux listes vides : une liste de pixels à visiter et une liste de pixels déjà visités 1. Chargez une image et initialiser deux listes vides : une liste de pixels à visiter et une liste de pixels déjà visités
2. Définir un pixel de départ $p_{(x, y)}$ et le rajouter dans la liste de pixels à visiter 2. Définissez un pixel de départ $p_{(x, y)}$ et le rajouter dans la liste de pixels à visiter
3. Extraire un pixel $p_{(i, j)}$ de la liste des pixels à visiter, il constituera la couleur $c_{(i, j)}$ de la région homogène et le rajouter dans une troisième liste de pixels homogènes à colorier avec cette couleur 3. Récupérez un pixel $p_{(i, j)}$ de la liste des pixels à visiter, il constituera la couleur $c_{(i, j)}$ de la région homogène et le rajouter dans une troisième liste de pixels homogènes à colorier avec cette couleur
4. Tant que la liste de pixels homogènes n'est pas vide, extraire un pixel de cette liste : 4. Tant que la liste de pixels homogènes n'est pas vide, récupérez un pixel de cette liste :
- Colorier le pixel avec la couleur $c_{(i, j)}$ et le rajouter dans la liste des pixels visités - Coloriez le pixel avec la couleur $c_{(i, j)}$ et le rajouter dans la liste des pixels visités
- Explorer les 4 voisins autour du pixel (haut, bas, gauche, droite) et pour chaque voisin : - Explorez les 4 voisins autour du pixel (haut, bas, gauche, droite) et pour chaque voisin :
- Si la couleur du voisin est en dessous d'un seuil d'homogénéité alors l'inclure dans la liste de pixels homogènes - Si la couleur du voisin est en dessous d'un seuil d'homogénéité alors l'inclure dans la liste de pixels homogènes
- Sinon rajouter le pixel dans la liste de pixels à visiter - Sinon rajoutez le pixel dans la liste de pixels à visiter
- Répéter cela tant que la liste de pixels homogènes n'est pas vide - Répétez cela tant que la liste de pixels homogènes n'est pas vide
5. Répéter cela tant que la liste des pixels non visités n'est pas vide 5. Répétez cela tant que la liste des pixels non visités n'est pas vide
Le résultat attendu est une image remplie coloriée avec un nombre de couleur inférieur au nombre initial de couleurs. Le résultat attendu est une image remplie coloriée avec un nombre de couleur inférieur au nombre initial de couleurs.
%% Cell type:code id:7dfc1c84-e0ca-49d1-b089-8a23175f715f tags: %% Cell type:code id:7dfc1c84-e0ca-49d1-b089-8a23175f715f tags:
``` python ``` python
def floodFill(w: int, h: int, start_x: int, start_y: int, seuil: int, px, px2) -> tuple: def floodFill(w: int, h: int, start_x: int, start_y: int, seuil: int, px, px2) -> tuple:
"""Flood fill algorithm. """Flood fill algorithm
Args: Args:
w (int): image width w (int): image width
h (int): image height h (int): image height
start_x (int): starting x coordinate start_x (int): starting x coordinate
start_y (int): starting y coordinate start_y (int): starting y coordinate
seuil (int): color distance threshold seuil (int): color distance threshold
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
px2 (PixelAccess): image pixel access px2 (PixelAccess): image pixel access (image we will re-color as a result)
Returns: Returns:
tuple: visited pixels, not visited pixels, unique colors used tuple: visited pixels, not visited pixels, unique colors used
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:1a50b940-d3b5-4dd5-9b9f-cd4977c9bc24 tags: %% Cell type:markdown id:1a50b940-d3b5-4dd5-9b9f-cd4977c9bc24 tags:
Le code ci-dessous va tester votre solution avec une image fournie et générer quelques statistiques liés au nombre de couleurs utilisées. Le code ci-dessous va tester votre solution avec une image fournie et générer quelques statistiques liés au nombre de couleurs utilisées.
%% Cell type:code id:102f2245-a8ce-41d6-9250-e163c96bb952 tags: %% Cell type:code id:102f2245-a8ce-41d6-9250-e163c96bb952 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:1647215c tags:
Vous pouvez tester avec l'image noire (de dimension 100x100) avec un rectangle blanc au centre et vous devrez obtenir :
```
nombre de pixels dans l'image: 10000
nombre de noeuds coloriés 10000
nombre de noeuds non visités 0
nombre de couleurs utilisées 2
````
%% Cell type:code id:40510314 tags:
``` python
im = Image.new('RGB', (100, 100))
px = im.load()
W, H = im.size
setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px)
im2 = Image.new('RGB', (im.width, im.height))
px2 = im2.load()
W, H = im.size
print("nombre de pixels dans l'image:", W * H)
seuil = 10
start_x, start_y = 0, 0
visited, not_visited, colors = floodFill(W, H, start_x, start_y, seuil, px, px2)
print("nombre de noeuds coloriés", len(visited)) # 10000
print("nombre de noeuds non visités", len(not_visited)) # 10000 (tous visités)
print(f"nombre de couleurs utilisées {len(colors)}") # 0 (aucun oubli)
display(im2) # 2 couleurs utilisées (noir et blanc)
```
%% Cell type:markdown id:e2eb0bd7-ef80-41fd-8aba-2460de5b6ad4 tags: %% Cell type:markdown id:e2eb0bd7-ef80-41fd-8aba-2460de5b6ad4 tags:
## Pour aller plus loin ## Pour aller plus loin
- Tester le remplissage dans 8 directions (en prenant en compte les diagonales) au moyen dans la fonction d'exploration de voisinage des pixels. - Testez le remplissage dans 8 directions (en prenant en compte les diagonales) au moyen dans la fonction d'exploration de voisinage des pixels.
%% Cell type:markdown id:0e3f9555-c2fe-4c68-b4cd-b659b928755e tags: %% Cell type:markdown id:0e3f9555-c2fe-4c68-b4cd-b659b928755e tags:
## Exercice 2 : Traitement d'image par filtre ## Exercice 2 : Traitement d'image par filtre
Nous allons aborder un deuxième aspect de manipulation d'image : le traitement d'image, afin d'en extraire des informations intéressantes (contours, formes, etc.). En particulier nous allons créer différents _filtres_ dont le but sera de transformer les valeurs des pixels afin de par exemple réduire le bruit que les images peuvent contenir (à savoir les variations locales de valeur). Nous allons aborder un deuxième aspect de la manipulation d'image : le traitement d'image, afin d'en extraire des informations intéressantes (contours, formes, etc.). En particulier nous allons créer différents _filtres_ dont le but sera de transformer les valeurs des pixels afin de par exemple réduire le bruit que les images peuvent contenir (à savoir les variations locales de valeur).
La plupart de ces méthodes étant couteuses en temps, nous travaillerons sur une version en niveau de gris. La plupart de ces méthodes étant couteuses en temps, nous travaillerons sur une version en niveau de gris.
%% Cell type:markdown id:057fa44d-2c9f-423c-af12-16731b401884 tags: %% Cell type:markdown id:057fa44d-2c9f-423c-af12-16731b401884 tags:
**Question 2.1 -** Écrire une fonction de conversion d'image en niveaux de gris (soit la moyenne des triplets `(r,g,b)` ou en utilisant la formule suivante : **Question 2.1 -** Écrivez une fonction de conversion d'image en niveaux de gris (soit la moyenne des triplets `(r,g,b)` ou en utilisant la formule suivante :
$ C_{gray} = (0.3 \times R) + (0.59 \times G) + (0.11 \times B)$ $ C_{gray} = (0.3 \times R) + (0.59 \times G) + (0.11 \times B)$
%% Cell type:code id:cee93e0f-a63b-4a19-87e7-00c736191465 tags: %% Cell type:code id:cee93e0f-a63b-4a19-87e7-00c736191465 tags:
``` python ``` python
def conversion_gris(px, W: int, H: int) -> None: def conversion_gris(px, W: int, H: int) -> None:
"""Convert an image to grayscale. """Convert an image to grayscale.
Args: Args:
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
W (int): image width W (int): image width
H (int): image height H (int): image height
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:4828de37-3b06-48f6-b32a-6d6e53285787 tags: %% Cell type:code id:4828de37-3b06-48f6-b32a-6d6e53285787 tags:
``` python ``` python
im = Image.open("lyon.png") im = Image.open("lyon.png")
im = im.convert("RGB") im = im.convert("RGB")
px = im.load() px = im.load()
W, H = im.size W, H = im.size
conversion_gris(px, W, H) conversion_gris(px, W, H)
display(im) display(im)
``` ```
%% Cell type:markdown id:1f8656cb-ec1f-4ce3-a837-2b938e36f1e5 tags: %% Cell type:markdown id:1f8656cb-ec1f-4ce3-a837-2b938e36f1e5 tags:
Nous commençon avec le filtre dit de _Flou Gaussien_, basé sur une opération dite de _convolution_, permettant d'appliquer une fonction de distribution gaussienne aux voisins d'un pixel et d'en faire la moyenne. Autrement dit il s'agira de réaliser la moyenne pondérée de chaque pixel en réalisant la moyenne du pixel et de ses voisins en utilisant par exemple la matrice ci-dessous (dont les valeurs sont définies par la distribution gaussienne donnée en annexe pour une matrice $3 \times 3$) : Nous commençon avec le filtre dit de _Flou Gaussien_, basé sur une opération dite de _convolution_, permettant d'appliquer une fonction de distribution gaussienne aux voisins d'un pixel et d'en faire la moyenne. Autrement dit il s'agira de réaliser la moyenne pondérée de chaque pixel en réalisant la moyenne du pixel et de ses voisins en utilisant par exemple la matrice ci-dessous (dont les valeurs sont définies par la distribution gaussienne donnée en annexe pour une matrice $3 \times 3$) :
$G(x) = \frac{1}{\sqrt[]{2 \pi } \sigma} e^{- \frac{x^{2}}{2 \sigma ^{2}}}$ $G(x) = \frac{1}{\sqrt[]{2 \pi } \sigma} e^{- \frac{x^{2}}{2 \sigma ^{2}}}$
%% Cell type:code id:9faad901-6b61-4011-be8e-55d85d3b7ded tags: %% Cell type:code id:9faad901-6b61-4011-be8e-55d85d3b7ded tags:
``` python ``` python
gauss3 = [[1,2,1], gauss3 = [[1,2,1],
[2,4,2], [2,4,2],
[1,2,1]] [1,2,1]]
gauss7 = [[1,1,2,2,2,1,1], gauss7 = [[1,1,2,2,2,1,1],
[1,2,2,4,2,2,1], [1,2,2,4,2,2,1],
[2,2,4,8,4,2,2], [2,2,4,8,4,2,2],
[2,4,8,16,8,4,2], [2,4,8,16,8,4,2],
[2,2,4,8,4,2,2], [2,2,4,8,4,2,2],
[1,2,2,4,2,2,1], [1,2,2,4,2,2,1],
[1,1,2,2,2,1,1]] [1,1,2,2,2,1,1]]
``` ```
%% Cell type:markdown id:d90ad339-06fc-4423-a302-034c1d872301 tags: %% Cell type:markdown id:d90ad339-06fc-4423-a302-034c1d872301 tags:
**Question 2.2 -** Implémentez le filtre gaussien tel que défini ci-dessus en définissant les filtres donnés ci-dessous. La fonction doit effectuer la multiplication des pixels centrés sur le pixel en cours avec les valeurs de la matrice, puis la somme pondérée. Vous pourrez utiliser les matrices ci-dessus d'approximation du filtre (en commençant par le filtre Gaussien $3 \times 3$ dont le total des valeurs est $16$). Utilisez la version de l'image en niveaux de gris afin de simplifier les traitements. **Question 2.2 -** Implémentez le filtre gaussien tel que défini ci-dessus en définissant les filtres donnés ci-dessous. La fonction doit effectuer la multiplication des pixels centrés sur le pixel en cours avec les valeurs de la matrice, puis la somme pondérée. Vous pourrez utiliser les matrices ci-dessus d'approximation du filtre (en commençant par le filtre Gaussien $3 \times 3$ dont le total des valeurs est $16$). Utilisez la version de l'image en niveaux de gris afin de simplifier les traitements.
Commencez tout d'abord par définir une fonction qui calcul la somme des valeurs d'une matrice (de type `gauss3`). Commencez tout d'abord par définir une fonction qui calcul la somme des valeurs d'une matrice (de type `gauss3`).
%% Cell type:code id:06cd5d47-9fde-4b0f-a833-b7ce415e6349 tags: %% Cell type:code id:06cd5d47-9fde-4b0f-a833-b7ce415e6349 tags:
``` python ``` python
def somme_matrice(m: list = []) -> float: def somme_matrice(m: list = []) -> float:
"""Compute the sum of all the elements in a matrix. """Compute the sum of all the elements in a matrix.
Args: Args:
m (list): matrix m (list): matrix
Returns: Returns:
float: sum of all the elements in the matrix float: sum of all the elements in the matrix
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:f02e6ba8-d870-40e0-8ed9-67b35ef12e57 tags: %% Cell type:code id:f02e6ba8-d870-40e0-8ed9-67b35ef12e57 tags:
``` python ``` python
assert somme_matrice(gauss3) == 16 assert somme_matrice(gauss3) == 16
``` ```
%% Cell type:markdown id:c0030b8b-159b-463d-a830-d6d554e6382b tags: %% Cell type:markdown id:c0030b8b-159b-463d-a830-d6d554e6382b tags:
Ecrire la fonction de convolution. Pensez à prendre en compte les bords de l'image. Conseil : ne vous approchez pas trop du bord afin de ne pas réaliser une convolution en dehors de l'image. Ecrire la fonction de convolution. Pensez à prendre en compte les bords de l'image. Conseil : ne vous approchez pas trop du bord afin de ne pas réaliser une convolution en dehors de l'image.
%% Cell type:code id:0c295c65-4e2c-4751-91a8-cb8108e18ba2 tags: %% Cell type:code id:0c295c65-4e2c-4751-91a8-cb8108e18ba2 tags:
``` python ``` python
def convolution(px, W: int, H: int, m: list) -> None: def convolution(px, W: int, H: int, m: list) -> None:
"""Apply a convolution matrix on an image. """Apply a convolution matrix on an image.
Args: Args:
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
W (int): image width W (int): image width
H (int): image height H (int): image height
m (list): convolution matrix m (list): convolution matrix
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:726a449a-e975-4647-9f57-e31f3581124d tags: %% Cell type:code id:726a449a-e975-4647-9f57-e31f3581124d tags:
``` python ``` python
im = Image.open("lyon.png") im = Image.open("lyon.png")
im = im.convert("RGB") im = im.convert("RGB")
px = im.load() px = im.load()
W, H = im.size W, H = im.size
conversion_gris(px, W, H) conversion_gris(px, W, H)
convolution(px, W, H, gauss3) convolution(px, W, H, gauss3)
display(im) display(im)
``` ```
%% Cell type:markdown id:746bd3e7-f8fc-4ed2-a89b-2b4e52d6ace1 tags: %% Cell type:markdown id:746bd3e7-f8fc-4ed2-a89b-2b4e52d6ace1 tags:
Vous pouvez comparer votre floutage avec celui de PIL : Vous pouvez comparer votre floutage avec celui de PIL :
%% Cell type:code id:513a179c-89bc-43e6-87e2-ca033ffee304 tags: %% Cell type:code id:513a179c-89bc-43e6-87e2-ca033ffee304 tags:
``` python ``` python
from PIL import Image, ImageFile, ImageDraw, ImageChops, ImageFilter from PIL import Image, ImageFile, ImageDraw, ImageChops, ImageFilter
_im = im.filter(ImageFilter.GaussianBlur) _im = im.filter(ImageFilter.GaussianBlur)
display(_im) display(_im)
``` ```
%% Cell type:markdown id:7bb648b1-450b-4bd9-b52a-c798107d6079 tags: %% Cell type:markdown id:7bb648b1-450b-4bd9-b52a-c798107d6079 tags:
**Question 2.3 -** Testez votre code avec ces filtres ci-dessous avec un facteur de normalisation/pondération de 1 : que se passe-t-il ? [(doc)](https://en.wikipedia.org/wiki/Kernel_(image_processing)). **Question 2.3 -** Testez votre code avec ces filtres ci-dessous avec un facteur de normalisation/pondération de 1 : que se passe-t-il ? [(doc)](https://en.wikipedia.org/wiki/Kernel_(image_processing)).
%% Cell type:code id:f213b45e-768f-4d89-b456-f12050a3217c tags: %% Cell type:code id:f213b45e-768f-4d89-b456-f12050a3217c tags:
``` python ``` python
sobely3 = [[-1, 0, 1], sobely3 = [[-1, 0, 1],
[-2, 0, 2], [-2, 0, 2],
[-1, 0, 1]] [-1, 0, 1]]
sobelx3 = [[-1, -2, -1], sobelx3 = [[-1, -2, -1],
[0, 0, 0], [0, 0, 0],
[1, 2, 1]] [1, 2, 1]]
``` ```
%% Cell type:code id:760d4ca7-5f31-457f-8b09-6e0dfc12c7a8 tags: %% Cell type:code id:760d4ca7-5f31-457f-8b09-6e0dfc12c7a8 tags:
``` python ``` python
def convolution_sobel(px, W: int, H: int, m: list, f = 1) -> None: def convolution_sobel(px, W: int, H: int, m: list, f = 1) -> None:
"""Apply a sobel filter convolution on an image. """Apply a sobel filter convolution on an image
Args: Args:
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
W (int): image width W (int): image width
H (int): image height H (int): image height
m (list): convolution matrix m (list): convolution matrix
f (int): factor f (int): factor
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:00c62110-6e7b-42d6-8450-d8ed7c7e233a tags: %% Cell type:markdown id:00c62110-6e7b-42d6-8450-d8ed7c7e233a tags:
## Tests pour la fonction de floodfill ## Tests pour la fonction de floodfill
%% Cell type:code id:d61c4763-f479-42fe-91bb-27311925f6c0 tags: %% Cell type:code id:d61c4763-f479-42fe-91bb-27311925f6c0 tags:
``` python ``` python
W, H = 100, 100 W, H = 100, 100
im = Image.new('RGB', (W, H)) im = Image.new('RGB', (W, H))
px = im.load() px = im.load()
setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px) setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px)
display(im) display(im)
im2 = Image.new('RGB', (W, H)) im2 = Image.new('RGB', (W, H))
px2 = im2.load() px2 = im2.load()
display(im2) display(im2)
d = distance((0, 0, 0), (255, 255, 255)) # pour trouver le seuil d = distance((0, 0, 0), (255, 255, 255)) # pour trouver le seuil
print("on est en dessous du seuil on garde les 2 couleurs") print("on est en dessous du seuil on garde les 2 couleurs")
print("on part du coin en haut à gauche -> noir") print("on part du coin en haut à gauche -> noir")
visited, not_visited, colors = floodFill(W, H, 0, 0, d - 1, px, px2) visited, not_visited, colors = floodFill(W, H, 0, 0, d - 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
print("on part du milieu -> blanc") print("on part du milieu -> blanc")
visited, not_visited, colors = floodFill(W, H, W/2, H/2, d - 1, px, px2) visited, not_visited, colors = floodFill(W, H, W/2, H/2, d - 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
print("on est au dessus du seuil on ne garde qu'une couleur") print("on est au dessus du seuil on ne garde qu'une couleur")
print("on part du coin en haut à gauche -> noir") print("on part du coin en haut à gauche -> noir")
visited, not_visited, colors = floodFill(W, H, 0, 0, d + 1, px, px2) visited, not_visited, colors = floodFill(W, H, 0, 0, d + 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
print("on part du milieu -> blanc") print("on part du milieu -> blanc")
visited, not_visited, colors = floodFill(W, H, W/2, H/2, d + 1, px, px2) visited, not_visited, colors = floodFill(W, H, W/2, H/2, d + 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
``` ```
%% Cell type:markdown id:f9d03a14 tags:
## FAQ
1. Problème d'accès aux fichiers d'images (image non trouvée) : vérifiez que l'image fonctionne bien et se situe dans le répertoire de votre notebook que vous pouvez trouver avec la commande ci-dessous.
%% Cell type:code id:980dfda1-8ac9-49da-ac66-6b460d91b5f2 tags:
``` python
import os
print(os.getcwd())
```
......
%% Cell type:markdown id:052d9fc3 tags: %% Cell type:markdown id:751c51f9 tags:
NAME: NAME:
%% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags: %% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags:
# INF TC1 - TD5 (2h) - Devoir à rendre #1 # INF TC1 - TD5 (2h + 2h AUTONOMIE) - Devoir à rendre #1
%% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags: %% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags:
--- ---
%% Cell type:markdown id:dd8d4905-55f9-4957-8008-a963cc6de061 tags: %% Cell type:markdown id:dd8d4905-55f9-4957-8008-a963cc6de061 tags:
Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après les séances d'autonomie et de TD. Le rendu sera à réaliser sous forme de **notebook** qui contient votre code et images. Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après la séance d'autonomie #1. Le rendu sera à réaliser sous forme de **notebook** qui contient votre code et images.
%% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags: %% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags:
<details style="border: 1px"> <details style="border: 1px">
<summary> MODALITES DE RENDU</summary> <summary> MODALITES DE RENDU</summary>
### Comment rendre son devoir ? ### Comment rendre son devoir ?
Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après les séances d'autonomie et de TD. Vous devrez créer une archive (zip, rar, etc.) nomée `nom1-nom2-inf-tc1-td5.zip` qui contiendra tous les éléments de votre rendu (rapport en notebook, code, images de test). Vous pouvez rendre ce rapport seul ou en binôme. Le rendu du TD doit contenir a minima : Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après les séances d'autonomie et de TD. Vous devrez créer une archive (zip, rar, etc.) nomée `nom1-nom2-inf-tc1-td5.zip` qui contiendra tous les éléments de votre rendu (rapport en notebook, code, images de test). Vous pouvez rendre ce rapport seul ou en binôme. Le rendu du TD doit contenir a minima :
1. Toutes les étapes jusqu'à la 6ème doivent avoir été abordées 1. Toutes les étapes jusqu'à la 6ème doivent avoir été abordées
2. Justifications, illustrations et tests sur plusieurs images 2. Justifications, illustrations et tests sur plusieurs images
**A garder en tête :** **A garder en tête :**
- Un code fonctionnel et les tests appropriés devront être fournis dans l'archive qui doit être autonome (le correcteur ne doit pas avoir à rajouter d'image ou de fichier supplémentaire) - Un code fonctionnel et les tests appropriés devront être fournis dans l'archive qui doit être autonome (le correcteur ne doit pas avoir à rajouter d'image ou de fichier supplémentaire)
- Vous fournirez les images de test et leurs résultats; évitez cependant de prendre des tailles d'images trop importantes. - Vous fournirez les images de test et leurs résultats; évitez cependant de prendre des tailles d'images trop importantes.
- Le rapport **devra être au format Notebook Jupyter** et comprendre : - Le rapport **devra être au format Notebook Jupyter** et comprendre :
- Le détail des étapes que vous avez suivies - Le détail des étapes que vous avez suivies
- La description de parties de code difficiles - La description de parties de code difficiles
- Tout souci ou point bloquant dans votre code - Tout souci ou point bloquant dans votre code
- Les graphiques et diagrammes nécessaires - Les graphiques et diagrammes nécessaires
- Des analyses et discussions en lien avec votre approche - Des analyses et discussions en lien avec votre approche
- Des exemples simples mais aussi difficiles - Des exemples simples mais aussi difficiles
**Tout travail supplémentaire (méthode originale, optimisation poussée) fera l'objet de points en bonus.** **Tout travail supplémentaire (méthode originale, optimisation poussée) fera l'objet de points en bonus.**
*Voici une suggestion afin de se faire un ordre d'idée* *Voici une suggestion afin de se faire un ordre d'idée*
En dessous de 10 :
- Les étapes suivies
- Un code fonctionnel et les méthodes basiques
- Un rapport de quelques pages
- Un code certes fonctionnel mais peu commenté
- Les exemples d'images fournies
Un groupe avec une note entre 10 et 12 : Un groupe avec une note entre 10 et 12 :
- Les étapes suivies - Les étapes suivies
- Un code fonctionnel et les méthodes basiques - Un code fonctionnel et les méthodes basiques
- Un rapport de quelques pages - Un rapport succint
- Un code certes fonctionnel mais peu commenté - Un code certes fonctionnel mais peu commenté
- Les exemples d'images fournies - Les exemples d'images fournies
Un groupe entre 12 et 14 a en plus proposé : Un groupe entre 12 et 14 a en plus proposé :
- Des structures de données avancées (Set, Files, etc) - Des structures de données avancées (Ensembles, Files, etc)
- Une justification de chaque étape - Une justification de chaque étape
- Une méthode un petit peu plus poussée - Une méthode un petit peu plus poussée
Un groupe entre 14 et 16 a en plus proposé : Un groupe entre 14 et 16 a en plus proposé :
- Une méthode originale (K-Means, etc) - Une méthode originale (K-Means, etc)
- Une démarche expérimentale très détaillée sur les optimisations - Une démarche expérimentale très détaillée sur les optimisations
- Des tests plutôt originaux - Des tests plutôt originaux
Un groupe au-dessus de 16 comporte une ou plusieurs parties exceptionnelles : Un groupe au-dessus de 16 comporte une ou plusieurs parties exceptionnelles :
- Rapport très détaillé et exemplaire sur le fond comme sur la forme - Rapport très détaillé et exemplaire sur le fond comme sur la forme
- Une démarche expérimentale très détaillée sur les optimisations - Une démarche expérimentale très détaillée sur les optimisations
- Code et tests - Code et tests
</details> </details>
%% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags: %% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags:
## Objectif du devoir ## Objectif du devoir
Le but de ce devoir est de **déterminer automatiquement une palette de couleurs optimale** pour une image donnée. Cette palette devra valider les contraintes suivantes : Le but de ce devoir est de **déterminer automatiquement une palette de couleurs optimale** pour une image donnée. Cette palette devra valider les contraintes suivantes :
1. de taille réduite par rapport au nombre initial de couleurs 1. utiliser moins de couleurs que le nombre disponible dans l'image donnée;
2. la plus représentative possible des couleurs initiales. 2. être la plus représentative possible des couleurs de l'image donnée.
En effet une image affichée sur un ordinateur peut être encodée sur 8 bits par composantes rouge, verte et bleue (soit 256 valeurs possibles par composante) ainsi potentiellement utiliser $256 \times 256 \times 256 = 16 777 216$ couleurs. En réalité, beaucoup moins sont utilisées et surtout perceptibles par l'humain. Réduire le nombre de couleur ou réaliser une "_quantification de couleurs_" est une tâche fréquente et c'est une fonctionnalité classique des outils éditeurs d'images (Photoshop, Gimp, etc.) implémentée aussi dans le module Pillow de Python. A noter que cette réduction s'effectue avec perte de couleurs et doit être réalisée avec les bons paramètres (nombre et choix des couleurs) ce qui est votre objectif. Comme nous l'avons vu dans le TD 4, les couleurs peuvent être encodée par composantes rouge, verte et bleue (soit 256 valeurs possibles par composante, autrement dit sur 8 bits) ainsi potentiellement utiliser $256 \times 256 \times 256 = 16 777 216$ couleurs. En réalité, beaucoup moins sont nécessaires et surtout perceptibles par l'humain. Réduire le nombre de couleurs ou réaliser une "_quantification de couleurs_" est une tâche fréquente et c'est une fonctionnalité classique des outils éditeurs d'images (Photoshop, Gimp, etc.) implémentée aussi dans le module Pillow de Python. A noter que cette réduction s'effectue avec perte de couleurs et doit être réalisée avec les bons paramètres (nombre et choix des couleurs) ce qui est votre objectif.
La figure ci-dessous illustre le problème à résoudre : étant donnée une image en entrée, proposer une liste de couleurs (que l'on appellera la palette), afin de re-colorier une image en sortie. La figure ci-dessous illustre le problème à résoudre : étant donnée une image en entrée, proposer une liste de couleurs (que l'on appellera la palette), afin de re-colorier une image en sortie.
<div style="text-align:center;"> <div style="text-align:center;">
<table> <table>
<tr> <tr>
<td> <td>
<img src="figures/color-rainbow.png" alt="Image originale" style="height:5cm;"> <img src="figures/color-rainbow.png" alt="Image originale" style="height:5cm;">
<p>Image originale</p> <p>Image donnée</p>
</td> </td>
<td> <td>
<img src="figures/rainbow-palette-8.png" alt="Palette de 8 couleurs représentatives" style="height:5cm;"> <img src="figures/rainbow-palette-8.png" alt="Palette de 8 couleurs représentatives" style="height:5cm;">
<p>Palette de 8 couleurs représentatives</p> <p>Palette de 8 couleurs représentatives</p>
</td> </td>
<td> <td>
<img src="figures/rainbow-recoloriee.png" alt="Image originale recoloriée avec la palette" style="height:5cm;"> <img src="figures/rainbow-recoloriee.png" alt="Image originale recoloriée avec la palette" style="height:5cm;">
<p>Image originale recoloriée avec la palette</p> <p>Image donnée recoloriée avec la palette</p>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
%% Cell type:markdown id:fd464e65-adfe-4e11-bf87-f12c513fbaea tags: %% Cell type:markdown id:fd464e65-adfe-4e11-bf87-f12c513fbaea tags:
## Étapes de travail ## Étapes de travail
Voici des étapes de travail suggérées : Voici des étapes de travail suggérées :
1. Prendre en main une image de votre choix (pas trop grande) en la chargeant avec PIL. Lister les couleurs présentes, identifier celles qui sont uniques et leur fréquence. Vous pouvez pour cela utiliser [Matplotlib](https://matplotlib.org/stable/gallery/index.html). 1. Prenez une image de votre choix (pas trop grande) en la chargeant avec PIL. Lister les couleurs présentes, identifier celles qui sont uniques et leur fréquence.
2. Proposer une méthode (naïve pour commencer) de choix d'une palette de $k$ couleurs. Affichez là sous forme d'image (exemple de d'image au milieu de la figure du dessus) avec une nouvelle image PIL. Utilisez également des images simples où le résultat attendu est connu comme mour les images ci-dessous : 2. Proposez une méthode (naïve pour commencer) de choix d'une palette de $k$ couleurs. Affichez là sous forme d'image (exemple de d'image au milieu de la figure du dessus) avec une nouvelle image PIL. Utilisez également des images simples où le résultat attendu est connu comme pour les images ci-dessous :
<div style="text-align:center;"> <div style="text-align:center;">
<table> <table>
<tr> <tr>
<td> <td>
<img src="figures/1-color-back.png" alt="1 couleur noir" style="width:3cm;"> <img src="figures/1-color-back.png" alt="1 couleur noir" style="width:3cm;">
<p>1 couleur noir</p> <p>1 couleur noir</p>
</td> </td>
<td> <td>
<img src="figures/4-color.png" alt="4 couleurs" style="width:3cm;"> <img src="figures/4-color.png" alt="4 couleurs" style="width:3cm;">
<p>4 couleurs</p> <p>4 couleurs</p>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
3. Re-colorier une image avec une palette de $k$ couleurs, et afficher le résultat sous forme d'image PIL. Pour re-colorier chaque pixel, prendre la couleur la plus proche dans la palette en utilisant une fonction de distance (Euclidienne par exemple). 3. Re-coloriez une image avec une palette de $k$ couleurs, et affichez le résultat sous forme d'image PIL. Pour re-colorier chaque pixel, prendre la couleur la plus proche dans la palette en utilisant une fonction de distance (Euclidienne par exemple..).
4. Proposer une méthode de validation de votre approche. Par exemple afficher la différence entre l'image originale et celle re-coloriée. Calculer un score global d'erreur. 4. Proposez une méthode de validation de votre approche. Par exemple affichez la différence entre l'image originale et celle re-coloriée. Calculez un score global d'erreur.
5. Améliorer le choix des $k$ couleurs afin de minimiser l'erreur entre l'image originale et re-coloriée. Une piste possible est de trier les couleurs dans une liste, diviser cette liste en $k$ intervals de couleurs et prendre la couleur du milieu de chaque interval. D'autres méthodes plus avancées peuvent être explorées ! 5. Améliorez le choix des $k$ couleurs afin de minimiser l'erreur entre l'image originale et re-coloriée. Une piste possible est de trier les couleurs dans une liste, diviser cette liste en $k$ intervals de couleurs et prendre la couleur du milieu de chaque interval. D'autres méthodes plus avancées peuvent être explorées !
6. Tester sur plusieurs images de votre choix ou générées automatiquement avec un nombre et une distribution connue de couleurs. Comparer les performances de vos techniques avec d'autres méthodes (cette fois vous pouvez utiliser un éditeur de texte ou la fonction _quantize_ de PIL [(doc)](https://pillow.readthedocs.io/en/stable/reference/Image.html). 6. Testez votre palette sur plusieurs images de votre choix ou générées automatiquement avec un nombre et une distribution connue de couleurs. Comparer les performances de vos techniques avec d'autres méthodes (cette fois vous pouvez utiliser un éditeur de texte ou la fonction _quantize_ de PIL [(doc)](https://pillow.readthedocs.io/en/stable/reference/Image.html).
7. Utiliser un pré-traitement des images (flou gaussien, etc) afin de lisser les couleurs est une piste afin de choisir de meilleurs couleurs représentatives. Proposez une quantification de cette amélioration (ou de déterioration éventuelle). 7. Utilisez un pré-traitement des images (flou gaussien, etc) afin de lisser les couleurs. Cela est une piste afin de choisir de meilleurs couleurs représentatives. Proposez une comparaison de cette amélioration (ou de déterioration éventuelle) avec les autres méthodes.
7. Proposer une méthode d'amélioration de calcul de la distance entre deux couleurs, vous pouvez vous baser sur d'autres espaces de couleur [(doc)](https://fr.wikipedia.org/wiki/Espace_de_couleur). Cette partie est difficile, les espaces de couleurs possibles sont complexes à comprendre. 8. Proposez une méthode d'amélioration de calcul de la distance entre deux couleurs, vous pouvez vous baser sur d'autres espaces de couleur [(doc)](https://fr.wikipedia.org/wiki/Espace_de_couleur). Cette partie est difficile, les espaces de couleurs possibles sont complexes à comprendre.
8. Optimiser les étapes précédentes (complexité, espace nécessaire, structures de données, etc.). 9. Optimisez les étapes précédentes (complexité, espace nécessaire, structures de données, etc.) et justifiez vos choix.
### Bonus ### Bonus
10. Créer une palette représentative à partir de plusieurs images. 10. Créez une palette représentative à partir de plusieurs images.
......
%% Cell type:markdown id:caaef4d1 tags: %% Cell type:markdown id:886b66ed tags:
NAME: NAME:
%% Cell type:markdown id:6d71a1d5-6589-4320-900f-b07f08df01f4 tags: %% Cell type:markdown id:6d71a1d5-6589-4320-900f-b07f08df01f4 tags:
# INF TC1 - TD6 (2h) - Automates # INF TC1 - TD6 (2h) - Automates
%% Cell type:markdown id:54c3bdf8-4ded-45da-a79a-6530af149f51 tags: %% Cell type:markdown id:54c3bdf8-4ded-45da-a79a-6530af149f51 tags:
--- ---
%% Cell type:markdown id:33320365-404e-4424-96d1-6e6b742c8f44 tags: %% Cell type:markdown id:33320365-404e-4424-96d1-6e6b742c8f44 tags:
## Objectif du TD ## Objectif du TD
Dans ce TD nous allons introduire les automates, qui sont (en informatique) un modèle de calcul permettant de déterminer si une séquence d'information est valide ou non, selon une règle déterminée. Dans un premier temps nous allons définir des automates simples, et ensuite les implémenter en Python et résoudre des problèmes de complexité croissante. Dans ce TD nous allons introduire les automates, qui sont (en informatique) un modèle de calcul permettant de déterminer si une séquence d'information est valide ou non, selon une règle déterminée. Dans un premier temps nous allons définir des automates simples, et ensuite les implémenter en Python et résoudre des problèmes de complexité croissante.
%% Cell type:markdown id:02ba4e95-34be-41b9-b36c-255b5af6b3de tags: %% Cell type:markdown id:02ba4e95-34be-41b9-b36c-255b5af6b3de tags:
## Qu'est-ce qu'un automate ? ## Qu'est-ce qu'un automate ?
Un automate est un outil de calcul permettant la validation de séquences d'instructions, à base d'états et de transitions. Un exemple d'automate est un feu tricolore, où : Un automate est un outil de calcul permettant la validation de séquences d'instructions, à base d'états et de transitions. Un exemple d'automate est un feu tricolore, où :
- les **états** sont la couleur du feu (rouge, vert ou orange) - les **états** sont la couleur du feu (rouge, vert ou orange)
- les **transitions** les changements possibles de couleurs (du rouge au vert, du vert au orange, et du orange au rouge). - les **transitions** les changements possibles de couleurs (du rouge au vert, du vert au orange, et du orange au rouge).
Les automates permettent donc de formaliser le fonctionnement d'un système et de détecter des erreurs éventuelles qui ne respectent pas les changements pré-définis (en reprenant l'exemple du feu tricolore, passer du vert au rouge directement est une erreur). Les applications des automates sont nombreuses et offrent souvent un code plus facile à écire et lire. Les automates permettent donc de formaliser le fonctionnement d'un système et de détecter des erreurs éventuelles qui ne respectent pas les changements pré-définis (en reprenant l'exemple du feu tricolore, passer du vert au rouge directement est une erreur). Les applications des automates sont nombreuses et offrent souvent un code plus facile à écire et lire.
%% Cell type:code id:ca93dafe-32d7-4332-9d26-c57c928805ee tags: %% Cell type:code id:ca93dafe-32d7-4332-9d26-c57c928805ee tags:
``` python ``` python
from IPython.core.display import HTML from IPython.core.display import HTML
HTML('<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><!-- Generated by graphviz version 7.1.0 (20230121.1956) --><!-- Pages: 1 --><svg width="131pt" height="52pt" viewBox="0.00 0.00 131.00 52.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 48)"><polygon fill="white" stroke="none" points="-4,4 -4,-48 127,-48 127,4 -4,4"/><g id="node1" class="node"><title>0</title><ellipse fill="none" stroke="black" stroke-width="2" cx="18" cy="-22" rx="18" ry="18"/><text text-anchor="middle" x="18" y="-18.3" font-family="Times,serif" font-size="14.00">0</text></g><g id="node2" class="node"><title>1</title><ellipse fill="none" stroke="black" cx="101" cy="-22" rx="18" ry="18"/><ellipse fill="none" stroke="black" cx="101" cy="-22" rx="22" ry="22"/><text text-anchor="middle" x="101" y="-18.3" font-family="Times,serif" font-size="14.00">1</text></g><g id="edge1" class="edge"><title>0→1</title><path fill="none" stroke="black" d="M36.18,-22C45.15,-22 56.45,-22 67.03,-22"/><polygon fill="black" stroke="black" points="67,-25.5 77,-22 67,-18.5 67,-25.5"/><text text-anchor="middle" x="57.5" y="-25.8" font-family="Times,serif" font-size="14.00">a</text></g></g></svg>') HTML('<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><!-- Generated by graphviz version 7.1.0 (20230121.1956) --><!-- Pages: 1 --><svg width="131pt" height="52pt" viewBox="0.00 0.00 131.00 52.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 48)"><polygon fill="white" stroke="none" points="-4,4 -4,-48 127,-48 127,4 -4,4"/><g id="node1" class="node"><title>0</title><ellipse fill="none" stroke="black" stroke-width="2" cx="18" cy="-22" rx="18" ry="18"/><text text-anchor="middle" x="18" y="-18.3" font-family="Times,serif" font-size="14.00">0</text></g><g id="node2" class="node"><title>1</title><ellipse fill="none" stroke="black" cx="101" cy="-22" rx="18" ry="18"/><ellipse fill="none" stroke="black" cx="101" cy="-22" rx="22" ry="22"/><text text-anchor="middle" x="101" y="-18.3" font-family="Times,serif" font-size="14.00">1</text></g><g id="edge1" class="edge"><title>0→1</title><path fill="none" stroke="black" d="M36.18,-22C45.15,-22 56.45,-22 67.03,-22"/><polygon fill="black" stroke="black" points="67,-25.5 77,-22 67,-18.5 67,-25.5"/><text text-anchor="middle" x="57.5" y="-25.8" font-family="Times,serif" font-size="14.00">a</text></g></g></svg>')
``` ```
%% Cell type:markdown id:07c95f05-1782-4f25-80fe-9dcf93bdedc6 tags: %% Cell type:markdown id:07c95f05-1782-4f25-80fe-9dcf93bdedc6 tags:
## Définitions ## Définitions
Un automate possède une structure de données similaire à un graphe orienté, où **chaque nœud représente un état** et un **arc représente une transition possible d'un état à un autre**. Ce graphe est ensuite parcouru à partir de `mots` (par exemple : `aba`), qui sont une suite de symboles (comme les lettres `a` et `b`) permettant de passer d'un état à un autre. L'état initial (unique) est représenté visuellement par un cercle en gras, et le dernier état (il peut y en avoir plusieurs) par un double cercle. Les symboles `a` et `b` constituent l'alphabet de l'automate (il est possible d'utiliser tout type d'alphabet, comme les transitions d'un feu tricolore). Un automate possède une structure de données similaire à un graphe orienté, où **chaque nœud représente un état** et un **arc représente une transition possible d'un état à un autre**. Ce graphe est ensuite parcouru à partir de `mots` (par exemple : `aba`), qui sont une suite de symboles (comme les lettres `a` et `b`) permettant de passer d'un état à un autre. L'état initial (unique) est représenté visuellement par un cercle en gras, et le dernier état (il peut y en avoir plusieurs) par un double cercle. Les symboles `a` et `b` constituent l'alphabet de l'automate (il est possible d'utiliser tout type d'alphabet, comme les transitions d'un feu tricolore).
Dans l'exemple ci-dessus, si le mot à lire est `a`, l'automate commence à lire le mot depuis l'état `0` et réalise ensuite une transition vers l'état `1` et s'arrête. Comme l'état `1` est un état final le mot `a` est validé. On parlera de motif pour indiquer les familles de mots validés par un automate, comme par exemple les mots qui commencent par `a` nous notons `a*` (l'astérisque indiquant que tout symbole peut être utilisé). Dans l'exemple ci-dessus, si le mot à lire est `a`, l'automate commence à lire le mot depuis l'état `0` et réalise ensuite une transition vers l'état `1` et s'arrête. Comme l'état `1` est un état final le mot `a` est validé. On parlera de motif pour indiquer les familles de mots validés par un automate, comme par exemple les mots qui commencent par `a` nous notons `a*` (l'astérisque indiquant que tout symbole peut être utilisé).
De manière générale, un automate est défini comme $A = (\Sigma, S, s_{0}, \delta, F)$, avec : De manière générale, un automate est défini comme $A = (\Sigma, S, s_{0}, \delta, F)$, avec :
- $\Sigma$, un ensemble fini, non vide de symboles qui est l'alphabet d'entrée - $\Sigma$, un ensemble fini, non vide de symboles qui est l'alphabet d'entrée
- $S$, un ensemble fini, non vide d'états - $S$, un ensemble fini, non vide d'états
- $s_{0}$, l'état initial, élément de $S$ - $s_{0}$, l'état initial, élément de $S$
- $\delta$, la fonction de transition d'états: $\delta : S \times \Sigma \rightarrow S$ - $\delta$, la fonction de transition d'états: $\delta : S \times \Sigma \rightarrow S$
- $F$ est l'ensemble des états terminaux, un sous-ensemble (éventuellement vide) de $S$ - $F$ est l'ensemble des états terminaux, un sous-ensemble (éventuellement vide) de $S$
%% Cell type:markdown id:15b6ca9c-be1c-4757-b02c-1511fed5df68 tags: %% Cell type:markdown id:15b6ca9c-be1c-4757-b02c-1511fed5df68 tags:
## Dessin d'automates ## Dessin d'automates
Pour dessiner des automates, nous utiliserons [Graphviz](https://graphviz.org/), un outil en ligne de commande qui permet de dessiner des graphes basé sur le langage [DOT](https://graphviz.org/doc/info/lang.html). Un exemple d'automate est donné ci-dessous : Pour dessiner des automates, nous utiliserons [Graphviz](https://graphviz.org/), un outil en ligne de commande qui permet de dessiner des graphes basé sur le langage [DOT](https://graphviz.org/doc/info/lang.html). Un exemple d'automate est donné ci-dessous :
```python ```python
from graphviz import Digraph from graphviz import Digraph
dot = Digraph() dot = Digraph()
dot.graph_attr['rankdir'] = 'LR' dot.graph_attr['rankdir'] = 'LR'
dot.node('A', shape='circle', style='bold', label='0') dot.node('A', shape='circle', style='bold', label='0')
dot.node('B', shape='doublecircle', label='1') dot.node('B', shape='doublecircle', label='1')
dot.edge('A', 'B', label='a') dot.edge('A', 'B', label='a')
dot.edge('B', 'A', label='a') dot.edge('B', 'A', label='a')
dot dot
`````` ``````
%% Cell type:markdown id:07d1cd6d-1845-4707-b126-fbe219408a92 tags: %% Cell type:markdown id:07d1cd6d-1845-4707-b126-fbe219408a92 tags:
**IMPORTANT :** vérifier que le code ci-dessus s'exécute bien (dans la cellule ci-dessous). Si cela n'est pas le cas alors suivez ces [instructions d'installation de la bibliothèque Graphviz](https://gitlab.ec-lyon.fr/rvuillem/inf-tc1/-/blob/master/graphviz.ipynb). **IMPORTANT :** vérifier que le code ci-dessus s'exécute bien (dans la cellule ci-dessous). Si cela n'est pas le cas alors suivez ces [instructions d'installation de la bibliothèque Graphviz](https://gitlab.ec-lyon.fr/rvuillem/inf-tc1/-/blob/master/graphviz.ipynb).
%% Cell type:code id:bff8d514-4ab5-4a24-81c2-ecd7ef39aa9e tags: %% Cell type:code id:bff8d514-4ab5-4a24-81c2-ecd7ef39aa9e tags:
``` python ``` python
from graphviz import Digraph from graphviz import Digraph
dot = Digraph() dot = Digraph()
dot.graph_attr['rankdir'] = 'LR' dot.graph_attr['rankdir'] = 'LR'
dot.node('0', shape='circle', style='bold', label='0') dot.node('0', shape='circle', style='bold', label='0')
dot.node('1', shape='doublecircle', label='1') dot.node('1', shape='doublecircle', label='1')
dot.edge('0', '1', label='a') dot.edge('0', '1', label='a')
dot dot
``` ```
%% Cell type:markdown id:5e0cae70-8346-4664-af97-02fb5a426518 tags:
**VERSION EN LIGNE SANS GRAPHVIZ** : si l'installation de graphviz ne fonctionne pas, vous pouvez utiliser une [version en ligne](https://dreampuf.github.io/GraphvizOnline/#digraph%20G%20%7B%0A%20%20rankdir%3DLR%3B%0A%20%20a%20-%3E%20b%20%5Blabel%3D%22X%22%5D%3B%0A%0A%20%20a%20%5Bshape%3Dcircle%2C%20style%3Dbold%5D%3B%0A%20%20b%20%5Bshape%3Ddoublecircle%5D%3B%0A%7D) afin de réaliser le dessins sous forme de code, et exporter le résultat en image à inclure dans une cellule comme ci-dessous.
%% Cell type:code id:0178c283 tags:
``` python
# version sans graphviz : version qui contient une image au lieu de code graphviz
from IPython.core.display import HTML
HTML('<img src="figures_automates/0_1.png">')
```
%% Cell type:markdown id:ad7810cc-eefc-4733-993e-50c131c3e514 tags: %% Cell type:markdown id:ad7810cc-eefc-4733-993e-50c131c3e514 tags:
## Exercice 1 : Automates simples ## Exercice 1 : Automates simples
Dans cette section, nous vous demandons de proposer un automate qui valide un motif donné. Pour les questions 1.1, 1.2 et 1.3, nous considérons un alphabet contenant les lettres `a` et `b` seulement. Vous pouvez répondre aux questions sur papier de préférence, ou utiliser le code ci-dessus du module `graphviz` pour dessiner l'automate. Dans cette section, nous vous demandons de proposer un automate qui valide un motif donné. Pour les questions 1.1, 1.2 et 1.3, nous considérons un alphabet contenant les lettres `a` et `b` seulement. Vous pouvez répondre aux questions sur papier de préférence, ou utiliser le code ci-dessus du module `graphviz` pour dessiner l'automate.
**Question 1.1 -** Proposer un automate qui contient un nombre paire de fois la lettre `a`. **Question 1.1 -** Proposer un automate qui contient un nombre paire de fois la lettre `a`.
%% Cell type:code id:d33a29aa-5e07-476d-bfa9-4ef7454bc6f4 tags: %% Cell type:code id:d33a29aa-5e07-476d-bfa9-4ef7454bc6f4 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:3187d0f6 tags:
``` python
# version sans graphviz
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:87566002-5968-4b5f-aa05-c108bfe1cf33 tags: %% Cell type:markdown id:87566002-5968-4b5f-aa05-c108bfe1cf33 tags:
**Question 1.2 -** Proposer un automate qui valide le motif `a*a` (les mots qui commencent et finissent par `a`, de taille > 2). **Question 1.2 -** Proposer un automate qui valide le motif `a*a` (les mots qui commencent et finissent par `a`, de taille > 2).
%% Cell type:code id:a8a32710-840f-4385-a85f-670c992ddca7 tags: %% Cell type:code id:a8a32710-840f-4385-a85f-670c992ddca7 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:9017ef71 tags:
``` python
# version sans graphviz
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:70c4a996-c123-49c9-b771-b58046b6bb97 tags: %% Cell type:markdown id:70c4a996-c123-49c9-b771-b58046b6bb97 tags:
penser à proposer une liste de mots à valider et à ne pas valider afin de guider les étudiants. penser à proposer une liste de mots à valider et à ne pas valider afin de guider les étudiants.
%% Cell type:markdown id:14d7d169-fa9b-4c90-87d9-b07cfb1cc323 tags: %% Cell type:markdown id:14d7d169-fa9b-4c90-87d9-b07cfb1cc323 tags:
**Question 1.3 -** Quel langage valide l'automate ci-dessous ? Donnez un exemple de mots validés et le langage. **Question 1.3 -** Quel langage valide l'automate ci-dessous ? Donnez un exemple de mots validés et le langage.
%% Cell type:code id:b83f2bad-2864-4eeb-9851-510c7d5dd273 tags: %% Cell type:code id:b83f2bad-2864-4eeb-9851-510c7d5dd273 tags:
``` python ``` python
from IPython.core.display import HTML from IPython.core.display import HTML
HTML('<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generated by graphviz version 7.1.0 (20230121.1956) --><!-- Pages: 1 --><svg width="368pt" height="118pt" viewBox="0.00 0.00 368.00 118.00" version="1.1" id="svg1036" sodipodi:docname="a.svg" inkscape:version="1.2.2 (b0a8486, 2022-12-01)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <defs id="defs1040" /> <sodipodi:namedview id="namedview1038" pagecolor="#ffffff" bordercolor="#000000" borderopacity="0.25" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="pt" showgrid="false" inkscape:zoom="1.5" inkscape:cx="245.66667" inkscape:cy="78.666667" inkscape:window-width="1309" inkscape:window-height="456" inkscape:window-x="0" inkscape:window-y="25" inkscape:window-maximized="0" inkscape:current-layer="svg1036" /> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 114)"> <polygon fill="white" stroke="none" points="-4,4 -4,-114 364,-114 364,4 -4,4" id="polygon906" /> <!-- Q_0 --> <g id="node1" class="node"> <title id="title908">Q_0</title> <ellipse fill="none" stroke="black" stroke-width="2" cx="18" cy="-39" rx="18" ry="18" id="ellipse910" /> <text text-anchor="middle" x="18" y="-35.3" font-family="Times,serif" font-size="14.00" id="text912">0</text> </g> <!-- Q_0&#45;&gt;Q_0 --> <g id="edge2" class="edge"> <title id="title915">Q_0-&gt;Q_0</title> <path fill="none" stroke="black" d="M11,-56.04C9.57,-65.86 11.91,-75 18,-75 21.52,-75 23.79,-71.94 24.8,-67.47" id="path917" /> <polygon fill="black" stroke="black" points="28.3,-67.61 24.97,-57.55 21.3,-67.49 28.3,-67.61" id="polygon919" /> <text text-anchor="middle" x="18" y="-78.8" font-family="Times,serif" font-size="14.00" id="text921">b</text> </g> <!-- Q_1 --> <g id="node2" class="node"> <title id="title924">Q_1</title> <ellipse fill="none" stroke="black" cx="97" cy="-52" rx="18" ry="18" id="ellipse926" /> <text text-anchor="middle" x="97" y="-48.3" font-family="Times,serif" font-size="14.00" id="text928">1</text> </g> <!-- Q_0&#45;&gt;Q_1 --> <g id="edge1" class="edge"> <title id="title931">Q_0-&gt;Q_1</title> <path fill="none" stroke="black" d="M34.18,-47.66C40.16,-50.6 47.21,-53.52 54,-55 58.45,-55.97 63.23,-56.29 67.9,-56.22" id="path933" /> <polygon fill="black" stroke="black" points="67.86,-59.73 77.58,-55.49 67.34,-52.75 67.86,-59.73" id="polygon935" /> <text text-anchor="middle" x="57.5" y="-58.8" font-family="Times,serif" font-size="14.00" id="text937">a</text> </g> <!-- Q_1&#45;&gt;Q_0 --> <g id="edge4" class="edge"> <title id="title940">Q_1-&gt;Q_0</title> <path fill="none" stroke="black" d="M80.82,-43.34C74.84,-40.4 67.79,-37.48 61,-36 56.55,-35.03 51.77,-34.71 47.1,-34.78" id="path942" /> <polygon fill="black" stroke="black" points="47.14,-31.27 37.42,-35.51 47.66,-38.25 47.14,-31.27" id="polygon944" /> <text text-anchor="middle" x="57.5" y="-39.8" font-family="Times,serif" font-size="14.00" id="text946">b</text> </g> <!-- Q_2 --> <g id="node3" class="node"> <title id="title949">Q_2</title> <ellipse fill="none" stroke="black" cx="176" cy="-52" rx="18" ry="18" id="ellipse951" /> <text text-anchor="middle" x="176" y="-48.3" font-family="Times,serif" font-size="14.00" id="text953">2</text> </g> <!-- Q_1&#45;&gt;Q_2 --> <g id="edge3" class="edge"> <title id="title956">Q_1-&gt;Q_2</title> <path fill="none" stroke="black" d="M115.47,-52C124.53,-52 135.83,-52 146.14,-52" id="path958" /> <polygon fill="black" stroke="black" points="146.08,-55.5 156.08,-52 146.08,-48.5 146.08,-55.5" id="polygon960" /> <text text-anchor="middle" x="136.5" y="-55.8" font-family="Times,serif" font-size="14.00" id="text962">a</text> </g> <!-- Q_2&#45;&gt;Q_2 --> <g id="edge6" class="edge"> <title id="title965">Q_2-&gt;Q_2</title> <path fill="none" stroke="black" d="M169,-69.04C167.57,-78.86 169.91,-88 176,-88 179.52,-88 181.79,-84.94 182.8,-80.47" id="path967" /> <polygon fill="black" stroke="black" points="186.3,-80.61 182.97,-70.55 179.3,-80.49 186.3,-80.61" id="polygon969" /> <text text-anchor="middle" x="176" y="-91.8" font-family="Times,serif" font-size="14.00" id="text971">a</text> </g> <!-- Q_3 --> <g id="node4" class="node"> <title id="title974">Q_3</title> <ellipse fill="none" stroke="black" cx="255" cy="-22" rx="18" ry="18" id="ellipse976" /> <text text-anchor="middle" x="255" y="-18.3" font-family="Times,serif" font-size="14.00" id="text978">3</text> </g> <!-- Q_2&#45;&gt;Q_3 --> <g id="edge5" class="edge"> <title id="title981">Q_2-&gt;Q_3</title> <path fill="none" stroke="black" d="M193.33,-45.64C203.13,-41.82 215.84,-36.87 227.09,-32.48" id="path983" /> <polygon fill="black" stroke="black" points="228.24,-35.79 236.28,-28.9 225.7,-29.27 228.24,-35.79" id="polygon985" /> <text text-anchor="middle" x="215.5" y="-40.8" font-family="Times,serif" font-size="14.00" id="text987">b</text> </g> <!-- Q_3&#45;&gt;Q_0 --> <g id="edge8" class="edge"> <title id="title990">Q_3-&gt;Q_0</title> <path fill="none" stroke="black" d="M236.53,-21.08C204.94,-19.69 136.3,-17.91 79,-25 68.32,-26.32 56.76,-28.77 46.67,-31.25" id="path992" /> <polygon fill="black" stroke="black" points="45.9,-27.83 37.1,-33.73 47.66,-34.61 45.9,-27.83" id="polygon994" /> <text text-anchor="middle" x="136.5" y="-23.8" font-family="Times,serif" font-size="14.00" id="text996">b</text> </g> <!-- Q_4 --> <g id="node5" class="node"> <title id="title999">Q_4</title> <ellipse fill="none" stroke="black" cx="338" cy="-22" rx="18" ry="18" id="ellipse1001" /> <ellipse fill="none" stroke="black" cx="338" cy="-22" rx="22" ry="22" id="ellipse1003" /> <text text-anchor="middle" x="338" y="-18.3" font-family="Times,serif" font-size="14.00" id="text1005">4</text> </g> <!-- Q_3&#45;&gt;Q_4 --> <g id="edge7" class="edge"> <title id="title1008">Q_3-&gt;Q_4</title> <path fill="none" stroke="black" d="M273.18,-22C282.15,-22 293.45,-22 304.03,-22" id="path1010" /> <polygon fill="black" stroke="black" points="304,-25.5 314,-22 304,-18.5 304,-25.5" id="polygon1012" /> <text text-anchor="middle" x="294.5" y="-25.8" font-family="Times,serif" font-size="14.00" id="text1014">a</text> </g> <!-- Q_4&#45;&gt;Q_4 --> <g id="edge9" class="edge"> <title id="title1017">Q_4-&gt;Q_4</title> <path fill="none" stroke="black" d="M333.99,-43.81C333.6,-53.56 334.94,-62 338,-62 339.72,-62 340.9,-59.33 341.53,-55.26" id="path1019" /> <polygon fill="black" stroke="black" points="345.02,-55.46 341.95,-45.32 338.03,-55.16 345.02,-55.46" id="polygon1021" /> <text text-anchor="middle" x="338" y="-65.8" font-family="Times,serif" font-size="14.00" id="text1023">a</text> </g> <!-- Q_4&#45;&gt;Q_4 --> <g id="edge10" class="edge"> <title id="title1026">Q_4-&gt;Q_4</title> <path fill="none" stroke="black" d="M331.14,-43.11C328.11,-61.1 330.4,-80 338,-80 344,-80 346.69,-68.23 346.07,-54.43" id="path1028" /> <polygon fill="black" stroke="black" points="349.56,-54.19 345.02,-44.62 342.6,-54.93 349.56,-54.19" id="polygon1030" /> <text text-anchor="middle" x="338" y="-83.8" font-family="Times,serif" font-size="14.00" id="text1032">b</text> </g> </g></svg>') HTML('<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generated by graphviz version 7.1.0 (20230121.1956) --><!-- Pages: 1 --><svg width="368pt" height="118pt" viewBox="0.00 0.00 368.00 118.00" version="1.1" id="svg1036" sodipodi:docname="a.svg" inkscape:version="1.2.2 (b0a8486, 2022-12-01)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <defs id="defs1040" /> <sodipodi:namedview id="namedview1038" pagecolor="#ffffff" bordercolor="#000000" borderopacity="0.25" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="pt" showgrid="false" inkscape:zoom="1.5" inkscape:cx="245.66667" inkscape:cy="78.666667" inkscape:window-width="1309" inkscape:window-height="456" inkscape:window-x="0" inkscape:window-y="25" inkscape:window-maximized="0" inkscape:current-layer="svg1036" /> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 114)"> <polygon fill="white" stroke="none" points="-4,4 -4,-114 364,-114 364,4 -4,4" id="polygon906" /> <!-- Q_0 --> <g id="node1" class="node"> <title id="title908">Q_0</title> <ellipse fill="none" stroke="black" stroke-width="2" cx="18" cy="-39" rx="18" ry="18" id="ellipse910" /> <text text-anchor="middle" x="18" y="-35.3" font-family="Times,serif" font-size="14.00" id="text912">0</text> </g> <!-- Q_0&#45;&gt;Q_0 --> <g id="edge2" class="edge"> <title id="title915">Q_0-&gt;Q_0</title> <path fill="none" stroke="black" d="M11,-56.04C9.57,-65.86 11.91,-75 18,-75 21.52,-75 23.79,-71.94 24.8,-67.47" id="path917" /> <polygon fill="black" stroke="black" points="28.3,-67.61 24.97,-57.55 21.3,-67.49 28.3,-67.61" id="polygon919" /> <text text-anchor="middle" x="18" y="-78.8" font-family="Times,serif" font-size="14.00" id="text921">b</text> </g> <!-- Q_1 --> <g id="node2" class="node"> <title id="title924">Q_1</title> <ellipse fill="none" stroke="black" cx="97" cy="-52" rx="18" ry="18" id="ellipse926" /> <text text-anchor="middle" x="97" y="-48.3" font-family="Times,serif" font-size="14.00" id="text928">1</text> </g> <!-- Q_0&#45;&gt;Q_1 --> <g id="edge1" class="edge"> <title id="title931">Q_0-&gt;Q_1</title> <path fill="none" stroke="black" d="M34.18,-47.66C40.16,-50.6 47.21,-53.52 54,-55 58.45,-55.97 63.23,-56.29 67.9,-56.22" id="path933" /> <polygon fill="black" stroke="black" points="67.86,-59.73 77.58,-55.49 67.34,-52.75 67.86,-59.73" id="polygon935" /> <text text-anchor="middle" x="57.5" y="-58.8" font-family="Times,serif" font-size="14.00" id="text937">a</text> </g> <!-- Q_1&#45;&gt;Q_0 --> <g id="edge4" class="edge"> <title id="title940">Q_1-&gt;Q_0</title> <path fill="none" stroke="black" d="M80.82,-43.34C74.84,-40.4 67.79,-37.48 61,-36 56.55,-35.03 51.77,-34.71 47.1,-34.78" id="path942" /> <polygon fill="black" stroke="black" points="47.14,-31.27 37.42,-35.51 47.66,-38.25 47.14,-31.27" id="polygon944" /> <text text-anchor="middle" x="57.5" y="-39.8" font-family="Times,serif" font-size="14.00" id="text946">b</text> </g> <!-- Q_2 --> <g id="node3" class="node"> <title id="title949">Q_2</title> <ellipse fill="none" stroke="black" cx="176" cy="-52" rx="18" ry="18" id="ellipse951" /> <text text-anchor="middle" x="176" y="-48.3" font-family="Times,serif" font-size="14.00" id="text953">2</text> </g> <!-- Q_1&#45;&gt;Q_2 --> <g id="edge3" class="edge"> <title id="title956">Q_1-&gt;Q_2</title> <path fill="none" stroke="black" d="M115.47,-52C124.53,-52 135.83,-52 146.14,-52" id="path958" /> <polygon fill="black" stroke="black" points="146.08,-55.5 156.08,-52 146.08,-48.5 146.08,-55.5" id="polygon960" /> <text text-anchor="middle" x="136.5" y="-55.8" font-family="Times,serif" font-size="14.00" id="text962">a</text> </g> <!-- Q_2&#45;&gt;Q_2 --> <g id="edge6" class="edge"> <title id="title965">Q_2-&gt;Q_2</title> <path fill="none" stroke="black" d="M169,-69.04C167.57,-78.86 169.91,-88 176,-88 179.52,-88 181.79,-84.94 182.8,-80.47" id="path967" /> <polygon fill="black" stroke="black" points="186.3,-80.61 182.97,-70.55 179.3,-80.49 186.3,-80.61" id="polygon969" /> <text text-anchor="middle" x="176" y="-91.8" font-family="Times,serif" font-size="14.00" id="text971">a</text> </g> <!-- Q_3 --> <g id="node4" class="node"> <title id="title974">Q_3</title> <ellipse fill="none" stroke="black" cx="255" cy="-22" rx="18" ry="18" id="ellipse976" /> <text text-anchor="middle" x="255" y="-18.3" font-family="Times,serif" font-size="14.00" id="text978">3</text> </g> <!-- Q_2&#45;&gt;Q_3 --> <g id="edge5" class="edge"> <title id="title981">Q_2-&gt;Q_3</title> <path fill="none" stroke="black" d="M193.33,-45.64C203.13,-41.82 215.84,-36.87 227.09,-32.48" id="path983" /> <polygon fill="black" stroke="black" points="228.24,-35.79 236.28,-28.9 225.7,-29.27 228.24,-35.79" id="polygon985" /> <text text-anchor="middle" x="215.5" y="-40.8" font-family="Times,serif" font-size="14.00" id="text987">b</text> </g> <!-- Q_3&#45;&gt;Q_0 --> <g id="edge8" class="edge"> <title id="title990">Q_3-&gt;Q_0</title> <path fill="none" stroke="black" d="M236.53,-21.08C204.94,-19.69 136.3,-17.91 79,-25 68.32,-26.32 56.76,-28.77 46.67,-31.25" id="path992" /> <polygon fill="black" stroke="black" points="45.9,-27.83 37.1,-33.73 47.66,-34.61 45.9,-27.83" id="polygon994" /> <text text-anchor="middle" x="136.5" y="-23.8" font-family="Times,serif" font-size="14.00" id="text996">b</text> </g> <!-- Q_4 --> <g id="node5" class="node"> <title id="title999">Q_4</title> <ellipse fill="none" stroke="black" cx="338" cy="-22" rx="18" ry="18" id="ellipse1001" /> <ellipse fill="none" stroke="black" cx="338" cy="-22" rx="22" ry="22" id="ellipse1003" /> <text text-anchor="middle" x="338" y="-18.3" font-family="Times,serif" font-size="14.00" id="text1005">4</text> </g> <!-- Q_3&#45;&gt;Q_4 --> <g id="edge7" class="edge"> <title id="title1008">Q_3-&gt;Q_4</title> <path fill="none" stroke="black" d="M273.18,-22C282.15,-22 293.45,-22 304.03,-22" id="path1010" /> <polygon fill="black" stroke="black" points="304,-25.5 314,-22 304,-18.5 304,-25.5" id="polygon1012" /> <text text-anchor="middle" x="294.5" y="-25.8" font-family="Times,serif" font-size="14.00" id="text1014">a</text> </g> <!-- Q_4&#45;&gt;Q_4 --> <g id="edge9" class="edge"> <title id="title1017">Q_4-&gt;Q_4</title> <path fill="none" stroke="black" d="M333.99,-43.81C333.6,-53.56 334.94,-62 338,-62 339.72,-62 340.9,-59.33 341.53,-55.26" id="path1019" /> <polygon fill="black" stroke="black" points="345.02,-55.46 341.95,-45.32 338.03,-55.16 345.02,-55.46" id="polygon1021" /> <text text-anchor="middle" x="338" y="-65.8" font-family="Times,serif" font-size="14.00" id="text1023">a</text> </g> <!-- Q_4&#45;&gt;Q_4 --> <g id="edge10" class="edge"> <title id="title1026">Q_4-&gt;Q_4</title> <path fill="none" stroke="black" d="M331.14,-43.11C328.11,-61.1 330.4,-80 338,-80 344,-80 346.69,-68.23 346.07,-54.43" id="path1028" /> <polygon fill="black" stroke="black" points="349.56,-54.19 345.02,-44.62 342.6,-54.93 349.56,-54.19" id="polygon1030" /> <text text-anchor="middle" x="338" y="-83.8" font-family="Times,serif" font-size="14.00" id="text1032">b</text> </g> </g></svg>')
``` ```
%% Cell type:code id:7aae22c9 tags: %% Cell type:code id:7aae22c9 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:b1c8a625-b2e5-4958-b49f-009c7bcdb038 tags: %% Cell type:markdown id:b1c8a625-b2e5-4958-b49f-009c7bcdb038 tags:
**Question 1.4 -** Proposer un automate qui valide la propriété suivante : une chaîne de caractères est une adresse email. Les adresses email peuvent être définies comme suit (de manière simplifiée) : **Question 1.4 -** Proposer un automate qui valide la propriété suivante : une chaîne de caractères est une adresse email. Les adresses email peuvent être définies comme suit (de manière simplifiée) :
- le premier caractère ne peut pas être un chiffre - le premier caractère ne peut pas être un chiffre
- ensuite tous les caractères de `a` à `z` (majuscules et minuscules) et chiffres sont acceptés - ensuite tous les caractères de `a` à `z` (majuscules et minuscules) et chiffres sont acceptés
- un `@` doit être présent - un `@` doit être présent
- ensuite un nom de domaine qui lui aussi ne peut commencer par un chiffre (et doit faire + de 1 caractère) - ensuite un nom de domaine qui lui aussi ne peut commencer par un chiffre (et doit faire + de 1 caractère)
- un "." - un "."
- une extension parmis une liste autorisée (`fr`, `com`, etc) - une extension parmis une liste autorisée (`fr`, `com`, etc)
Un exemple de chaîne qui n'est pas validée est `3toto@a.fr2` car la première partie commence par un chiffre, le nom de domaine est trop court et enfin l'extension n'est pas valide. Pour la partie de validation de l'extension (`.fr`, etc.), vous pouvez simplifier en proposant une reconnaissance de motifs pré-définis (`.fr`, `.com`, etc.). Vous pouvez vous référer à la page Wikipedia [ici](https://fr.wikipedia.org/wiki/Adresse_\%C3\%A9lectronique) ou à la RFC 8222 [ici](https://www.w3.org/Protocols/rfc822/) pour une définition plus précise. Un exemple de chaîne qui n'est pas validée est `3toto@a.fr2` car la première partie commence par un chiffre, le nom de domaine est trop court et enfin l'extension n'est pas valide. Pour la partie de validation de l'extension (`.fr`, etc.), vous pouvez simplifier en proposant une reconnaissance de motifs pré-définis (`.fr`, `.com`, etc.). Vous pouvez vous référer à la page Wikipedia [ici](https://fr.wikipedia.org/wiki/Adresse_\%C3\%A9lectronique) ou à la RFC 8222 [ici](https://www.w3.org/Protocols/rfc822/) pour une définition plus précise.
%% Cell type:code id:2ee41daa-5611-433d-862a-f098127cad73 tags: %% Cell type:code id:2ee41daa-5611-433d-862a-f098127cad73 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:73d884f0 tags:
``` python
# version sans graphviz
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:6655a83e tags: %% Cell type:markdown id:6655a83e tags:
## Exercice 2 : Structure de données d'automate en Python ## Exercice 2 : Structure de données d'automate en Python
%% Cell type:markdown id:bd6c2f5f-a14a-4a72-bbbe-ad7ec0406dad tags: %% Cell type:markdown id:bd6c2f5f-a14a-4a72-bbbe-ad7ec0406dad tags:
**Question 2.1 -** Nous allons désormais implémenter en Python une structure de données d'automate. Celle-ci doit être en mesure de stocker toutes les informations relatives à la définition d'un automate (symboles reconnu, états, états initiaux/finaux) et valider un mot donné. Votre structure de données peut être composée comme suit : **Question 2.1 -** Nous allons désormais implémenter en Python une structure de données d'automate. Celle-ci doit être en mesure de stocker toutes les informations relatives à la définition d'un automate (symboles reconnu, états, états initiaux/finaux) et valider un mot donné. Votre structure de données peut être composée comme suit :
1. Un constructeur `__init__` qui initialise l'automate avec les symboles du motif (ici `a` et `b`) et les variables d'état interne. En particulier l'état initial. 1. Un constructeur `__init__` qui initialise l'automate avec les symboles du motif (ici `a` et `b`) et les variables d'état interne. En particulier l'état initial.
2. Une méthode `ajout_etat` qui rajoute un nouvel état et s'assure que l'état n'existe pas déjà; un paramètre additionnel `final` indiquera si il s'agit d'un état finaal 2. Une méthode `ajout_etat` qui rajoute un nouvel état et s'assure que l'état n'existe pas déjà; un paramètre additionnel `final` indiquera si il s'agit d'un état finaal
3. Une méthode `ajout_transition` qui rajoute un nouvel état et s'assure que l'état n'existe pas déjà. 3. Une méthode `ajout_transition` qui rajoute une nouvelle transition entre deux états (et s'assure que ces états existent bien et qu'il n'y a pas de transition existante entre ces états).
4. Une méthode `recherche_etat` qui étant donné un état source et un symbole, renvoie l'état correspondant (via la transition correspondant au symbole donné). 4. Une méthode `recherche_etat` qui étant donné un état source et un symbole, renvoie l'état correspondant (via la transition correspondant au symbole donné).
5. Une fonction `run` qui valide un mot donné, et renvoie `True` si l'état final est atteint et `False`. 5. Une fonction `run` qui valide un mot donné, et renvoie `True` si l'état final est atteint et `False`.
%% Cell type:code id:445d2964 tags: %% Cell type:code id:445d2964 tags:
``` python ``` python
class automate: class automate:
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:dd5fe66b-8232-4ab6-94d7-0e49ca591f26 tags: %% Cell type:markdown id:dd5fe66b-8232-4ab6-94d7-0e49ca591f26 tags:
Voici un exemple attendu d'utilisation de votre structure de données : Voici un exemple attendu d'utilisation de votre structure de données :
%% Cell type:code id:51a7c400 tags: %% Cell type:code id:51a7c400 tags:
``` python ``` python
a = automate("ab") a = automate("ab")
a.ajout_etat("0") a.ajout_etat("0")
a.ajout_etat("1", True) a.ajout_etat("1", True)
a.ajout_transition("0", "b", "0") a.ajout_transition("0", "b", "0")
a.ajout_transition("0", "a", "1") a.ajout_transition("0", "a", "1")
a.ajout_transition("1", "a", "1") a.ajout_transition("1", "a", "1")
a.ajout_transition("1", "b", "1") a.ajout_transition("1", "b", "1")
assert a.run("abaaaaa") == True assert a.run("abaaaaa") == True
assert a.run("bbb") == False assert a.run("bbb") == False
``` ```
%% Cell type:markdown id:d16f624a-7810-489c-9b6a-f0bba5eb25f2 tags: %% Cell type:markdown id:d16f624a-7810-489c-9b6a-f0bba5eb25f2 tags:
**Question 2.2 -** Utilisez votre structure de données pour implémenter les automates de la partie précédente. **Question 2.2 -** Utilisez votre structure de données pour implémenter les automates de la partie précédente.
%% Cell type:markdown id:244afe6c-a606-4e1f-b09b-f9dd59882135 tags: %% Cell type:markdown id:244afe6c-a606-4e1f-b09b-f9dd59882135 tags:
Question 1.1 (solution) : Question 1.1 (solution) :
%% Cell type:code id:428cf819-3d60-4776-ba94-3e8733eeadea tags: %% Cell type:code id:428cf819-3d60-4776-ba94-3e8733eeadea tags:
``` python ``` python
# automate qui valide un nombre pair de fois la lettre "a" avec langage a, b # automate qui valide un nombre pair de fois la lettre "a" avec langage a, b
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
# tests valides # tests valides
assert a.run("") == True assert a.run("") == True
assert a.run("aa") == True assert a.run("aa") == True
assert a.run("aaaa") == True assert a.run("aaaa") == True
assert a.run(''.join("a" for i in range(100))) == True assert a.run(''.join("a" for i in range(100))) == True
# tests non-valides # tests non-valides
assert a.run("a") == False assert a.run("a") == False
assert a.run(''.join("a" for i in range(100 + 1))) == False assert a.run(''.join("a" for i in range(100 + 1))) == False
``` ```
%% Cell type:markdown id:64e021b0-4613-444d-87d8-b0abeed5aef2 tags: %% Cell type:markdown id:64e021b0-4613-444d-87d8-b0abeed5aef2 tags:
Question 1.2 (solution) : Question 1.2 (solution) :
%% Cell type:code id:f51ce343-fcdb-49c0-a126-4ffeaf4e1920 tags: %% Cell type:code id:f51ce343-fcdb-49c0-a126-4ffeaf4e1920 tags:
``` python ``` python
# automate qui valide a*a # automate qui valide a*a
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
# tests valides # tests valides
assert a.run("aa") == True assert a.run("aa") == True
assert a.run("aaaa") == True assert a.run("aaaa") == True
assert a.run(''.join("a" for i in range(100))) == True assert a.run(''.join("a" for i in range(100))) == True
# tests non-valides # tests non-valides
assert a.run("") == False assert a.run("") == False
assert a.run("a") == False assert a.run("a") == False
assert a.run("aabb") == False assert a.run("aabb") == False
assert a.run("b") == False assert a.run("b") == False
``` ```
%% Cell type:markdown id:9b44355d-e2a6-4ef2-b427-d5c39cc72662 tags: %% Cell type:markdown id:9b44355d-e2a6-4ef2-b427-d5c39cc72662 tags:
Question 1.3 (solution) : Question 1.3 (solution) :
%% Cell type:code id:6c1a0049-03c3-47b1-adf7-35a59d8c78f7 tags: %% Cell type:code id:6c1a0049-03c3-47b1-adf7-35a59d8c78f7 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:34eb2bb6-aba0-4263-9702-761694af7348 tags: %% Cell type:markdown id:34eb2bb6-aba0-4263-9702-761694af7348 tags:
**Question 2.3 (bonus) -** Implémentez une méthode `visualize(self)` afin d'afficher votre automate en utilisant le code `graphviz` fourni dans les questions précédentes. En voici les principales étapes (il s'agit de construire un graphe..) : **Question 2.3 (bonus) -** Implémentez une méthode `visualize(self)` afin d'afficher votre automate en utilisant le code `graphviz` fourni dans les questions précédentes. En voici les principales étapes (il s'agit de construire un graphe..) :
1. Initialiser les sommes 1. Initialiser les sommes
2. Rajouter les arrêtes 2. Rajouter les arrêtes
3. Inclure les propriétés et labels 3. Inclure les propriétés et labels
%% Cell type:markdown id:954a7d30-9e8a-4269-aee4-e36924da2e86 tags: %% Cell type:markdown id:954a7d30-9e8a-4269-aee4-e36924da2e86 tags:
**Question 2.4 (bonus) -** Implémentez une méthode `__str__` afin que la commande `print(a)` affiche les états internes à l'automate comme ci-dessous : **Question 2.4 (bonus) -** Implémentez une méthode `__str__` afin que la commande `print(a)` affiche les états internes à l'automate comme ci-dessous :
%% Cell type:markdown id:0a4548cc-197f-4c70-a4f9-e3c5c706d378 tags: %% Cell type:markdown id:0a4548cc-197f-4c70-a4f9-e3c5c706d378 tags:
``` ```
automate : automate :
- alphabet : 'ab' - alphabet : 'ab'
- init : 0 - init : 0
- final : ['1'] - final : ['1']
- etats (2) : - etats (2) :
- (0)automate : - (0)automate :
- alphabet : 'ab' - alphabet : 'ab'
- init : 0 - init : 0
- final : ['1'] - final : ['1']
- etats (2) : - etats (2) :
- (0): - (0):
--(b)--> (0) --(b)--> (0)
--(a)--> (1) --(a)--> (1)
- (1): - (1):
--(a)--> (1) --(a)--> (1)
--(b)--> (1) --(b)--> (1)
``` ```
%% Cell type:markdown id:6764667b-ab2b-46c1-a5bb-2a12bb670afd tags: %% Cell type:markdown id:6764667b-ab2b-46c1-a5bb-2a12bb670afd tags:
## Exercice 3 : Analyse de texte avec un automate ## Exercice 3 : Analyse de texte avec un automate
%% Cell type:markdown id:b13e013c-8c73-4008-ac92-cf981b2a5cb0 tags: %% Cell type:markdown id:b13e013c-8c73-4008-ac92-cf981b2a5cb0 tags:
Nous allons maintenant développer un programme qui utilise votre structure de données d'automate implémentée en Python dans la section précédente. L'objectif de ce programme sera le suivant : proposer de compléter un mot, à partir d'une séquence de lettres partielle donnée. Par exemple si votre programme prend en entrée la séquence `bon`, en retour vous devez proposer une séquence de lettres pertinentes afin de compléter ce mot comme `bonjour` ou `bonsoir`. Nous allons maintenant développer un programme qui utilise votre structure de données d'automate implémentée en Python dans la section précédente. L'objectif de ce programme sera le suivant : proposer de compléter un mot, à partir d'une séquence de lettres partielle donnée. Par exemple si votre programme prend en entrée la séquence `bon`, en retour vous devez proposer une séquence de lettres pertinentes afin de compléter ce mot comme `bonjour` ou `bonsoir`.
Vous êtes libres de proposer la stratégie de recommandation de lettres que vous souhaitez. Nous vous proposons de vous baser sur es listes de mots les plus fréquents en Français [ce lien](http://www.pallier.org/extra/liste.de.mots.francais.frgut.txt) (fourni dans le fichier `mots.txt`). Ces mots permettent de réaliser des statistiques de co-occurences. Par exemple, étant donné les mots suivants : Vous êtes libres de proposer la stratégie de recommandation de lettres que vous souhaitez. Nous vous proposons de vous baser sur des listes de mots les plus fréquents en Français [ce lien](http://www.pallier.org/extra/liste.de.mots.francais.frgut.txt) (fourni dans le fichier `mots.txt`). Ces mots permettent de réaliser des statistiques de co-occurences. Par exemple, étant donné les mots suivants :
``` ```
abaissa abaissa
abaissable abaissable
abaissables abaissables
abaissai abaissai
abaissaient abaissaient
abaissais abaissais
abaissait abaissait
abaissâmes abaissâmes
``` ```
Si le mot d'entrée est `abaissa` alors votre programme suggère les lettres suivantes ordonnées par ordre de probabilité de transition pour compléter le mot (basé sur l'analyse du fichier `code/mots-10.txt` qui contient les mots ci-dessus): Si le mot d'entrée est `abaissa` alors votre programme suggère les lettres suivantes ordonnées par ordre de probabilité de transition pour compléter le mot (basé sur l'analyse du fichier `code/mots-10.txt` qui contient les mots ci-dessus):
``` ```
i (4) i (4)
b (2) b (2)
m (1) m (1)
``` ```
Conseils : Conseils :
1. Utiliser les fichiers de listes de mots (`mots.txt`, ..) en analysant la fréquence de co-occurrences de lettres (autrement dit calculer probabilité d'être l'une après l'autre) 1. Utiliser les fichiers de listes de mots (`mots.txt`, ..) en analysant la fréquence de co-occurrences de lettres (autrement dit calculer probabilité d'être l'une après l'autre)
2. Construire un automate dont les transitions sont les probabilités de co-occurence entre les lettres 2. Construire un automate dont les transitions sont les probabilités de co-occurence entre les lettres
3. Proposer une méthode de recommandation de transition à partir de quelques lettres fournies en entrée 3. Proposer une méthode de recommandation de transition à partir de quelques lettres fournies en entrée
%% Cell type:code id:b5309ac0 tags: %% Cell type:code id:b5309ac0 tags:
``` python ``` python
# lecture du fichier de mots # lecture du fichier de mots
data = [] data = []
with open("mots-10.txt") as f: with open("mots-10.txt") as f:
keys = None keys = None
for line in f: for line in f:
l = [w.strip() for w in line.split(';')] l = [w.strip() for w in line.split(';')]
if keys is None: if keys is None:
keys = l keys = l
else: else:
data.append(l) data.append(l)
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:77408003 tags: %% Cell type:markdown id:77408003 tags:
## Pour aller plus loin ## Pour aller plus loin
- Vérifier si les automates sont [déterministes](https://fr.wikipedia.org/wiki/Automate_fini_d%C3%A9terministe) - Vérifier si les automates sont [déterministes](https://fr.wikipedia.org/wiki/Automate_fini_d%C3%A9terministe)
- Comparer vos résultats avec une implémentation [Python d'Automate](https://pypi.org/project/python-automaton/) : - Comparer vos résultats avec une implémentation [Python d'Automate](https://pypi.org/project/python-automaton/) :
- Enregistrer les automates en utilisant la commande - Enregistrer les automates en utilisant la commande
`dot.render('graph', format='svg', view=True)` `dot.render('graph', format='svg', view=True)`
......
TD06/figures_automates/0_1.png

15.7 KiB

TD06/figures_automates/0_1_2.png

25.4 KiB

TD06/figures_automates/0_1_2_3_4.png

43.5 KiB

TD06/figures_automates/0_1_a_b.png

20.5 KiB

TD06/figures_automates/email.png

55.2 KiB

%% Cell type:markdown id:7f210b68 tags:
NAME:
%% Cell type:markdown id:57c2dc5b-0ce7-47e4-bfeb-445c2f90cb6e tags:
# INF TC1 - TD7 (2h) - Rendu de monnaie
%% Cell type:markdown id:2a40aa1b-c3fe-4cd2-9236-3c7aeddedf55 tags:
---
%% Cell type:markdown id:25995ac6-8673-46f2-9c77-37b48a889b73 tags:
## Objectif du TD
Dans ce TD nous allons aborder le problème dit de de _rendu de monnaie_ selon plusieurs méthodes algorithmiques :
- technique de programmation Gloutonne
- chemin minimal dans un arbre de recherche
- Programmation Dynamique (cela fera l'objet du **prochain TD7bis et du devoir à rendre**)
Vous serez amené dans ce TD (et les suivant) à créer une structure de données de graphe et une méthode de parcours de celui-ci.
**Les réponses de la partie sur la programmation dynamique feront l'objet d'un autre sujet de TD et d'un rendu sous forme de rapport à rendre sur Moodle.**
%% Cell type:markdown id:c38c9de3-f716-4907-8387-7cf6f4f5e926 tags:
## Le problème de "rendu de monnaie"
Le problème de rendu de monnaie est très fréquent dans la vie de tous les jours et peut être défini comme suit : étant donné un montant, une machine capable de rendre la monnaie doit rendre ce montant au client à partir de pièces (1c, 2€, etc.) et de billets (10€, 50€, etc.). On suppose pour simplifier qu'il n'y a que des pièces en centimes; un billet de 5€ sera représenté comme une pièce de 500 centimes. On suppose également (dans un premier temps) qu'il existe un nombre suffisant (autant que nécessaire) de chaque pièce, mais dans un second temps nous introduirons des contraintes de disponibilité des pièces.
%% Cell type:markdown id:5a29e913-8954-4e88-b477-234c4ee0fd9b tags:
| | **la table S** | **la table D** |
|---------------|------------------------|----------------------------------|
| **indice $i$**| **Valeur ($v_i$)** | **Disponibilité ($d_i$)** |
| 1 | 1c | nombre de pièces de 1c disponibles |
| 2 | 2c | nombre de pièces de 2c disponibles |
| 3 | 5c | ... |
| 4 | 10c | ... |
| 5 | 20c | ... |
| 6 | 50c | ... |
| 7 | 100 (1€) | pièces de 1€ |
| 8 | 200 (2€) | pièces de 2€ |
| 9 | 500 (5€) | billets de 5€ |
| 10 | 1000 | billets de 10€ |
| 11 | 2000 | billets de 20€ |
| 12 | 5000 | billets de 50€ |
| 13 | 10000 | billets de 100€ |
%% Cell type:markdown id:7f845f7b-8bd0-4544-8110-fa5ec1a7a960 tags:
De manière plus formelle, un stock de pièces est un tuple $S=(v_1, v_2, ..., v_n)$ où l'entier $v_i > 0$ est la valeur de la $i^{ème}$ pièce. Pour refléter le fait qu'on a des pièces de 1, 2 et 5c, $S$ contiendra $v_1=1$ (1 centime), $v_2=2, v_3=5$. Le problème de monnaie est un problème d'optimisation combinatoire $(S,M)$ permettant de trouver le tuple $T=(x_1, x_2, ..., x_n)$ avec $x_i \geq 0$ qui minimise $ \sum_{i=1}^n x_i$ sous la contrainte $\sum_{i=1}^n x_i.v_i=M$. Autrement dit, nous souhaitons aussi bien obtenir le montant exact, que minimiser le nombre total de pièces $x_i$ de valeur $v_i$ utilisées. Appelons $Q(S,M) = \sum_{i=1}^n x_i$ la quantité de pièces à rendre pour le montant *M* étant donné le système *S* décrit dans la Table~1. Une solution optimale $Q_{opt}$ à ce problème est telle que *Q(S,M)* soit minimale :
$Q_{opt}(S,M) = min \ \sum_{i=1}^n x_i$.
Dans certaines situations il faudra gérer le nombre de pièces/billets disponibles (la table *D*). Nous noterons *d[i]=k* pour dire : il y a *k* pièces/billets du montant *$v_i$* disponibles (pièces ou billets du montant *v[i]*) à l'indice *i* dans la table *S*. On supposera cependant dans un premier temps qu'il y a un nombre suffisant de chaque pièce/billet dans le tableau S. On supposera également que *S* est ordonné dans un ordre croissant.
%% Cell type:markdown id:077ca702-bc18-4f2d-95e6-86fd4d53ebe7 tags:
## Exemples
**M = 9€:** étant donné S dans la Table ci-dessous, la solution qui minimise le nombre total de pièces rendues à 3 est *T=(0,0,0,0,0,0,0,2,1,0,0,0,0)*. Donc, $Q_{opt}(S,9) = \min \ Q(S,9) = 3$. Détails (avec des pièces $\geq$ 1€) :
| Description | T | Rendu |
|--------------------------------------------------------|----------------------------------------|---------------------------------------------|
| 9 pièces de 1€ et 0 pour toutes les autres | T=(0,0,0,0,0,0,0,9,0,0,0...0) | $\rightarrow$ 9 pièces | $Q(S,9) =9$ |
| $\circ$ 5 $\times$ 1€ + 2 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,5,2,0,0...0) | $\rightarrow$ 7 pièces | $Q(S,9) =7$ |
| $\circ$ 1 $\times$ 1€ + 4 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,1,4,0,0...0) | $\rightarrow$ 5 pièces | $Q(S,9) =5$ |
| $\circ$ 2 $\times$ 2€ + 1 $\times$ 5€, 0 pour les autres | T=(0,0,0,0,0,0,0,0,2,1,0...0) | $\rightarrow$ 3 pièces | **$Q(S,9) =3$** |
| $\circ$ 3 $\times$ 1 + 3 $\times$ 2, 0 pour les autres | T=(0,0,0,0,0,0,0,3,3,0,0...0) | $\rightarrow$ 6 pièces | |
| $\circ$ 4 $\times$ 1 + 1 $\times$ 5, 0 pour les autres | T=(0,0,0,0,0,0,0,4,0,1,0...0) | $\rightarrow$ 5 pièces | |
| $\circ$ etc. sans parler des solutions avec des centimes ! | | | |
<center>
<i>Table 1</i>
</center>
**M = 1989€:** pour rendre la somme de 1989€ pièces (sans les centimes), on aura : $1989 = 500 \times 3 + 488 = 500 \times 3 + 200 \times 2 + 50 \times 1 + 20 \times 1 + 10 \times 1 + 5 \times 1 + 2 \times 2$, soit $3+2+1+1+1 = 8$ grosses pièces (billets) et $1+2 = 3$ pièces.
%% Cell type:markdown id:254f7b95-aaeb-4f96-b8ec-47df74a3c3a3 tags:
## Résolution du problème
L'exemple de la Table 1 est un système *canonique*, c'est à dire qu'en choisissant systématiquement les pièces de plus grande valeur (algorithme glouton) on obtient toujours la solution optimale. Il existe des systèmes pour lesquels c'est moins simple, par exemple $S=(1,7,23)$. Pour **M = 28**, en choisissant en priorité les pièces de plus grande valeur on trouvera $T=(5,0,1)$ (6 pièces), alors que la solution optimale est $T=(0,4,0)$ (4 pièces).
Dans le cas général, ce problème est démontré NP-difficile, c'est-à-dire qu'on ne connaît pas d'algorithme qui puisse le résoudre en complexité polynomiale par rapport à la taille de $S$.
Il existe plusieurs approches :
1. Approche gloutonne (pas toujours optimale)
2. Recherche de chemin de longueur minimale avec (ou sans) un arbre de recherche, ...
3. Programmation Dynamique, ... et autres méthodes algorithmiques (cf. quasi-Dijkstra)
Il existe d'autres approches de résolutions algorithmiques ou non-algorithmiques comme par exemple un système d'équations à optimiser. Nous allons dans ce TD aborder les différentes approches algorithmiques mentionnées ci-dessus.
%% Cell type:markdown id:c8b2e4fe-cbfa-4301-89af-d02ea55473f0 tags:
## 1. Algorithme Glouton
Les techniques de programmation gloutonnes ont la particularité de faire des choix locaux à chaque étape de calcul. Cette méthode ne donne pas forcément un nombre minimal de pièces mais elle est simple à comprendre et à implémenter. Son application au problème de rendu de monnaie est simple : on trouve la pièce la plus grande inférieure ou égale à $M$. Soit $v_i$ cette pièce. On utilise ($x_i = M \ div \ v_i$) fois la pièce $v_i$; le reste à traiter sera $M'=(M \mod v_i)$. Ensuite, on recommence suivant le même principe pour satisfaire $M'$. Ce qui donne le pseudo-code d'algorithme suivant (qui fait l'hypothèse d'un nombre illimité de pièces, on ne tient pas compte ici de $D$ : $d_i=\infty$) :
```
Fonction Monnaie_Gloutonne
Entrées : la somme S, M
Sorties : le vecteur T = Q(S,M) : le nombre de pièces nécessaires
M'=M
Répéter
Chercher dans S l'indice i tel que Vi =< M'
Ti = M' div Vi
M' = M' mod Si
Jusqu'à M' = 0
Constituer T avec les Ti utilisés
Q(S,M) = somme de i=1 a i=n de T_i
T est la valeur de sortie de l'algorithme
Fin Monnaie_Gloutonne
```
Pour **M = 236,65€** cet algorithme se déroule de la manière suivante :
$23665 \geq 10000$
- $T_{13}=23665 \ div \ 10000 = 2$ ($T_{13}$ correspond à 100€)
- $M'=23665 \ mod \ 10000 = 3665$
$3665 \geq 2000$
- $T_{11}=3665 \ div \ 2000 = 1$
- $M'=3665 \ mod \ 2000 = 1665$
...
$\rightarrow$ On obtient $T_{1..13}=(0,0,1,1,0,1,1,0,1,1,1,0,2)$ et $Q(S,M) = 9$
A noter qu'en Python les indices commencent à zéro ! Faire -1 sur les indices ci-dessus.
%% Cell type:markdown id:425fe58a-b0bc-41eb-acea-0d88e1daf18f tags:
**Question 1.1 -** Implémenter l'algorithme glouton ci-dessus.
%% Cell type:code id:e3880c0c-4c2c-48c4-96b1-0c95c712b489 tags:
``` python
def gloutonne(S: list, M: int) -> tuple:
"""
Algorithme glouton sans nombre limité de pièces
Args:
- S (list): Une liste d'entier représentant les pièces
- M (int): La somme à atteindre
Returns:
- tuple: (True, Q(S,M)) si succès OU (False, None) si échec
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:2a39cbfb-d35d-4da7-bbaa-d273bc900c2d tags:
Les tests suivants doivent être validés :
%% Cell type:code id:90cd609d-71d9-41b0-ba28-c66fd94a2524 tags:
``` python
Rep = gloutonne(sorted([1, 7, 23], reverse=True), 28)
print(Rep[1], ': ', sum(Rep[1]),' pièces') if Rep[0] else print('Echec')
# [28, 0, 0] : 28 pièces
Rep = gloutonne(sorted([7, 23], reverse=True), 5)
print(Rep[1], ': ', sum(Rep[1]),' pièces') if Rep[0] else print('Echec')
# Echec car M < la + petite pièce
Rep = gloutonne(sorted([7, 23], reverse=True), 8)
print(Rep[1], ': ', sum(Rep[1]),' pièces') if Rep[0] else print('Echec')
# Echec car on ne peut jamais faire la monnaie avec cette S
```
%% Cell type:markdown id:c5d61f32-13cc-4176-8f47-b0ca861a74ba tags:
**Question 1.2 -** Proposez une modification du pseudo-code ci-dessus pour prendre en compte un nombre **limité** de pièces (en prenant en compte la table $D$). Implémenter cet algorithme modifié (vous fournirez vos propres valeurs de disponibilité dans $D$).
%% Cell type:code id:91951f93-bf34-4ea9-ad08-76c73ff3ce44 tags:
``` python
def gloutonne_avec_D(S: list, M: int, D: list) -> tuple:
"""
Algorithme glouton avec nombre limité de pièces
Args:
- S (list): Une liste d'entier représentant les pièces
- M (int): La somme à atteindre
- D (list): La disponibilité des pièces
Returns:
- tuple: (True, Q(S,M)) si succès OU (False, None) si échec
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:9786ca50-6bfe-491d-8971-89be91ccfd8b tags:
Les tests suivants doivent être validés :
%% Cell type:code id:a09ba678-bff6-4728-b301-6579f8a62e40 tags:
``` python
Rep= gloutonne_avec_D([23, 7, 1], 28, [0, 10, 10]) # On ne fait pas de tri car il faudrait trier D aussi
print(Rep[1], ': ', sum(Rep[1]),' pièces utilisées') if Rep[0] else print('Echec')
# Echec
Rep= gloutonne_avec_D([23, 7, 1], 28, [0, 1, 21])
print(Rep[1], ': ', sum(Rep[1]),' pièces utilisées') if Rep[0] else print('Echec')
# [21, 1, 0] : 22 pièces utilisées
```
%% Cell type:markdown id:70534a4f-cc60-4c29-b5e6-1392126fc9f5 tags:
Vous noterez que la méthode Gloutonne ne garantit pas que *Q(S, M)* soit minimal comme les tests l'ont montré pour M=28 et S=(1, 7, 23). La méthode Gloutonne utilise un optimum local (choix de la plus grande pièce) qui ne débouche pas forcément sur un optimum global. Cependant, elle est très simple à calculer.
%% Cell type:markdown id:321b284b-a735-47b5-a889-ef52006c967e tags:
## 2. Chemin minimal dans un arbre
Une deuxième méthode consiste à construire toutes les solutions possibles sous forme d'arbre, et ensuite de choisir la solution de manière globale. Cette méthode est plus complexe à mettre en oeuvre mais donne une solution optimale.
%% Cell type:markdown id:431ee4d7-1fa8-4bfd-bf9c-ec0725d08e5b tags:
<center>
<img src="monnaie-graph.png" alt="Image originale" style="height:10cm;">
</center>
Exemple d'arbre de recherche résultant *M=28* et *S=(1,7,23)*. C'est à dire, on a seulement des pièces de 1, 7 et 23 (€ ou cents) en nombre suffisant. Dès qu'on atteint 0, on remonte de ce 0 à la racine pour obtenir la solution minimale donnant le nombre minimal d'arcs entre ce 0 et la racine. On remarque qu'on utilisera 4 pièces de 7c pour avoir 28c. Par ailleurs, on remarque que le choix initial de 23 (à gauche de l'arbre) laisse le montant *M'=5c* à satisfaire lequel impose le choix de 5 pièces de 1c. En bleu les autres chemins choisissant une pièce de 23.
%% Cell type:markdown id:36bc9ae5-81f2-4f12-9b94-03d90db050c0 tags:
On construit un arbre de recherche (arbre des possibilités) dont la racine est *M*. Chaque noeud de l'arbre représente un montant : les noeuds autres que la racine initiale représentent $M' < M$ une fois qu'on aura utilisé une (seule) des pièces de $S$ (parmi 1, 7 ou 23€). La pièce utilisée pour aller de $M$ à $M'$ sera la valeur de l'arc reliant ces deux montants. Cet arbre est donc développé par niveau (en largeur). On arrête de développer un niveau supplémentaire dès qu'un noeud a atteint 0 auquel cas une solution sera le vecteur des valeurs (des arcs) allant de la racine à ce noeud.
Le principe de l'algorithme correspondant à un parcours en largeur dont la version itérative utilise une *File d'attente* est :
```
Fonction Monnaie_graphe
% on suppose qu'il y a un nombre suffisant de chaque pièce/billet dans la tableau S
Entrées : la somme M
Sorties : le vecteur T et Qopt(S,M) le nombre de pièces nécessaires
File F = vide ;
Arbre A contenant un noeud racine dont la valeur = M
Enfiler(M) % la file F contient initialement M
Répéter
M'= défiler()
Pour chaque pièce vi =< M' disponible dans S :
S'il existe dans l'arbre A un noeud dont la valeur est M' - vi
Alors établir un arc étiqueté par vi allant de M' à ce noeud
Sinon
Créer un nouveau noeud de valeur M' - vi dans A et lier ce noeud
à M' par un arc étiqueté par vi
Enfiler ce nouveau noeud
Fin Si
Jusqu'à (M' - vi = 0) ou (F = vide)
Si (F est vide Et M' - vi /= 0)
Alors il y a un problème dans les calculs ! STOP.
Sinon Le dernier noeud créer porte la valeur 0
On remonte de ce noeud à la racine et on comptabilise dans T où
Ti = le nombre d'occurrences des arcs étiquetés vi de la racine
au noeud v de valeur 0
$Q(S,M) = somme de i=1 a i=n de T_i$
Fin si
Fin Monnaie_graphe
```
%% Cell type:markdown id:d15c2fca-64f1-4d6d-b6bf-5edf9b095828 tags:
**Question 2.1 -** Ecrire un algorithme de construction de l'arbre.
%% Cell type:code id:2d2556ed-8be9-416f-a976-8964ccca8061 tags:
``` python
def Monnaie_graphe(S: list, M: int) -> dict:
"""
Génère un graphe représentant les combinaisons de pièces possibles pour atteindre un montant spécifié
en utilisant un ensemble donné de valeurs de pièces.
Args:
- S (list): Une liste d'entiers représentant les valeurs disponibles des pièces.
- M (int): Un entier spécifiant le montant cible à atteindre.
Returns:
- dict: Un graphe (représenté sous forme de dictionnaire)
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:c2709c24-bbe0-4c6a-aaf8-f2e3712131f5 tags:
``` python
A = Monnaie_graphe([1, 2, 5], 7)
print("Arbre créé", A)
```
%% Cell type:markdown id:91d7262d-9a9c-4f22-b60e-6d43267a59bb tags:
Vous devriez pouvoir visualiser votre arbre avec la méthode `plot_graph` (chargez d'abord les dernières cellules du notebook afin d'initialiser cette fonction) :
%% Cell type:code id:701a7359-145f-4077-a8ee-e056eb3447f1 tags:
``` python
plot_graph(A)
```
%% Cell type:markdown id:9de0a303-b587-4901-b0ac-68a7cd04bfc7 tags:
**Question 2.2 -** Ecrire un algorithme de recherche du chemin le plus court dans l'arbre.
%% Cell type:code id:f5f033d8-1d49-49c7-9af7-e1cd59905d96 tags:
``` python
def plus_court_chemin(graph: dict, start: str) -> int:
"""
Calcule les distances les plus courtes depuis un nœud de départ dans un graphe.
Args:
- graph (dict): Un dictionnaire représentant le graphe
- start (str): Le nœud de départ à partir duquel calculer les distances.
Returns:
- int: la taille du chemin
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:7aca69d8-79ed-4f06-bb25-5c42b116cbc8 tags:
``` python
assert plus_court_chemin(A, 3)[0] == 2
```
%% Cell type:markdown id:72832556-e0d7-4959-8d93-2b55145e7c25 tags:
**Question 2.3 -** Compléter l'algorithme précédent avec un mécanisme de backtracking permettant de retourner le plus court chemin utilisé.
%% Cell type:code id:636ed054-38e5-46d1-b44f-c2d861bf1cc0 tags:
``` python
def plus_court_chemin_backtracking(graph: dict, start: str) -> list:
"""
Calcule les distances les plus courtes depuis un nœud de départ dans un graphe.
Args:
- graph (dict): Un dictionnaire représentant le graphe
- start (str): Le nœud de départ à partir duquel calculer les distances.
Returns:
- list: le chemin utilisé et les montants intermédiaires
"""
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:code id:b0253f7c-0f07-436c-b72c-2411fb757e47 tags:
``` python
assert plus_court_chemin_backtracking(A, 7) == [7, 5, 0]
```
%% Cell type:markdown id:f358da32-9534-493c-80b1-60d387f89229 tags:
A noter que vous pouvez combiner la construction et la recherche du plus court chemin (en vous arrêtant au premier 0 rencontré). Cependant, cette methode a pour inconvénient d'explorer un très large espace de recherche (qui devient vite très grand). Un autre inconvénient est le calcul de solutions dont on sait qu'elles ne seront pas optimales.
%% Cell type:markdown id:91af94f8-c687-4739-b4fb-ef58cb7e6081 tags:
## Utils
%% Cell type:code id:7cbf74a0-525c-4b6f-b078-5f0eea6fe09d tags:
``` python
from graphviz import Digraph
from IPython.display import display, Image
import graphviz
```
%% Cell type:code id:83f489e1-8fb7-45ec-a906-4d2699c63a46 tags:
``` python
def plot_graph(graph):
dot = graphviz.Digraph()
for node in graph:
dot.node(str(node))
for node, neighbors in graph.items():
for neighbor, weight in neighbors.items():
dot.edge(str(node), str(neighbor), label=str(weight))
display(dot)
```
TD07/monnaie-graph.png

69.5 KiB

TD07/monnaie-progdyn.png

67.4 KiB

%% Cell type:markdown id:2c7e0986 tags:
NAME:
%% Cell type:markdown id:57c2dc5b-0ce7-47e4-bfeb-445c2f90cb6e tags:
# INF TC1 - TD7bis (2h + 2h AUTO) - Rendu du Rendu de monnaie
%% Cell type:markdown id:2a40aa1b-c3fe-4cd2-9236-3c7aeddedf55 tags:
---
%% Cell type:markdown id:25995ac6-8673-46f2-9c77-37b48a889b73 tags:
## Objectif du TD
Dans ce rendu de TD est d'aborder le problème de rendu de monnaie selon la méthode de **Programmation Dynamique**.
**Vos réponses à ce TD fera l'objet d'un rendu à [déposer sur Moodle](https://pedagogie1.ec-lyon.fr/course/view.php?id=1865).**
## Modalités de rendu du TD
Le rendu sera à déposer sur Moodle une (1) semaine après le dernier TD d'autonomie. Il pourra être réalisé seul ou en binôme. Ce rendu devra comporter :
- Les réponses aux questions de la partie 3 sous forme de notebook ou autre document.
- Un code fonctionnel et les tests appropriés (avec `assert` par exemple) sous forme de notebook ou de fichier Pythons classiques.
- De **nombreux** tests avec plusieurs systèmes de monnaies (canoniques et non canoniques) démontrant l'efficacité de votre approche. **Pensez également à inclure des tests démontrant les limites des solutions que vous proposez.** Pensez à rajouter les types de variables et commentaires dans les fonctions.
- La description de parties de code difficiles (dans le code ou dans des cellules supplémentaires).
- Tout soucis ou point bloquant dans votre code.
- Les diagrammes, exemples et illustration nécessaires (dans ce cas vous devrez créer une archive comprenant les fichiers de code, notebook et les fichiers d'illustration).
- Tout élément et exemple supplémentaire que vous jugerez nécessaires.
%% Cell type:markdown id:1fcb1349-4cae-4f76-b3b6-d9a810225afe tags:
NOM(s) et PRENOM(s) :
Groupe de TD :
%% Cell type:markdown id:c38c9de3-f716-4907-8387-7cf6f4f5e926 tags:
## Rappel: Le problème de "rendu de monnaie"
Le problème de rendu de monnaie est très fréquent dans la vie de tous les jours et peut être défini comme suit : étant donné un montant, une machine capable de rendre la monnaie doit rendre ce montant au client à partir de pièces (1c à 2€) et de billets. On suppose pour simplifier qu'il n'y a que des pièces en centimes; un billet de 5€ sera représenté comme une pièce de 500 centimes. On supposera dans un premier temps qu'il existe un nombre suffisant (autant que nécessaire) de chaque pièce, mais dans un second temps nous introduirons des contraintes de disponibilité des pièces.
%% Cell type:markdown id:5a29e913-8954-4e88-b477-234c4ee0fd9b tags:
| | **la table S** | **la table D** |
|---------------|------------------------|----------------------------------|
| **indice $i$**| **Valeur ($v_i$)** | **Disponibilité ($d_i$)** |
| 1 | 1c | nombre de pièces de 1c disponibles |
| 2 | 2c | nombre de pièces de 2c disponibles |
| 3 | 5c | ... |
| 4 | 10c | ... |
| 5 | 20c | ... |
| 6 | 50c | ... |
| 7 | 100 (1€) | pièces de 1€ |
| 8 | 200 (2€) | pièces de 2€ |
| 9 | 500 (5€) | billets de 5€ |
| 10 | 1000 | billets de 10€ |
| 11 | 2000 | billets de 20€ |
| 12 | 5000 | billets de 50€ |
| 13 | 10000 | billets de 100€ |
%% Cell type:markdown id:7f845f7b-8bd0-4544-8110-fa5ec1a7a960 tags:
De manière plus formelle, un stock de pièces est un tuple $S=(v_1, v_2, ..., v_n)$ où l'entier $v_i > 0$ est la valeur de la $i^{ème}$ pièce. Pour refléter le fait qu'on a des pièces de 1, 2 et 5c, $S$ contiendra $v_1=1$ (1 centime), $v_2=2, v_3=5$. Le problème de monnaie est un problème d'optimisation combinatoire $(S,M)$ permettant de trouver le tuple $T=(x_1, x_2, ..., x_n)$ avec $x_i \geq 0$ qui minimise $ \sum_{i=1}^n x_i$ sous la contrainte $\sum_{i=1}^n x_i.v_i=M$. Autrement dit, nous souhaitons aussi bien obtenir le montant exact, que minimiser le nombre total de pièces $x_i$ de valeur $v_i$ utilisées. Appelons $Q(S,M) = \sum_{i=1}^n x_i$ la quantité de pièces à rendre pour le montant *M* étant donné le système *S* décrit dans la Table~1. Une solution optimale $Q_{opt}$ à ce problème est telle que *Q(S,M)* soit minimale :
$Q_{opt}(S,M) = min \ \sum_{i=1}^n x_i$.
Dans certaines situations il faudra gérer le nombre de pièces/billets disponibles (la table *D*). Nous noterons *d[i]=k* pour dire : il y a *k* pièces/billets du montant *$v_i$* disponibles (pièces ou billets du montant *v[i]*) à l'indice *i* dans la table *S*. On supposera cependant dans un premier temps qu'il y a un nombre suffisant de chaque pièce/billet dans le tableau S. On supposera également que *S* est ordonné dans un ordre croissant.
%% Cell type:markdown id:077ca702-bc18-4f2d-95e6-86fd4d53ebe7 tags:
## Exemples
**M = 9€:** étant donné S dans la Table ci-dessous, la solution qui minimise le nombre total de pièces rendues à 3 est *T=(0,0,0,0,0,0,0,2,1,0,0,0,0)*. Donc, $Q_{opt}(S,9) = \min \ Q(S,9) = 3$. Détails (avec des pièces $\geq$ 1€) :
| Description | T | Rendu |
|--------------------------------------------------------|----------------------------------------|---------------------------------------------|
| 9 pièces de 1€ et 0 pour toutes les autres | T=(0,0,0,0,0,0,0,9,0,0,0...0) | $\rightarrow$ 9 pièces | $Q(S,9) =9$ |
| $\circ$ 5 $\times$ 1€ + 2 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,5,2,0,0...0) | $\rightarrow$ 7 pièces | $Q(S,9) =7$ |
| $\circ$ 1 $\times$ 1€ + 4 $\times$ 2€, 0 pour les autres | T=(0,0,0,0,0,0,0,1,4,0,0...0) | $\rightarrow$ 5 pièces | $Q(S,9) =5$ |
| $\circ$ 2 $\times$ 2€ + 1 $\times$ 5€, 0 pour les autres | T=(0,0,0,0,0,0,0,0,2,1,0...0) | $\rightarrow$ 3 pièces | **$Q(S,9) =3$** |
| $\circ$ 3 $\times$ 1 + 3 $\times$ 2, 0 pour les autres | T=(0,0,0,0,0,0,0,3,3,0,0...0) | $\rightarrow$ 6 pièces | |
| $\circ$ 4 $\times$ 1 + 1 $\times$ 5, 0 pour les autres | T=(0,0,0,0,0,0,0,4,0,1,0...0) | $\rightarrow$ 5 pièces | |
| $\circ$ etc. sans parler des solutions avec des centimes ! | | | |
<center>
<i>Table 1</i>
</center>
**M = 1989€:** pour rendre la somme de 1989€ pièces (sans les centimes), on aura : $1989 = 500 \times 3 + 488 = 500 \times 3 + 200 \times 2 + 50 \times 1 + 20 \times 1 + 10 \times 1 + 5 \times 1 + 2 \times 2$, soit $3+2+1+1+1 = 8$ grosses pièces (billets) et $1+2 = 3$ pièces.
%% Cell type:markdown id:6c6aef6c-c6b7-4376-a0dd-06346df476df tags:
## 3. Algorithme de Programmation Dynamique (Rendu du TD)
Nous introduisons une troisième et dernière méthode de résolution qui se base sur la programmation dynamique dont les principes sont :
1) identifier une formule récursive pour résoudre le problème de façon incrémentale,
2) résoudre le problème pour des conditions aux bords,
3) itérer pour résoudre le problème complet.
Supposons que l'on puisse, pour le montant *M*, savoir calculer une solution optimale pour tout montant $M' < M$. Pour satisfaire *M*, il faudra alors prendre une (seule) pièce $v_i$ supplémentaire parmi les *n* pièces disponibles. Une fois cette pièce choisie, le reste $M' = M-v_i$ est forcément inférieur à *M* et on sait qu'on peut calculer un nombre optimal de pièces pour *M'*. Par conséquent :
%% Cell type:markdown id:53272a7f-ff93-4143-83cd-f33e1750b0b2 tags:
$Q_{opt}(i,m) = min
\begin{cases}
1 + Q_{opt}(i, m - v_i) \quad si \ (m - v_i) \geq 0 \qquad \text{ on utilise une pièce de type i de
valeur $v_i$}\\
Q_{opt}(i-1, m) \qquad \quad si \ i \geq 1 \qquad \qquad \quad \text{ on n'utilise pas la pièce de type i,
essayons i-1}
\end{cases}$
L'inconvénient de cette méthode est que chaque appel à $Q_{opt}$ fait deux appels à lui-même, donc le nombre d'opérations nécessaires est exponentiel à la taille de *M*. Pour éviter cela ferons appel au principe de *mémoïsation* de la programmation dynamique, en stockant les résultats intermédiaires dans une matrice $mat[|S|][M]$ (Figure 2).
<center>
<img src="monnaie-progdyn.png" alt="Image originale" style="height:5cm;">
</center>
<center>
<i>Figure 2</i>
</center>
Illustration de la résolution par programmation dynamique. L'ordre de remplissage des cellules (de haut en bas, de gauche à droite) est représenté en vert.
Les colonnes de la matrice sont les valeurs de M qu'on doit atteindre. Les lignes sont les pièces dont on dispose pour atteindre chaque valeur de M. En première ligne, on ne dispose d'aucune pièce, en ligne 2 on dispose d'une infinité de pièces de 1c, en ligne 3 on dispose d'une infinité de pièces de 1c et de 3c, etc. Les cellules de la matrice indiquent le nombre minimal de pièces à utiliser parmi celles autorisées par la ligne courante, pour atteindre la valeur en colonne. Ainsi on peut lire dans la matrice que pour payer 6 centimes avec des pièces de 1, 3 et 4 il faut utiliser 2 pièces (2 $\times$ 3c). Le remplissage de chaque cellule se fait en calculant le minimum de deux voisins (illustrés avec des flèches rouges). Si un voisin est hors de la matrice, il ne compte pas dans le calcul du minimum. Le pseudo-code de construction de la matrice peut s'écrire de la façon suivante :
```
fonction Progdyn(S, M) : S est le stock des pièces, M est le montant
soit mat la matrice d'indices [0, |S|] x [0 , M]
pour i = 0 à |S| faire
pour m = 0 à M faire
si m = 0 alors
mat[i][m] = 0
sinon si i = 0 alors
mat[i][m] = infini
sinon
mat[i][m] = min(
si m - S[i-1] >= 0 alors 1 + mat[i][m - S[i-1]] sinon infini
si i >= 1 alors mat[i-1][m] sinon infini
)
renvoyer mat [|S|][M]
```
%% Cell type:markdown id:1287c1b0-4900-4b18-ab59-5719f867e18d tags:
**Question 3.1 -** Proposer une solution Python de cette méthode. Dans une première étape, trouver simplement le nombre minimal de pièces. Renvoyer `None` si il n'existe pas de solution possible.
%% Cell type:code id:fc6d6123-9d62-4d58-a9c7-dd0540c77d9c tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:90172789-470c-452d-96ff-8adc56d8c134 tags:
**Question 3.2 -** Modifier votre solution pour renvoyer également les pièces utilisées.
%% Cell type:code id:51354054-3292-48c6-8d43-8f42ffd11127 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:d544f60e-b75c-4b8f-a695-7b9ec7bd138b tags:
**Question 3.3 -** On souhaite à présent implémenter en Programmation Dynamique la limite du stock de pièces présentée pour les exercices 1.2 et 1.3. Pour ce faire on modifie la formule de $Q_{opt}$ donnée précédemment (valable ici uniquement pour $i\geq1$) :
$Q_{opt}(i,m) = min
\begin{cases}
Q_{opt}(i-1, m) \qquad \qquad \qquad \qquad \qquad \qquad \qquad \text{ on n'utilise aucune pièce de type i}\\
1 + Q_{opt}(i-1, m-v_i) \quad si\ d_i\geq1\ et\ (m-v_i) \geq 0 \qquad \text{ on utilise 1 pièce de type i}\\
2 + Q_{opt}(i-1, m-2v_i) \quad si\ d_i\geq2\ et\ (m-2v_i) \geq 0 \quad \text{ on utilise 2 pièces de type i}\\
3 + Q_{opt}(i-1, m-3v_i) \quad si\ d_i\geq3\ et\ (m-3v_i) \geq 0 \quad \text{ on utilise 3 pièces de type i}\\
...
\end{cases}
$
Implémentez la limite de pièces avec cette nouvelle formule pour l'algorithme en Programmation Dynamique. Notez ici qu'on ne va plus chercher les valeurs sur la même ligne dans la matrice, mais qu'on remonte _systématiquement_ d'une ligne, pour qu'après l'utilisation de $k$ pièces de valeur $v_i$, on ne puisse plus ajouter d'autres pièces de cette même valeur.
%% Cell type:code id:8c05a462-3d75-48be-903c-3fa922bbd63d tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:5ee21bfb-c169-48d0-93cc-75c6be089d03 tags:
**Question 3.4 -** On dispose à présent du poids de chaque pièce en plus de sa valeur (Table 2), et on cherche à minimiser le poids total des pièces pour atteindre la valeur M. À l'aide d'une matrice analogue à la Figure 2, trouvez le poids minimal pour atteindre la valeur M=7. Notez qu'on ne cherchera plus à minimiser le nombre de pièces utilisées, seul le critère de poids compte pour cet exercice.
| S (valeur de chaque pièce) | P (poids de chaque pièce) |
|---------------------------|---------------------------|
| 1c | 2.30g |
| 2c | 3.06g |
| 5c | 3.92g |
| 10c | 4.10g |
| 20c | 5.74g |
| 50c | 7.80g |
| 100 (1€) | 7.50g |
| 200 (2€) | 8.50g |
| 500 (5€) | 0.6g |
| 1000 | 0.7g |
| 2000 | 0.8g |
| 5000 | 0.9g |
| 10000 | 1g |
<center>
<i>Table 2</i>
</center>
%% Cell type:code id:3ecdadb6-b735-47cd-ba9e-d79abb285648 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:b48169e9-ada6-4f03-a24c-28745caa99bd tags:
**Question 3.5 -** Implémentez à présent un algorithme de Programmation Dynamique qui calcule le poids minimal pour atteindre une valeur M. Étant donné qu'avec le système monétaire en Table 2 la minimisation du nombre de pièces minimise aussi le poids, vous pourrez tester votre algorithme avec un système plus complexe tel que donné en Table 3. Essayez par exemple avec la valeur $M=6$.
| S (valeur de chaque pièce) | P (poids de chaque pièce) |
|---------------------------|---------------------------|
| 1c | 10g |
| 3c | 27g |
| 4c | 32g |
| 7c | 55g |
<center>
<i>Table 3</i>
</center>
%% Cell type:code id:6e72a851-5698-4cd8-85ae-b830b8469ea9 tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
%% Cell type:markdown id:d1970f87-775a-4374-9947-2ae1cff39418 tags:
**Question 3.6 -** On propose l'algorithme ci-dessous qui tente de résoudre le problème de façon gloutonne. Implémentez cet algorithme. Pour quelles valeurs de M ($\leq$ 20) avec la Table 3 donne-t-il une solution différente de l'algorithme à Programmation Dynamique ?
```
Fonction Poids_Gloutonne
Entrées : liste S, liste P, entier M
Sortie : poids minimal pour atteindre M
Soit L la liste des tuples (Pi/Si, Si, Pi), triée croissante selon le premier
élément de chaque tuple
M'=M
res = 0
Répéter
Chercher dans L le premier triplet (r, s, p) tel que s <= M'
res = res + p * (M' // s)
M' = M' % s
Jusqu'à M' = 0
res est la valeur de sortie de l'algorithme
Fin Poids_Gloutonne
```
%% Cell type:code id:b7f93bbc-c1d7-4de4-8470-961a68050d9e tags:
``` python
# YOUR CODE HERE
raise NotImplementedError()
```
TD07bis/monnaie-graph.png

69.5 KiB

TD07bis/monnaie-progdyn.png

67.4 KiB