L2S3 Harmonisation UFR d'IEEA Université Lille 1 Algorithmique 20142015 TP : Tri Rapide Dans ce TP vous pouvez utiliser le module Permutation donné sur le portail. Il fournit les fonctions suivantes : identite : int -> int array qui prend en paramètre un entier n et retourne un tableau de taille n dont la case i contient l'entier i melange : int array -> int array qui prend en paramètre un tableau d'entiers et échange (pseudo-)aléatoirement ses cases 1 Principe Le tri rapide, ou quicksort, est fondé comme le tri par fusion sur le paradigme "diviser pour régner". C'est en eet également un algorithme récursif qui procède en divisant le tableau en deux sous-tableaux, les tri (récursivement) et recombine les deux parties. La diérence entre ces deux algorithmes de tri réside dans la manière de diviser le tableau puis de recombiner. Contrairement au tri par fusion, l'étape la plus coûteuse du tri rapide, donc celle où s'eectue tout le travail, est la division en deux sous tableaux. La recombinaison et immédiate et ne nécessite aucun travail. De plus le tri rapide est un tri "en place" ou "sur place" : un espace mémoire constant en plus du tableau tableau à trier est susant pour eectuer ce tri. De manière plus détaillée le principe du tri rapide est le suivant : 1. Diviser/Partitionner : 2. Trier : 3. Recombiner : 2 Un élément du tableau est choisi arbitrairement, on appel cet élément le pivot. La division consiste à placer tous les éléments de valeur inférieure à celle du pivot au début du tableau et tous les supérieurs en n de tableau. Les éléments de valeur égale au pivot se trouvent au milieu. Le tableau est donc divisé en trois parties : les éléments inférieurs, égaux puis supérieurs au pivot. Il s'agit simplement de trier récursivement les deux parties contenant les éléments inférieurs et supérieurs au pivot. Rien à faire, le tableau est déjà trié ! Choix du pivot Comme vous le verrez dans la suite du TP choisir une bonne valeur de pivot est très important pour avoir la meilleur complexité possible. Cependant choisir le pivot optimal à chaque appel récursif nécessite presque autant d'opérations que pour trier le tableau, c'est pourquoi on choisit en général une valeur aléatoirement dans le tableau. Ecrivez la fonction pivot retournant une valeur ( pas un indice !) choisie aléatoirement dans le tableau t entre les indices d et f , où t, d et f sont donnés en paramètre. Q1 . 1 3 Partition Il y a plusieurs méthodes pour écrire un algorithme itératif de partition, l'une d'elles consiste à maintenir quatre zones délimitées par trois indices, a, b et c, comme illustré sur la Figure 1 : 1. les éléments inférieurs au pivot se trouvent aux indices strictement inférieurs à a, 2. les éléments égaux au pivot se trouvent entre les indices a et c − 1, 3. les éléments supérieurs au pivot sont aux indices strictement supérieurs à b, 4. les éléments restant entre les indices c et b n'ont pas encore été comparés au pivot et n'ont donc pas encore été placés dans l'une des trois précédentes parties. a < pivot c = pivot b ??? > pivot Figure 1 Partition L'idée est de comparer l'élément d'indice c au pivot puis de l'insérer dans l'une des trois parties, inférieur, supérieur ou égale au pivot, suivant sa valeur. 3.1 La fonction partition Donnez les conditions vériées par les indices a, b et c lorsque la partition est terminée, c'est-à-dire lorsqu'il ne reste plus d'éléments qui n'ont pas été comparés au pivot. Q2 . Quel est, en fonction de a, b et c, le plus grand indice x d'un élément strictement inférieur au pivot à la n de l'algorithme de partition ? Q3 . Quel est, en fonction de a, b et c, le plus petit indice y d'un élément strictement supérieur au pivot à la n de l'algorithme de partition ? Q4 . Q5 . Donnez les valeurs initiales des indices a, b et c en fonction des indices d et f de début et n de tableau, c'est-à-dire lorsqu'aucun élément n'a encore été comparé au pivot. Ecrivez la fonction partition qui partitionne selon valeur p du pivot les éléments du tableau t compris entre les indices d et f en trois parties : éléments inférieurs, égaux et supérieurs au pivot. Cette fonction prend en paramètre t, d, f et p et retourne le couple (x,y ) d'indices où x est le plus grand indice d'un élément strictement inférieur au pivot et y est le plus petit indice d'un élément supérieur au pivot à la n de l'algorithme de paritionnement. Q6 . Quelle propriété intéressante pour des éléments au cours d'un algorithme de tri est vériée par tous les éléments de valeur égale au pivot lorsque la partition est terminée ? Q7 . 2 3.2 Complexité L'algorithme a-t-il des cas pire ou meilleur que les autres en terme de nombre de comparaisons eectuées entre le pivot et les autres éléments du tableau ? Si oui donnez ces cas, sinon expliquez pourquoi. Q8 . Combien de comparaisons entre le pivot et les autres éléments du tableau t entre les indices d et f sont eectuées ? Vous pouvez justier votre réponse par un argument (convaincant) ou un calcul. Q9 . Q10 . Donnez l'ordre de grandeur asymptotique du nombre de comparaisons. 3.3 Preuve de correction Cette partie n'est pas obligatoire mais pas inintéressante non plus ... Prouver qu'un algorithme est correct consiste simplement à montrer que le calcul ou le traitement qu'il eectue est bien celui pour lequel il a été écrit : à partir de données vériant ses conditions d'utilisation il conduira toujours à un état nal ou résultat qui vérie une propriété attendue. Une preuve de correction est partielle si elle montre seulement que si l'algorithme s'arrête alors il est correct. Il faut dans ce cas si possible la compléter par une preuve de terminaison, c'est-à-dire montrer que l'algorithme va eectivement s'arrêter. La logique de Hoare permet à partir d'une propriété vraie en début d'algorithme, comme une condition d'utilisation, de déduire des propriétés vériées à chaque étape, c'est-à-dire entre chaque calculs élémentaires, puis en n de l'algorithme. Ecrire une preuve compléte à la main est en général fastidieux. Cependant la mise en évidence d'invariant de boucle permet souvent de se convaincre qu'un algorithme itératif est correct. Un invariant de boucle est une propriété qui est vériée après la phase d'initialisation, reste vraie à l'issue de chaque itération et qui conjointement avec la condition d'arrêt de la boucle permet de montrer que le résultat obtenu à la n de la boucle est bien celui qui est attendu. Proposez un invariant (de boucle) pour (l'unique boucle de) l'algorithme de partitionnement. Q11 . Q12 . Votre invariant est-il vrai au début de la première itération ? Expliquer pourquoi si l'invariant est vérié en début d'une itération il sera toujours vrai au début de l'itération suivante. Q13 . Q14 . Donnez une preuve de correction informelle et partielle de l'algorithme. En observant ce qu'il se passe à chaque itération, justiez que l'algorithme de partionnement va bien se terminer. Q15 . 4 Tri récursif Toute la diculté du tri se trouve dans la partition du tableau et comme pour le tri par fusion, une fois la partie délicate de l'algorithme encapsulée dans une fonction 3 (partition ici) l'algorithme récursif de tri ne prend plus que quelque lignes à écrire. 4.1 La procédure tri_rapide Ecrivez la procédure tri_rapide prenant en paramètre un tableau t et deux indices d et f de début et n de tableau et qui eectue un tri rapide sur t. Vous pouvez naturellement utiliser les fonctions pivot et partition écrites précédemment. Q16 . Justiez rapidement que l'algorithme eectue bien un tri du tableau. Vous pouvez pour cela utiliser la propriété vériée par les éléments égaux à la valeur du pivot à la n de la fonction partition. Q17 . 4.2 Complexité Donnez l'équation de récurrence décrivant le nombre de comparaisons entre éléments du tableau eectuées lors de l'exécution de la procédure tri_rapide sur un tableau de taille n. Dans un premier temps on pourra considérer qu'il y a nI éléments inférieurs et nS éléments supérieurs au pivot. Dans la suite on supposera qu'une valeur n'apparaît qu'une seule fois dans le tableau et donc qu'il ne peut pas y avoir plusieurs cases contenant une valeur égale au pivot. Q18 . Que devient l'équation de récurrence s'il y a toujours autant d'éléments inférieurs que d'éléments supérieurs au pivot ? Q19 . Résoudre l'équation dans ce cas là et donner un ordre de grandeur asymptotique du nombre de comparaisons que le tri eectue. Q20 . Que devient l'équation de récurrence si le pivot est toujours l'élément maximum ou minimum du tableau ? Q21 . Résoudre l'équation dans ce cas là et donner un ordre de grandeur asymptotique du nombre de comparaisons que le tri eectue. Q22 . Déduire de vos résultats précédents un pire et un meilleur cas pour l'algorithme du tri rapide. Q23 . Q24 . Le tri rapide est il intéressant pour un tableau presque trié ? Justiez votre réponse. Remarque : La complexité en moyenne du tri rapide, c'est-à-dire la moyenne du nombre d'opérations sur toutes les données possibles de taille n (= tous les tableau possibles de taille n), est en Θ(n log n). En pratique ce tri est généralement plus ecace que le tri par fusion dont la complexité est toujours en Θ(n log n). 4