Exercice corrigé Arbre de décision Le tri d’un ensemble d’éléments (par exemple des entiers) peut être vu de manière abstraite à l’aide d’un arbre de décision. Un arbre de décision est un arbre binaire représentant les comparaisons entre éléments lors du tri. Chaque nœud interne de l’arbre correspond à la comparaison de deux éléments a et b, et on note ce nœud a?b. Chaque feuille correspond à un ordre, i.e. une permutation, sur les éléments de l’ensemble, qu’on note ha1 , a2 , . . . , an i pour signifier a1 < a2 , etc. L’exécution de l’algorithme de tri correspond à un chemin entre la racine et une feuille dans l’arbre de décision. La figure 1 illustre par exemple le déroulement de l’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. Fig. 1 – Arbre de décision pour l’algorithme de tri par insertion sur trois éléments a, b et c. Exercice 1. Montrer qu’un arbre de décision est forcément localement complet. 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 un arbre de décision. Un nœud interne de A correspond à la comparaison de deux éléments a et b, et possède donc forcément deux fils : soit a < b, soit a ≥ b. A est donc bien localement complet. Soit N le nombre total de nœuds de A : N = ni+f , avec ni et f respectivement le nombre de nœuds internes et le nombre de feuilles de A. Comme A est localement complet, on sait que f = ni + 1 (cette propriété se démontre par récurrence sur ni). On a donc N = 2f − 1. Si A est l’arbre de décision d’un algorithme de tri d’un ensemble de n éléments, en supposant que cet algorithme est correct, chaque feuille doit correspondre à une permutation différente de ces éléments, et toutes les permutations doivent être représentées dans A. Comme il y a n! permutations possibles, on a f = n!, et donc N = 2n! − 1. (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. Que vaut cette hauteur ? Par définition d’un arbre binaire, on a N ≤ 2h+1 − 1 (2) Grenoble-INP Ensimag, 1ère année, 2010-2011 Algo 2 — Exercice corrigé (avec égalité si et seulement si l’arbre est complet). En combinant les équations (1) et (2), on obtient 2n! ≤ 2h+1 ou encore log(n!) ≤ h. (3) La formule bien connue de Stirling nous donne une approximation 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). 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. Grenoble-INP Ensimag, 1ère année, 2010-2011 Algo 2 — Exercice corrigé Exercice 2. Soit T un tableau contenant n entiers positifs ou nuls. Supposons que nous codons un arbre de décision, associé à un certain algorithme de tri sur T, de la façon suivante. Chaque nœud contient deux entiers, correspondant aux deux indices des valeurs du tableau comparées dans le cas d’un nœud interne, et égaux à 0 dans le cas d’une feuille. Il contient également un tableau de n éléments, correspondant à la permutation résultat dans le cas d’une feuille et contenant une unique valeur −1 dans le cas d’un nœud interne (on prend cette valeur car elle n’est pas présente dans T). Ceci donne en Ada : type Tab is array(1..n) of Integer; type Noeud; type ArbreDecision is access Noeud; type Noeud is record Indice1, Indice2 : Integer; -- vaut 0 si feuille Permutation : Tab; -- vaut [-1,-1,...,-1] si noeud interne Gauche, Droit : ArbreDecision; -- vaut null si feuille end record; Ecrire une fonction GetPermutation(T : in Tab, A : in ArbreDecision) return Tab ; qui parcourt un arbre de décision (supposé construit) A et renvoie la permutation correspondant aux valeurs stockées dans T. Démonstration. L’algorithme sera bien sûr récursif. Le principe est le suivant : on part de la racine ; tant qu’on est sur un nœud interne on compare T[Indice1] et T[Indice2], et on descend dans l’arbre en fonction du résultat. Une fois arrivé sur une feuille, il suffit de renvoyer le tableau Permutation. function GetPermutation(T : in Tab, A : in ArbreDecision) return Tab is begin if (A.Indice1 == 0) then -- on est sur une feuille return A.Permutation; end if; -- sinon on est sur un noeud interne if (A.T[A.Indice1] < A.T[A.Indice2]) then -- on descend à gauche return GetPermutation(T,A.Gauche); end if; -- sinon on descend à droite return GetPermutation(T,A.Droit); end GetPermutation; Grenoble-INP Ensimag, 1ère année, 2010-2011 Algo 2 — Exercice corrigé