From 1281027e971cefedd49f830e7462f4e91d571592 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Tue, 29 Jun 2021 12:59:14 +0000 Subject: [PATCH] Supprimer 'FOAD-bloc5.md' --- FOAD-bloc5.md | 325 -------------------------------------------------- 1 file changed, 325 deletions(-) delete mode 100644 FOAD-bloc5.md diff --git a/FOAD-bloc5.md b/FOAD-bloc5.md deleted file mode 100644 index dadfe6a..0000000 --- a/FOAD-bloc5.md +++ /dev/null @@ -1,325 +0,0 @@ -# Recherche du plus grand carré blanc dans une "image" - -## Objectif et déroulement - -On considère le problème suivant : étant donné une image monochrome $n \times n$, déterminer le plus grand carré qui ne contient aucun point noir. - -Le but du TP est d'établir plusieurs algorithmes permettant de trouver le plus grand carré blanc dans une image. - -Déroulement du TP : - -- Conception d'un algorithme récursif naïf : ```RechercheCarreBlanc_naif()``` -- Programmation dynamique - Utilisation de la mémoïsation : ```RechercheCarreBlanc_memoisation()``` -- Programmation dynamique - Optimisation par "Bottom_up" : ```RechercheCarreBlanc_BU()``` - -# Conception d'un algorithme récursif naïf : ```RechercheCarreBlanc_naif()``` - -## Une image c'est quoi ? - -Une image, c'est un tableau de tuples qui représentent les couleurs des pixels. Dans le cadre de ce TP, nous nous limiterons à des images noir et blanc : - -![Image N/B](1.png) - -```python -t=[ - [1,0,0,1,0,0,1,0,0,1], - [0,0,0,0,0,0,1,0,0,0], - ... - [0,0,0,0,1,0,0,0,1,0] -] -``` - -```t[5][1]==1``` car le pixel de la ligne ```5``` et de la colonne ```1``` est noir. (Attention les numéros des lignes/colonnes commencent à 0). - -## Etude du problème - -L'objectif est de trouver le carré blanc le plus grand possible à partir d'un pixel donné (coin inférieur droit). - -- ```PlusGrandCarreBlanc_Rec(lig,col)``` : Retourne la taille du carré blanc à partir du coin inférieur droit (ligne,colonne). - -Par ex : - -- ```PlusGrandCarreBlanc_Rec(1,2) == 2``` -- ```PlusGrandCarreBlanc_Rec(6,4) == 2``` - -![Image N/B](2.png) - -> -> 01. Pour l'image suivante, donner le résultat de : -> -> - ```PlusGrandCarreBlanc_Rec(2,1) == ...``` -> - ```PlusGrandCarreBlanc_Rec(1,2) == ...``` -> - ```PlusGrandCarreBlanc_Rec(1,1) == ...``` -> ->![Image N/B](3.png) -> -> 02. Quelle relation existe-il entre ```PlusGrandCarreBlanc_Rec(2,2)``` et les 3 valeurs précédentes ? -> -> 03. Généraliser ce résultat pour un pixel quelconque : -> -> ```PlusGrandCarreBlanc_Rec(lig,col) = min( ......... , ......... , ......... ) + .........``` -> -> 4. Proposer un algorithme récursif pour ```PlusGrandCarreBlanc_Rec(i,j)``` : -> -> ```python -> def PlusGrandCarreBlanc_Rec(t,lig,col): # t est le tableau représentant l'image noir et blanc -> -> if t(lig, col) == 1: # Si le pixel est noir -> return ... # On retourne ... -> -> # Sinon : -> elif ...... : # Si le pixel est sur la 1ère ligne ou 1ère colonne -> return ... # On retourne ... -> -> else : # Sinon : -> return ... # On retourne 1 + min(PlusGrandCarre des pixels adjacents) -> # Adjacents => à gauche, en haut, en diagonale haut-gauche -> ``` -> -> 05. Tester votre algorithme sur le tableau suivant : -> -> ```python -> t=[ [0,0,0,1,0], -> [0,0,0,0,1], -> [1,0,0,0,0], -> [0,0,1,1,0], -> [0,1,0,0,1] ] -> ``` -> -> ```python -> >>> print(PlusGrandCarreBlanc_Rec(t,2,2)) -> 2 -> ``` -> -> **Remarque :** D'autres tableaux sont présent dans le fichier ```TP.py``` -> -> **Remarque :** Lors de l'évaluation du minimum par ```min()```, l'interpréteur DOIT calculer les 3 valeurs à comparer. Or, nous pouvons lui éviter dans certains cas ce travail (et économiser un nombre d'appels récursifs). En effet, si la première valeur calculée est nulle alors le ```min(... , ... , ...)``` retournera FORCEMENT 0 et la fonction retournera 1. De même pour la seconde et la troisième. -> -> 6. Modifier ```PlusGrandCarreBlanc_Rec(lig,col)``` en tenant compte de la remarque précédente. -> -> ```python -> def PlusGrandCarreBlanc_Rec(t,lig,col): -> ... -> else : -> g = ... # Valeur du pixel de gauche -> if g==0: -> return 1 -> h = ... # Valeur du pixel du haut -> if h==0: -> return 1 -> ... # ... -> return ... # On retourne 1 + min(g,h,...) -> ``` -> -> 7. Ecrire un algorithme simple pour afficher ```t``` à l'aide de ```matplotlib``` et vérifier le bon fonctionnement de votre algorithme. -> -> > **Aide** -> > -> > ```python -> > import matplotlib.pyplot as plt -> > ... -> > plt.imshow(t,cmap='binary') -> > plt.show() -> > ``` -> -> 8. Ecrire un algorithme qui parcourt toute l'image et qui retourne la ligne et la colonne du carré le plus grand ainsi que sa taille. -> -> - ```RechercheCarreBlanc_naif(t)``` : Retourne la ligne et la colonne du coin inférieur droit du carré blanc le plus grand ainsi que sa taille. -> -> ```python -> def RechercheCarreBlanc_naif(t): -> ... -> for lig in range(...): -> for col in range(...): -> ... -> return l,c,tmax -> ``` -> -> 9. Bonus : Faire en sorte d'afficher ce carré lors de l'affichage de l'image. -> -> 10. Ajouter un compteur (variable global) ```compteur_naif``` permetttant de compter le nombre d'appels récursifs -> -> 11. Ecrire une fonction permettant de générer un tableau de $n\times n$ content un ratio de points noir de ```ratio_noir```% -> -> ```python -> def genere_tab_alea(n, ratio_noir): -> ... -> return t -> ``` - -# Programmation dynamique - Utilisation de la mémoïsation : ```RechercheCarreBlanc_memoisation()``` - -Remplacons nous dans le cadre d'un cas simple, on cherche ```PlusGrandCarreBlanc_Rec(2,2)```. - -![Image N/B](3.png) - -> 1. Pendant l'appel de ```PlusGrandCarreBlanc_Rec(2,2)```, quelles sont les 3 appels récursifs qui vont être exécutés ? -> -> - Appel n°1 : ```PlusGrandCarreBlanc_Rec( ... , ... )``` -> - Appel n°2 : ```PlusGrandCarreBlanc_Rec( ... , ... )``` -> - Appel n°3 : ```PlusGrandCarreBlanc_Rec( ... , ... )``` -> -> 2. Compléter le tableau suivant qui liste, pour les appels n°1, n°2 et n°3, les appels récursifs exécutés. -> -> Appel n°1 : ```PGCB_Rec( ... , ... )``` | Appel n°2 : ```PGCB_Rec( ... , ... )``` | Appel n°3 : ```PGCB_Rec( ... , ... )``` -> ---------|----------|--------- -> ```PGCB_Rec( ... , ... )``` | ```PGrCB_Rec( ... , ... )``` | ```PGCB_Rec( ... , ... )``` -> ```PGCB_Rec( ... , ... )``` | ```PGrCB_Rec( ... , ... )``` | ```PGCB_Rec( ... , ... )``` -> ```PGCB_Rec( ... , ... )``` | ```PGrCB_Rec( ... , ... )``` | ```PGCB_Rec( ... , ... )``` -> -> 3. Quels appels sont redondants ? Proposez une solution pour éviter cela. -> - -## Mise en place du tableau de mémoïsation - -Pour économiser des appels récursifs, vous allez stocker **au fur et à mesure** les résultats de ces appels dans un tableau de même taille que l'image. - -Ce tableau sera initialisé entièrement à ```-1```, ce qui signifie qu'aucun résultat n'a déjà été calculé pour ce pixel. Dans le cas contraire, il faudra stocker le résultat dans ce ```t_memo``` - -- ```t_memo``` : Tableau de même dimension que ```t``` qui stocke les valeurs de ```PlusGrandCarreBlanc_memo()``` au fur et à mesure des appels récursifs. Ce tableau est une variable globale. - -> 4. Initialiser la variable globale ```t_memo```. -> -> **Aide :** En une ligne ... ```t_memo = [[-1 for i in ... ] for j ... ]``` -> -> **Remarque :** On peut être malin et faire une copie de ```t``` en remplaceant les ```1``` (pixel noir) par des ```0``` (résultat connu) et les ```0``` (pixels blancs) par des ```-1``` (résultat inconnu) - -## Évolution de ```PlusGrandCarreBlanc_Rec()``` en ```PlusGrandCarreBlanc_memo()``` - -> 5. En vous aidant de ```PlusGrandCarreBlanc_Rec()```, écrire la fonction ```PlusGrandCarreBlanc_memo()``` en stockant et utilisant les résultats précédamment calculés dans le tableau ```t_memo``` - -```python -def PlusGrandCarreBlanc_memo(t, lig, col): - if t_memo[lig][col] != -1: # Utilisation d'une valeur présente dans t_memo - return ... - if t[lig][col] == 1: - ... - return ... -``` - -> 6. Effectuer les tests sur différents pixels d'une image et sur différentes images. Vous pouvez aussi utiliser la fonction ```genere_tab_alea(n, ratio_noir)``` pour valider votre algorithme. - -## Pour aller plus vite ... ```RechercheCarreBlanc_memo()``` ? - -```PlusGrandCarreBlanc_memo()``` est une fonction équivalente à ```PlusGrandCarreBlanc_Rec()``` mais normalement plus rapide. - -Il reste donc à parcourir une image pour trouver les coordonnées du coin inférieur droit du plus grand carré blanc dans l'image. - -> 7. Ecrire la fonction ```RechercheCarreBlanc_memo()``` qui retourne les coordonnées du coin inférieur droit du plus grand carré blanc ainsi que sa taille. -> -> ```python -> def RechercheCarreBlanc_memo(t): -> l, c, tmax = 0, 0, 0 -> ... -> return l, c, tmax -> ``` - -## Complexité - -> 8. A l'aide du module ```time```, mesurer la différence de performance de l'algorithme naïf et de celui qui utilisa le mémoïsation sur des images de tailles et de "ratio de noir" différents. -> -> $~$ | ```n=10``` / ```ratio=50``` | ```n=30``` / ```ratio=50``` | ```n=50``` / ```ratio=40``` | ```n=70``` / ```ratio=35``` -> ---------|----------|---------|----------|---------- -> ```RechercheCarreBlanc_Rec``` | $~$ | $~$| $~$| $~$ -> ```RechercheCarreBlanc_memo``` | $~$ | $~$| $~$| $~$ -> -> 9. De la même manière que pour l'algorithme naïf, implanter un ```compteur_memo``` qui permettra de compter le nombre d'appels récursifs. Effectuer les tests avec les mêmes paramètres que dans la question précédente. -> -> $~$ | ```n=10``` / ```ratio=50``` | ```n=30``` / ```ratio=50``` | ```n=50``` / ```ratio=40``` | ```n=70``` / ```ratio=35``` -> ---------|----------|---------|----------|---------- -> ```RechercheCarreBlanc_Rec``` | $~$ | $~$| $~$| $~$ -> ```RechercheCarreBlanc_memo``` | $~$ | $~$| $~$| $~$ -> -> Uniquement avec ```RechercheCarreBlanc_memo```, mesurer le temps d'exécution de l'algorithme avec ```n=500``` / ```ratio=50``` puis avec ```n=1000``` / ```ratio=50```. -> -> 10. Par combien a été multiplié le temps d'exécution lorsque ```n``` a doublé ? Conjecturer sur la complexité de cet algorithme. -> -> 11. Le gain de performance est-il subtanciel ? -> - -![Roadrunner](https://media1.tenor.com/images/272319d1c44ce6047b25e7f266da00a0/tenor.gif) - -# Programmation dynamique - Optimisation "Bottom Up" : ```RechercheCarreBlanc_BU()``` - -L'optimisation "BOTTOM UP", consiste à traiter les problèmes du plus évident (les "plus petits") au moins évident (les "plus gros") en stockant les résultats au fur et à mesure dans un tableau. - -Il faut toutefois identifier un ordre dans lequel résoudre les sous-problèmes. Dans notre cas l'ordre est : - -- Traiter les problèmes de taille "0" : Les cases noires -- Traiter les problèmes de taille "1" : Les cases sur la première ligne et première colonne -- Traiter les problèmes de taille "2" : ... - -Le tableau de "résultats", qui stocke au fur et à mesure les résultats de ````PlusGrandCarreBlanc(lig,col)````, se nommera ```res```. - -> 1. Compléter, à la main, le tableau ```res``` pour les problèmes de taille "0". -> -> - Si le ```pixel(lig,col)``` est noir alors ```res[lig][col] = 0``` -> - -![Image N/B](4.png) - -> 2. Compléter, à la main, le précédent tableau ```res``` pour les problèmes de taille "1". -> -> - Si le ```pixel(lig,col)``` est sur la première ligne et/ou colonne alors ```res[lig][col] = 1``` -> - -![Image N/B](4.png) - -> 3. En l'état actuel, peut-on, à l'aide de ```res```, calculer ```PlusGrandCarreBlanc(1,1)``` directement (sans calcul intermédiaire) ? et ```PlusGrandCarreBlanc(2,2)``` ? -> -> 4. Comment faut-il parcourir le tableau pour être sûr qu'à chaque appel, l'algorithme dispose des résultats des problèmes de taille inférieur. -> -> 5. Ecrire un algorithme ```PlusGrandCarreBlanc_BU()``` qui : -> -> - Initialise le tableau ```res``` à -1 -> - Complète ```res``` avec les résultats de taille "0" (les cases noires) -> - Complète ```res``` avec les résultats de taille "1" (1ère ligne/colonne) -> - Parcours l'image et calcule les résultats de taille supérieur -> - Et retourne ```res``` - -**Aide :** - -- Remplir un tableau ```res```, qui a la même taille que l'image, et qui est initialisé -1 dans toutes les cases. -- A l'aide de 2 boucles imbriquées sur les lignes et colonnes : - - Si le ```t[lig][col]``` est noir, affectez ```0``` à ```res[lig][col]``` - - Sinon si ```lig==0``` ou ```col==0```, affectez 1 à ```res[lig][col]``` - - Sinon affectez ```min(res[lig][col-1] , res[lig-1][col-1] , res[lig-1][col] ) + 1``` à ```res[lig][col]``` -- Retourner ```res``` - -```python -def PlusGrandCarreBlanc_BU(t): - nb_lig, nb_col = ... - res = [[-1 for i in ...] for j in ...] - for lig in range(nb_lig): - for col in range(nb_col): - ... - return res -``` - -## .... et trouver le carré blanc le plus grand (presque à la vitesse de la lumière) : ```RechercheCarreBlanc_BU()``` - -> 6. Ecrire ```RechercheCarreBlanc_BU()``` qui retourne les coordonnées du coin inférieur droit du plus grand carré blanc ainsi que sa taille à l'aide de l'algorithme précédent. -> -> ``` -> def RechercheCarreBlanc_BU(t): -> l, c, tmax = 0, 0, 0 -> ... -> return l, c, tmax -> ``` - -## Comparaison des algorithmes - -> 7. Comparer le temps d'éxecution des ces algorithmes. -> -> $~$ | ```n=100``` / ```ratio=30``` | ```n=300``` / ```ratio=20``` | ```n=1000``` / ```ratio=20``` | ```n=3000``` / ```ratio=40``` -> ---------|----------|---------|----------|---------- -> ```RechercheCarreBlanc_memo``` | $~$ | $~$| $~$| $~$ -> ```RechercheCarreBlanc_BU``` | $~$ | $~$| $~$| $~$ - -## Conclusion - -Programmation dynamique = Gain en performance - -L'étude théorique d'un problème permet une optimisation précise de l'algorithme qui le résoudra. - -![Programmation dynamique](https://www.artelys.com/wp-content/uploads/2018/11/formation4.jpg) \ No newline at end of file