Nicolas Gourmelon Calcul Scientifique et Symbolique Avancés 9 avril 2014 TP n˚4 Arbres binaires de recherche, 2ème partie On a étudié au TP précédent le tri par construction d’arbres binaires de recherche. Le coût d’insertion d’un noeud dans un arbre binaire de recherche est majoré par la profondeur de l’ABR. Si on n’a pas de chance, il se peut que cette profondeur soit linéaire en la taille de l’ABR. Ceci nous donne un tri dont le coût est de l’ordre de n2 , où n est la longueur de la liste à trier. Pour éviter cela, on cherche à insérer des noeuds dans un arbre binaire de recherche tout en préservant un équilibre qu’on va définir. 1 Algèbre linéaire avec Sage Essayer les commandes suivantes : sage: sage: sage: sage: sage: sage: sage: sage: sage: sage: v=vector([2,0,1,4]) v ; v*v v.norm() v.column() A=matrix([[2,1],[-1,1]]) ; parent(A) A1=matrix([[SR(2),1],[-1,1]]) ; parent(A1) P=A.characteristic_polynomial() A.eigenvalues() A1.eigenvalues() a=A1.eigenvalues()[1] ; a.show() Expliquer les résultats donnés par les commandes suivantes : sage: sage: sage: sage: sage: sage: sage: sage: B=matrix([[-1,1,2,1],[2,3,3,0],[0,4,5,5],[2,4,0,1]]) B1=matrix([[-1,1,2,SR(1)],[2,3,3,0],[0,4,5,5],[2,4,0,1]]) C=matrix([[-1,1,2,1,0],[2,3,3,0,0],[0,4,5,5,5],[2,4,0,1,1],[2,4,1,1,0]]) C1=matrix([[-1,1,2,1,SR(0)],[2,3,3,0,0],[0,4,5,5,5],[2,4,0,1,1],[2,4,1,1,0]]) B.eigenvalues() B1.eigenvalues() C.eigenvalues() C1.eigenvalues() On peut diagonaliser et calculer les matrices de passages à la base diagonalisante grâce aux commandes suivantes : sage: sage: sage: sage: sage: sage: A.right_eigenmatrix() A1.right_eigenmatrix() Delta=A.right_eigenmatrix()[0] P=A.right_eigenmatrix()[1] P*A*P^{-1} P^{-1}*A*P Manipulation d’une variable symbolique ’n’ : sage: sage: sage: sage: sage: sage: sage: sage: 2^n n=var(’n’) a=2^n a.show() 2*a factor(2*a) b=factor(2*a) b.show() 1 Exercice 1 : Ecrire un programme qui à une matrice carrée A de taille 6 3 à coefficients dans l’anneau des entiers renvoie la puissance n-ième de A, où n est une variable symbolique. 2 Arbres équilibrés et arbres de Fibonacci Un arbre binaire est dit équilibré si en tout noeud de cet arbre, les profondeurs du sous-arbres de gauche et du sous-arbre de droite diffèrent d’au plus 1. Exercice 2 : On montre dans cet exercice que la profondeur d’un arbre équilibré est faible par rapport au nombre de noeuds, et donc que le coût de l’insertion d’un noeud dans un arbre binaire de recherche équilibré est faible. Les arbres de Fibonacci A_n sont définis récursivement de la manière suivante : l’arbre A_0=[] et un arbre A_1 ayant un seul noeud sont des arbres de Fibonacci d’ordre 0 et 1, respectivement. Un arbre binaire A_{n+2} est un arbre de Fibonacci d’ordre n + 2 si son sous-arbre gauche A_{n+1} et son sous-arbre droit A_n sont d’ordres n + 1 et n, respectivement. 1. Ecrire un algorithme qui à n associe un arbre de Fibonacci A_n d’ordre n. 2. Quelle est la profondeur d’un tel arbre A_n ? Exprimer à l’aide de Sage le nombre de noeuds d’un arbre A_n. On utilisera une équation matricielle, et on en déduira à l’aide de Sage une expression de ce nombre en fonction de n. 3. 4. Montrer que parmi les arbres équilibrés de profondeur n+1, les arbres de Fibonacci d’ordre n minimisent le nombre de noeuds. En remarquant que l’arbre binaire complet maximise lui trivialement le nombre de noeuds à profondeur fixée, en déduire l’ordre de grandeur de la profondeur d’un arbre équilibré en fonction de son nombre de noeuds. 5. Exercice 3 : Ecrire un algorithme verif_equ qui à un ABR A associe un booléen, et qui teste si un ABR est équilibré ou non. 3 Rééquilibrages et tris La procédure décrite au TP précédent qui insère un noeud dans un arbre binaire de recherche, ne rend pas (en général) un arbre équilibré. On veut écrire un algorithme tel qu’à chaque insertion d’un noeud dans l’ABR, l’arbre retourné soit équilibré, de sorte que le coût d’une insertion d’un nouveau noeud soit de l’ordre du logarithme de la taille de l’arbre. Exercice 4 : On considère un ABR T déséquilibré tels que les sous-arbres gauche et droit de T sont équilibrés. Ecrire un algorithme equilibrage qui à un tel arbre associe un ABR équilibré (on utilisera les rotations vues en cours). 1. On applique la procédure de l’exercice 2 à un arbre équilibré et on suppose que l’arbre retourné n’est pas équilibré. Démontrer qu’il existe un unique sous-arbre T déséquilibré de profondeur maximale, autrement dit, tel que les sous-arbres de T sont équilibrés. 2. 3. Ecrire une fonction insert_equ qui insère un nombre a dans un ABR T et qui rééquilibre l’arbre, si nécessaire. En déduire un algorithme de tri tri_ABR_equ par construction d’un arbre binaire de recherche, tel qu’à chaque étape de la construction l’arbre est équilibré. 4. 5. Comparer son coût à celui du tri rapide. Que peut-on en dire ? 2 Exercice 5 : Dans l’exercice précédent, on teste à chaque insertion d’un noeud si l’arbre binaire est équilibré. Or, ce test requiert le calcul de la profondeur de chaque sous-arbre. Le coût du calcul de la profondeur est linéaire. La vérification de ce qu’un ABR est équilibré doit donc avoir un coût au moins linéaire. Le coût total du tri ainsi effectué doit donc être en n2 . Pour accélérer la vérification qu’un arbre est équilibré, on peut "décorer" chaque noeud par la profondeur du sous-arbre correspondant. Nous travaillerons sur des objets de type "ABR+profondeur" définis récursivement de la façon suivante : — la liste [[],[],[],0] est l’ABRP vide de profondeur 0, — toute liste T=[p,a,T1,T2] est un un ABRP si et seulement si les conditions suivantes sont vérifiées : — a est un réel et p est un entier positif, — T1 et T2 sont des ABRP, — T1[0]6 a 6T2[0] et la profondeur de T est donnée par T[3]==p et est égale à 1+max(profondeurs de T1 et T2). 1. Ecrire une fonction equilibrage_P qui adapte la fonction equilibrage de l’exercice 4 aux ABRP. Ecrire une fonction insert_equ_P qui insère un nombre a dans un ABRP T équilibré et qui le rééquilibre, si nécessaire. Quel est le coût de cet algorithme ? 2. 3. Ecrire un algorithme tri_ABRP de tri par ABRP. 3