Université François Rabelais de Tours Laboratoire de Mathématiques et Physique Théorique Mathématiques Discrètes et Algorithmique UE 5-4 Option Semestre 5 4. Algorithmes récursifs 4.1. Quelques exemples instructifs. Un algorithme est dit récursif si il s’appelle lui même. L’exemple canonique est celui de l’algorithme d’Euclide qui calcule le pgcd de deux entiers a, b. Si a = bq + r où q = a ÷ b et r = a mod b on a pgcd(a, b) = pgcd(b, r), d’où l’algorithme suivant Procédure : pgcd Données : a, b ∈ N, 0 < b < a si b=0 alors retourner a sinon retourner pgcd(b, a mod b) fin Algorithme 10: Calcul recursif du plus grand diviseur commun de a, b (pgcd) On a par exemple : pgcd(96, 81) = pgcd(81, 15) = pgcd(15, 6) = pgcd(6, 3) = pgcd(3, 0) =3 Pour voir qu’un tel algorithme est correct il faut vérifier (1) que le résultat est effectivement le pgcd(a, b) et (2) que le processus s’arrête après un nombre fini d’étapes. Ici, c’est relativement simple, puisque la suite des restes est strictement décroissante et que pgcd(a, b) = pgcd(b, a mod b).(On rappelle ici que a mod b désigne le reste de la division euclidienne de a par b.) Donnons un autre exemple : la fonction ψ de Ackermann et Péter. Elle définie de la manière suivante 1. 2. 3. ψ(0, n) = ψ(m + 1, 0) = ψ(m + 1, n + 1) = n+1 ψ(m, 1) ψ(m, ψ(m + 1, n)) Cette fonction a l’air simple mais pour calculer ψ(1, 1) il faut déjà 4 étapes : 3 ψ(1, 1) = ψ(0, ψ(1, 0)) 2 = ψ(0, ψ(0, 1)) 1 = ψ(0, 2) 1 = 3. Pour calculer ψ(2, 2), il faut 27 étapes ! On peut montrer que 2·· ·2 ψ(4, y) = 2| 2{z } − 3 y+3-fois et que pour calculer ψ(4, 3) il faut utiliser l’étape 1 un nombre de fois incroyable (bien plus que le nombre d’atomes estimé dans l’univers). Mais quelque soit (x, y), le calcul de ψ(x, y) est tout de même fini ! Moralité, il faut faire attention avec la récurrence ! 1 2 4.2. Un algorithme de tri récursif : le tri par fusion. Dans cette partie on utilisera l’algorithme Fusion(L1 , L2 ) qui prend en entrée deux listes triées et qui renvoie une liste L = L1 ∪ L2 triée. Exemple 4.1. L1 = [2, 3, 5, 6] et L2 = [1, 4]. On procède de la manière suivante pour fusionner L1 et L2 : L1 [2, 3, 5, 6] [2, 3, 5, 6] [3, 5, 6] [5, 6] [5, 6] ∅ L2 [1, 4] [4] [4] [4] ∅ ∅ L ∅ [1] [1, 2] [1, 2, 3] [1, 2, 3, 4] [1, 2, 3, 4, 5, 6] comparaison 1<2 2<4 3<4 4<5 Si L1 est de longueur n et L2 de longueur m alors Fusion fais au plus n + m − 1 comparaisons pour construire L. On introduit maintenant l’algorithme récursif de tri par fusion. Procédure : Tpf Données : L = (a1 , . . . , an ) si n>1 alors m := bn/2c L1 := Tpf(a1 , . . . , am ) L2 := Tpf(am+1 , . . . , an ) L := Fusion(L1 , L2 ) fin Retourner L Algorithme 11: Tri par Fusion (Tpf) Exemple 4.2. Trions à l’aide du tri par fusion la liste [9, 1, 3, 4, 7]. Il est pratique de représenter les differentes étapes de l’algorithme dans un arbre. [9,1,3,4,7] [9,1] [9] [3,4,7] [1] [3] [4,7] [4] [7] 4.3. Arbres. Un graphe orienté G est la donnée d’un ensemble (en général fini) V , dont les éléments sont appelés des sommets et d’un ensemble E de couples ordonnés de sommets, dont les éléments sont appelés arêtes. Un graphe non orienté est un graphe ou on oublie l’orientation des arêtes. On représente un graphe par un dessin de manière naturelle ! Les courbes qui relient deux sommets sont “fléchées” ou non selon que le graphe est orienté ou non. Un graphe est dit : • simple s’il ne contient pas d’arêtes de la forme (a, a) avec a ∈ G et si il y a au plus une arête joignat deux sommets distincts. • acyclique si aucune suite d’arêtes ne couple sur les sommets • connexe s’il existe une représentation géométrique de G dans laquelle il est possible de relier deux points quelconques du graphe. • Le degré d’un sommet v ∈ V est le nombre d’arêtes dont v est une des composantes. Pour plus de détails sur les graphes, on se réferera aux chapitres sur la théorie des graphes ou on trouvera de nombreux exemples. 3 Définition 4.3. Un arbre est un graphe acyclique connexe dans lequel on distingue un sommet en particulier que l’on appelle racine. Les sommets de degré 1 autres que la racine sont appelés feuilles de l’arbre. Un arbre est tacitement un graphe orienté dont les arêtes sont orientés en s’éloignant de la racine. Deux sommets appartenant à une même arête ont un ordre, celui du côté de la racine est appelé le père, celui du côté des feuilles est appelé le fils. Un arbre binaire est un arbre dans lequel chaque sommet est père d’au plus 2 fils. On définit les termes suivants • Le niveau d’un sommet est la “distance de ce sommet” à la racine. • Le niveau k d’un arbre est l’ensemble des sommets de niveau k • La hauteur d’un arbre est le niveau maximum Exemple 4.4. Dans l’arbre suivant, a est la racine. Les feuilles sont g, l, i, j, k. Le sommet d est le père de g et h. Le niveau 2 est composé des sommets {d, e, f }. Cet arbre est de hauteur 4. Figure 1. Un arbre binaire Définition 4.5. Un arbre binaire complet est arbre binaire dans lequel tous les niveaux sont saturés (i.e. on ne peut pas ajouter de sommets) à l’exception, peut-être du dernier niveau. On vérifie facilement par récurrence la proposition suivante. Proposition 4.6. i) Au niveau k d’un arbre binaire complet de hauteur > k, il y a 2k -sommets. ii) Soit A un arbre binaire complet de hauteur k et à S sommets. On a 2k ≤ S ≤ 2k+1 − 1 iii) Soit A un arbre binaire de hauteur k à N feuilles. Alors ≤ N ≤ 2k . Si de plus A est complet on a 2k−1 < N ≤ 2k 4.4. Complexité minimale d’un algorithme de tri à base de comparaisons. Dans cette partie on montre que le tri par fusion est en O(n ln(n)) et que tout algorithme de tri basée sur des comparaisons est au mieux en O(n ln(n)). En d’autres termes, le tri par fusion est en quelque sorte optimal. Proposition 4.7. Le tri par fusion nécessite O(n ln n) comparaisons pour trier une liste de longueur n. Démonstration. Pour estimer la complecité du Tpf on supposera pour simplifier que la liste L est de longueur n = 2m . Dans l’arbre suivant on écrit Tpf(N ) pour dire que l’on applique le Tpf à une liste de longueur N . • Au niveau 1 : 2 listes de longueur 2m−1 • Au niveau 2 : 22 listes de longueur 2m−2 • Au niveau k : 2k listes de longueur 2m−k • Au niveau m : 2m listes de longueur 1 Ensuite, pour remonter du niveau m au niveau m − 1, on fusionne 2m−1 couples de listes de longueur 1 soit 2m−1 comparaisons. Pour remonter du niveau k au niveau k − 1, on fusionne 2k couples de listes de longueur 2m−k soit 2k−1 (2m−k+1 − 1) comparaisons. Finalement, en tout, on fait k X i=1 2k−1 (2m−k+1 − 1) = m X (2m − 2k ) k=1 = m2m − 2m + 1 = n log2 (n) − n + 1 Et donc le tri par fusion nécessite O(n ln n) comparaisons. 4 Théorème 4.8. Un algorithme de tri basée sur des comparaisons est au minimum en O(n ln n). Démonstration. On utilise la notion d’arbre de décision pour le tri d’une liste de longueur L = (a1 , . . . , an ) : c’est un arbre binaire dont les sommets representent une comparaison ai : aj et les deux fils du sommet ai : aj représente les cas où a1 > aj et ai < aj . Les feuilles sont donc toutes les manières de trier une liste à n éléments. (On suppose pour simplifier qu’aucun des ai ne sont égaux). L’arbre de décision A d’une Figure 2. Arbre de décision pour trier une liste à 3 éléments liste à n éléments a donc n! feuilles, puisqu’il y a n! manières différentes d’ordonner L. A chaque feuille correspond une manière d’ordonner L. Le nombre de comparaisons nécessaires pour arriver à cet ordre est la distance de la feuille à la racine. Soit k la hauteur de l’arbre A. C’est un arbre binaire, on a donc n! ≤ 2k par la Proposition 4.6. D’où log2 (n!) ≤ k. on obtient le résultat puisque log2 (n!) = O(n log n).