Algorithmique P2 Optimisation d'un algorithme de tri 2009-2010, Ulg R.Dumont
Sources supplémentaires Cours Algorithms and Data Structures in Java, Patrick Prosser, 2000, Glasgow University Algorithmique du tri, C.Jard, 2006, Département Informatique et Télécommunications, ENS Cachan Articles QuickSort, A Historical Perspective and Empirical Study, Laila Khreisat, Fairleigh Dickinson University, 2007 Autres QuickSort is Optimal, Sedgewick & Bentley
Diviser pour régner : Quicksort Diviser : Le tableau A[p.. r] est partitionné en deux soustableaux (éventuellement vides) A[p..q 1] et A[q+1..r] tels que chaque élément de A[p..q 1] soit inférieur ou égal à A[q] qui, lui-même, est inférieur ou égal à chaque élément de A[q+1..r]. L indice q est calculé dans le cadre de cette procédure de partitionnement Problème central Régner : Les deux sous-tableaux A[p..q 1] et A[q+1..r] sont triés par des appels récursifs au tri rapide. Combiner : Suite aux appels récursifs sur les sous-tableaux, aucun travail n est nécessaire pour les recombiner : le tableau A[p..r] est maintenant trié.
Quicksort
Quicksort Initialement, Le premier segment est vide Le deuxième segment également Le segment "non traité" s'étend de l'élément p à l'élément r-1 Le pivot est à l'indice r Chaque itération de la boucle réduit le segment non traité d'un élément Finalement, Le segment non traité est vide Les deux autres segments contiennent 0 ou plusieurs éléments.
Quicksort
Quicksort - Python
Choix du pivot Prendre le premier élément du tableau (ou le dernier ) Si les éléments ne sont pas déjà partiellement triés ou en ordre inverse Ok Sinon Considérons le cas d'une liste déjà triée (ou inversement triée) Quelle est la taille des sous-listes L et R? Tous les éléments vont dans A[p..q-1] ou A[q+1..r] Le pivot étant constamment choisi à cette position, le problème se répercute récursivement. Dans ces cas particuliers, le quicksort est dit "dégénéré" Complexité O(n 2 )
Quicksort : Complexité Soit l'arbre de partition du Quicksort À partir de P.Prosser (voir sources sup.)
Quicksort : Complexité v1 Soit s i (n), la taille totale des entrées au niveau i de l'arbre T On a s 0 (n) = n s 1 (n) = n-1 (on ne traite plus le pivot) s 2 (n) = n-3 (on ne traite plus les pivots) ou n-2 (si une partition est vide) Pire cas n 1 n 1 n i ( ) ( ) i= 0 i= 0 i= 1 2 ( ) O s n = O n i = O i = O n À partir de P.Prosser (voir sources sup.)
Quicksort : Complexité v1 Meilleur des cas = partitions de taille égale : s 0 (n) = n s 1 (n) = n-1 s 2 (n) = n-3 = n-(1+2) s 3 (n) = n-7 = n-(1+2+4)=n-(1+2+2 2 ) s i (n) = n-(1+2+2 2 + +2 i-1 )=n-(2 i -1)=n-2 i +1 i s ( n) = n + n 1+ n 3 +... + n 2 + 1 Il vient i Or, au max, i=log n (puisque prop. à la hauteur de T) Donc, i si ( n) = ( i + 1) n (1 + 3 +... + 2 ) + 1 Ou encore s ( n) = log n. n + n (1 + 3 +... + n) + 1 O( nlog n) i
Quicksort : Complexité v2 Plus formellement, la complexité de Partition est O(n), à une constante près notée α Le pivot sépare le tableau en deux parties (k) éléments et (n-k) éléments Travaillons sur la relation suivante T(n) = T(k) + T(n-k)+ αn
Quicksort : Complexité v2 Cas défavorable : Dans ce cas, on a k=1 et n-k=n-1 (une des soustableaux est vide) pivot le + petit ou le + grand Il vient successivement T(n) = T(1)+T(n-1)+αn = T(n-1)+T(1)+αn = [T(n-2)+T(1)+α(n-1)]+T(1)+αn = T(n-2)+2.T(1)+α(n-1+n) = [T(n-3)+T(1)+α(n-2)]+2.T(1)+α(n-1+n) = T(n-3)+3.T(1)+α(n-2+n-1+n) [= T(n-i)+i.T(1)+α(n-i+1+ +n-2+n-1+n) i 1 = T(n-i)+i.T(1)+α. ( n j) ] n 2 j= 0 = T(1)+(n-1)T(1)+α. ( n j) j= 0 = nt(1)+α(n(n-2)-(n-2)(n-1)/2) O(n²) O(n²)
Quicksort : Complexité v2 Cas favorable : Dans ce cas, on a k=n/2 et n-k=n/2 Il vient successivement T(n) = 2T(n/2)+αn = 2(2T(n/4)+α(n/2))+αn = 4T(n/4)+2α(n/2)+αn = 2²T(n/4)+2αn = 2²(2T(n/8)+α(n/4))+2αn = 2³T(n/8)+3αn [= 2 k T(n/2 k )+kαn] = nt(1)+αnlogn O(n O(n logn)
Optimisations possibles Choix du pivot Pivot aléatoire, central, median-of-three Autre algorithme de tri pour les petites listes Insertion Sort Optimisations avancées Différentes manières de partitionner les sous-listes (Dutch flag) Améliorations plus 'techniques' (Qsorte, Bsort, )
Optimisations possibles Afin de simplifier la présentation des améliorations, adoptons une nouvelle approche Le choix du pivot est inclus en tant que paramètre Le parcours s'effectue à partir des deux extrémités
Optimisations possibles Problème : Tableau en partie trié (pire des cas = tableau entièrement trié) Cas relativement fréquent Résulte du choix du pivot Solutions pour le pivot : 1. Pivot aléatoire Solution classique 2. Pivot en milieu de tableau
Pivot en tant qu'élément central 1 5 15 8 7 2 4 11 6 1 5 15 8 6 2 4 11 6 i j 1 5 15 8 6 2 4 11 6 i j 1 5 4 8 6 2 15 11 6 i j 1 5 4 2 6 8 15 11 6 j i 1 5 4 2 6 7 15 11 8
Optimisations possibles La solution idéale aurait été un pivot médian Obtention de deux sous-tableaux de tailles proches [N/2] et [N/2]-1 Problème : détermination coûteuse en temps 3. Calcul de la médiane (T[p], T[r] et T[ (p+r)/2 ]) Median-of-three partitioning: a. Calcul de la médiane à partir des valeurs de 3 éléments choisis aléatoirement, ou b. Calcul basé sur le premier élément, le central et le dernier, ou c. Choix sur 3 groupes de 3 éléments 3 médianes 1 médiane Peut dépendre du nombre d'éléments à trier. Exemple : < 7 : a. 7<=x<=40 : b. > 40 : c.
Pivot médian x y z x z y x<=z y>=z Choix des éléments choix du pivot Exemple x m z y placement du pivot 3 9 7 2 8 10 6 5 1 4 sélection des 3 éléments 3 9 7 2 4 10 6 5 1 8 déplacement des éléments 3 9 7 2 1 10 6 5 4 8 échange du pivot sélectionné 3 9 7 2 1 10 6 5 4 8 i j
QS : 3-way way, aperçu 'Multikey QuickSort', 'Qsort7' Implémentation du problème du drapeau hollandais (Dutch Flag) de Dijkstra par Benteley et McIlroy Optimisation pour le tri de chaines de caractères Choisir un élément comme pivot Considérer le premier caractère de la string pivot Partitionner en 3 sous-tableaux Le premier contient celles dont le caractère correspondant est inférieur Le second, contient les égaux Le troisième, les supérieurs Tri récursif sur les tableaux 1 et 3 Tri récursif sur le tableau 2, sur le second caractère du pivot
QS : 3-way way, aperçu a (d) pointe le premier (dernier) élément inférieur (supérieur) à l'élément pivot b (c) pointe le premier (dernier) élément à traiter b et c sont échangés quand nécessaire Au final, on rassemble les extrémités au centre du tableau Voir L. Khreisat, Sources Suppl.
Du tri rapide au tri par insertion Lorsque le tableau contient peu d'éléments (moins de 15), il est conseillé de passer au tri par insertion. Tri itératif Efficace en temps Plus efficace en espace mémoire en raison de l'absence de récursion sur les sous-listes
Tri par insertion
Complexité du tri par insertion Source basée sur C.Jard, voir s. supp.
Tri par insertion cas favorable et défavorable Cas favorable : tableau déjà trié. Fonction linéaire Cas défavorable : tableau trié par ordre décroissant Car et Fonction quadratique
Tri par insertion Cas moyen Cas le plus favorable = borne inférieure Cas le plus défavorable = borne supérieure Cas général = entre les deux On fait parfois des analyses de "cas moyen" : Tableau d'entrée = n nombres tirés au hasard (on suppose une loi uniforme). Pour une clé choisie ligne 2 en moyenne la moitié du tableau lui est supérieure et l'autre moitié lui est inférieure.
Tri par insertion Cas moyen Donc Il vient Fonction quadratique
Tri par insertion complexité simp. Op. fondamentale = comp. de deux éléments
Tri par insertion complexité simp.
Algorithmique P2 Techniques de preuves 2009-2010, Ulg R.Dumont
Preuve par (contre-)exemple Par l'exemple Proposition Il existe des nombres égaux au produit de la somme et du produit de leurs chiffres Preuve 135 = 9*15 Par contre-exemple Proposition Tout nombre 2 i 1 est premier Preuve 2 4-1=15=5*3
Preuve par contrapositive Proposition Soient a et b entiers. Si ab est pair, alors a est pair ou b est pair. Preuve Si a et b sont impairs, alors ab est impair. Supposons a = 2i + 1 et b = 2j + 1. Alors, ab = 4ij + 2i + 2j + 1 = 2(2i j + i + j) + 1 Par conséquent, ab est impair.
Preuve par contradiction Par contradiction Proposition Soient a et b entiers. Si ab est impair, alors a est impair et b est impair. Preuve Soit ab impair. Supposons que a est pair. Alors, a = 2i. Par conséquent, ab = 2ib. Donc ab est pair, ce qui constitue une contradiction. En conséquence, a est impair et b est impair.
Preuve par récurrence Proposition F(n) < 2 n où F est la fonction Fibonacci définie par F(0) = 0, F(1) = 1 et F(n) = F(n 2) + F(n 1). Preuve Initialisation F(0) <2 0, F(1)<2 1. Hérédité Supposons la relation vérifiée pour F(n-2) et F(n-1) Alors F(n)=F(n-2)+F(n-1)<2 n-2 +2 n-1 <2.2 n-1 =2 n
Preuve par récurrence Proposition n! 2 n 1 n Preuve Vrai pour n =0 et n =1 Par ailleurs (n +1)!=(n +1)n! (n +1)2 n 1 2.2 n 1 =2 n
Algorithmique P2 Type de données abstrait 2009-2010, Ulg R.Dumont
TDA Type de Données Abstrait ADT Abstract Data Type TDA = Description d'une structure de données et des opérations de manipulation (accès, modification, suppression, ) de cette structure. Il comprend Une partie interface Description de la structure et des opérations possibles Opérations élémentaires (= primitives) Opérations possibles, basées sur ces primitives Les éventuels arguments et retours de ces opérations Une partie implémentation Description explicite
TAD La définition d'un TAD repose sur Des structures de données simples Les algorithmes permettant de les implémenter Éventuellement d'autres TAD Il ne dépend pas d'un langage particulier Il permet d'utiliser une structure de données sans pour autant y accéder directement Un TAD est un ensemble limité d'opérations
TAD - Exemples Pile push(e : element, p: pile) Place l'élément e au sommet de la pile p pop(p : pile) Supprime l'élément au sommet de la pile taille(p : pile) : entier Retourne la taille de la pile estvide(p : pile) : booleen Teste si la pile p est vide top(p : pile) : value Retourne la valeur de l'élément au sommet de la pile
TAD - Exemples File push(e : element, f: file) Place l'élément e en queue de la file f pop(f : file) Supprime l'élément en tête de la file taille(f : file) : entier Retourne la taille de la file estvide(f : file) : booleen Teste si la file f est vide top(f : file) : value Retourne la valeur de l'élément en tête de file
TAD Exemples Arbre binaire EstVide(A : arbre) : booleen Teste si l'arbre A est vide Racine(A : arbre) : value Retourne la valeur de l'élément racine de A Gauche(A : arbre) : tree Retourne le sous-arbre à gauche de A Droite(A) : tree Retourne le sous-arbre à droite de A Construire(x,B,C) Construit l'arbre de racine x, ss-arbres gauche B et droit C