Exercices corrigés Arbre de décision Les arbres de décisions permettent de caractériser une classe d’algorithmes de tris : les algos de tris par comparaisons séquentiels et déterministes. Cela correspond grosso-modo aux programmes de tri qu’on peut écrire dans les langages impératifs standard comme Ada, et en ne faisant que l’hypothèse d’une fonction de préordre < sur les éléments à trier. Plus précisément, un algorithme donné correspond à un ensemble d’arbres de décision, dans lequel chaque arbre de décision représente les suites de comparaisons effectuées par l’algorithme lorsque la séquence en entrée a une taille fixée n. Étant donné le caractère séquentiel et déterministe des algos considérés, ces suites ont une structure d’arbre. Définition 1 (Arbre de décision). Soit Σ un ensemble fini à n éléments appelé alphabet. Un arbre de décision sur Σ est un arbre binaire, dont : – chaque nœud interne est étiqueté “a?b” avec a et b dans Σ ; un tel nœud représente une comparaison effectuée par l’algorithme entre deux éléments de nom a et b dans la séquence d’entrée. – chaque feuille est étiquetée – soit par une permutation des éléments de Σ, qu’on note ha1 , a2 , . . . , an i pour signifier a1 < a2 , etc. (cette permutation correspond donc à la permutation appliquée par l’algorithme pour trier la séquence en entrée.) – soit étiquetée par un symbole spécial ⊥ qui représente du code de l’algorithme qui n’est jamais exécuté. 1 Pour une séquence d’entrée donnée (qui associe à chaque élément de Σ une valeur concrète), l’exécution de l’algorithme de tri représenté par l’arbre de décision, doit correspondre exactement à un chemin entre la racine et une feuille-permutation dans l’arbre. La figure 1 illustre par exemple le déroulement d’un algorithme de tri par insertion sur un ensemble de trois éléments Σ = {a, b, c}. Si on prend par exemple a = 27, b = 42 et c = 5, l’algorithme va suivre le chemin surligné en rouge. Figure 1 – Arbre de décision sur Σ = {a, b, c} pour un algorithme de tri par insertion. 1. On peut donc ainsi représenter des algorithmes qui font des tests superflus. Grenoble-INP Ensimag, 1ère année, 2010-2011 Algo 2 — Exercices corrigés type Sequence i s array ( Positive range < >) o f Element ; procedure TriInsertionSenti ( T : in out Sequence ) i s -- version du tri par insertion avec sentinelle : -- on remplace des comparaisons sur les indices -- par une comparaison au maximum ( la sentinelle ). I : Integer ; E : Element ; Fini : Boolean := True ; begin -- on place le max en fin f o r K in T ’ First .. T ’ Last -1 loop i f T ( K +1) < T ( K ) then Fini := False ; E := T ( K ) ; T ( K ) := T ( K +1) ; T ( K +1) := E ; end i f ; end loop ; i f Fini then return ; end i f ; -- ici : T ’ Last > T ’ First et T (T ’ Last ) est le max de T . f o r K in reverse T ’ First .. T ’ Last -2 loop E := T ( K ); I := K +1 ; while T ( I ) < E loop T (I -1) := T ( I ); I := I +1 ; end loop ; T (I -1) := E ; end loop ; end ; Figure 2 – Tri par insertion avec sentinelle. Exercice 1. Calculer l’arbre de décision sur Σ = {A, B, C} associé à l’algorithme de tri donné en figure 2. Démonstration. Voilà l’arbre de décision sous forme textuelle : si B<A alors | si C<A alors | | si C<B alors | | | si A<B alors | | | | absurde | | | sinon (A>B) | | | | tri=[C;B;A] | | sinon (C>B) | | | tri=[B;C;A] | sinon (C>A) | | si A<B alors | | | absurde Grenoble-INP Ensimag, 1ère année, 2010-2011 Algo 2 — Exercices corrigés | | sinon (A>B) | | | tri=[B;A;C] sinon (B>A) | si C<B alors | | si C<A alors | | | si B<A alors | | | | absurde | | | sinon (B>A) | | | | tri=[C;A;B] | | sinon (C>A) | | | tri=[A;C;B] | sinon (C>B) | | tri=[A;B;C] Sur cet algo, certains tests apparaissent superflus dans l’arbre de décision (mais pas dans l’algo lui-même) : cela vient du fait que dans l’arbre de décision, on raisonne avec n fixé, alors que dans l’algo, on raisonne avec n quelconque. Grenoble-INP Ensimag, 1ère année, 2010-2011 Algo 2 — Exercices corrigés type Tab i s array (1.. n ) o f Integer ; type Noeud ; type ArbreDecision i s access Noeud ; type Noeud ( EstFeuille : Boolean ) i s record case EstFeuille i s when True = > Permutation : Tab ; when others = > Indice1 , Indice2 : Integer ; FGauche , FDroit : ArbreDecision ; -- vaut null si cas impossible end case ; end record ; Figure 3 – Structure Ada d’un arbre de décision. Exercice 2. Soit T un tableau contenant à n éléments. Pour coder un arbre de décision associé à un certain algorithme de tri sur T, on utilise un type Noeud avec un champ booléen EstFeuille indiquant si le nœud est une feuille ou non. En Ada (voir figure 3), on fait de ce champ un discriminant. Ansi, si EstFeuille vaut False, alors le nœud a les autres champs suivants : – Indice1 et Indice2 correspondant au test T(Indice1) < T(Indice2). – FGauche et FDroit correspondant aux 2 fils. Dans ces fils, une feuille ⊥ est simplement représentée par un pointeur null. Au contraire, si EstFeuille vaut True, alors le nœud représente une permutation qui est donnée par son champ Permutation. Celle-ci associe à chaque position dans le tableau final (trié) sa position initiale dans T. Ecrire une fonction Execute qui parcourt un arbre de décision (supposé construit) A et renvoie le tableau trié correspondant aux valeurs stockées dans T. function Execute ( A : in ArbreDecision ; T : Tab ) return Tab ; Démonstration. On sélectionne la branche de A en fonction des valeurs de T, puis on compose T avec la permutation trouvée. function Execute ( A : ArbreDecision ; T : Tab ) return Tab i s Res : Tab ; Cour : ArbreDecision := A ; begin while not Cour . EstFeuille loop i f T [ Cour . Indice1 ] < T [ Cour . Indice2 ] then Cour := Cour . FGauche ; else Cour := Cour . FDroit ; end i f ; -- normalement , Cour /= null si A represente un tri correct ! end loop ; f o r I in Res ’Range loop Res ( I ) := T ( Cour . Permutation ( I )) ; end loop ; return Res ; end Execute ; Grenoble-INP Ensimag, 1ère année, 2010-2011 Algo 2 — Exercices corrigés Exercice 3. Combien de feuilles doit au moins posséder l’arbre de décision associé à un algorithme de tri ? En déduire que tout algorithme de tri d’un ensemble de n éléments nécessite Ω(n log n) comparaisons dans le cas le pire. Démonstration. Soit A l’arbre de décision associé à un algorithme de tri d’un ensemble de n éléments. En supposant que cet algorithme est correct, chacunes des n! permutations à n éléments doit figurer sur une feuille de A (sinon, l’inverse d’une permutation absente donne un ordre initial sur les éléments pour lequel l’algorithme ne trie pas le tableau correctement). Le nombre f de feuilles possibles de A vérifie donc n! ≤ f . n! ≤ f (1) Remarquons à présent que le nombre de comparaisons dans le cas le pire correspond au nombre de nœuds internes traversés par le chemin le plus long allant de la racine à une feuille de l’arbre de décision. Autrement dit, c’est exactement la hauteur h de l’arbre. Comme A est un arbre binaire, on peut démontrer par récurrence que : f ≤ 2h (2) En combinant les formules (1) et (2), on obtient log2 (n!) ≤ h. (3) La formule bien connue de Stirling nous donne une approximation 2 de la factorielle de n : n! = On a donc : √ n 1 2πn( )n (1 + Θ( )). e n n ∃A > 0, n! ≥ A( )n e ce qui nous donne : log(n!) ≥ log A + n log n − n log e. On peut vérifier que ∀n ≥ e2 , n log n − n log e ≥ 1 n log n. 2 Grâce à l’équation (3), on a donc bien ∃B > 0, ∃N0 > 0, ∀n ≥ N0 , h ≥ Bn log n. On a donc bien montré que le nombre de comparaisons dans le cas le pire est en Ω(n log n). Exercice 4. Écrire un programme Ada qui construit automatiquement l’arbre de décision associé à un programme de tri. Démonstration. Voir solution sur le wiki. Bibliographie indicative [1] T. Cormen, C. Leiserson, R. Rivest, C. Klein. Introduction à l’algorithmique : cours et exercices. Dunod, 2002. Disponible à la B.U. Sciences et à la bibliothèque MI2S, en français et en anglais. 2. Alternativement, si on ne se souvient plus par cœur de Stirling, on peut aussi transformer “log2 (n!)” en “ ln1 2 et majorer cette dernière somme par une intégrale (la fonction ln s’intègre par partie très facilement) Grenoble-INP Ensimag, 1ère année, 2010-2011 Pn i=1 ln i” Algo 2 — Exercices corrigés