Exercice 1 : Algorithme de tri Correction
Correction Exercice 1 : Algorithme de tri Un algorithme de Tri prend en paramètre un tableau d éléments d ordonnables, par exemple une liste de nombres et renvoie le tableau trié dans le sens croissant. Exercice 1. Écrire un algorithme de tri de son choix. Quels sont sa complexité dans le pire des cas? Dans le meilleur des cas?
Correction Exercice 1 : Algorithme de tri Le tri par sélection : def triselection(t): """Algorithme de tri par sélection""" N = len(t) # Balayage for k in range(n-1): # Recherche du minimum à partir de k ind = k for i in range(k,n): if T[i] < T[ind]: ind = i # Placement du minimum à l indice k T[ind], T[k] = T[k], T[ind] return T de complexité quadratique dans le pire et le meilleur des cas.
Correction Exercice 1 : Algorithme de tri Le tri à bulle : def tribulle(t): """Tri à bulle""" N = len(t) chgt = True while chgt: for k in range(n-1): chgt = False if T[i]>T[i+1]: T[i], T[i+1] = T[i+1], T[i] chgt = True N -= 1 return T Complexité linéaire dans le meilleur des cas (pour un tableau déjà trié), quadratique dans le meilleur des cas.
Exercice 2. Tri par insertion. C est celui que l on utilise habituellement dans la vie courante, par exemple, pour trier un paquet de carte : On constitue (imaginairement) 2 tas : l un dans la main droite, contenant toutes les cartes avant tri, l autre dans la main gauche, contenant les cartes déjà triées, Initialement la main gauche est vide. Chaque étape consiste à : prendre la première carte du tas non trié L insérer progressivement à sa bonne place dans le tas trié, en la faisant descendre d une position tant que sa valeur reste inférieure à celle de la carte située en dessous-d elle. Après chaque étape le tas non trié contient une carte de moins, le tas trié une carte de plus. A la fin du tri la main droite est vide. La main gauche contient toutes les cartes, triées.
1. Ecrire une fonction triinsertion prenant en paramètre une liste de nombres et qui applique un tri par insertion pour le trier dans le sens croissant. 2. Quel invariant de boucle permet de justifier qu à la fin de l algorithme le tableau est trié? 3. Complexité dans le meilleur des cas : quel est le meilleur des cas? Quelle est alors la complexité? 4. Complexité dans le pire des cas : quel est le pire des cas? Quelle est alors la complexité?
Exercice 2 : corrigé def triinsertion(t): """Tri par insertion""" N = len(t) # Balayage for k in range(1,n): # Insertion i = k while i > 0 and T[i-1] > T[i]: T[i-1], T[i] = T[i], T[i-1] i -= 1 return T Invariant de boucle : Après k insertions les k premiers éléments du tableau sont ordonnés dans le sens croissant. (pour la boucle for).
Exercice 2 : corrigé Meilleur des cas : celui d un tableau déjà trié. La complexité est linéaire (chaque insertion est en O(1)). Pire des cas : celui d un tableau ordonné dans le sens décroissant : complexité quadratique : la k-ième insertion se fait en Θ(k). N 1 k=1 N 1 Θ(k) = Θ ( k=1 k) = Θ ( N(N 1) ) = Θ(N 2 ). 2
Ce tri particulièrement rapide, est basé sur le principe de diviser pour régner : Choisir arbitrairement un élément e dans le tableau T, e Décomposer les autres éléments du tableau en deux sous-tableaux : T- constitué des éléments inférieurs à e, T+ constitué des éléments supérieurs à e. T- e T+ Après appels récursifs sur les deux sous-tableaux T- et T+, le tableau T trié s obtient en concaténant les sous-tableaux T- trié, suivi de e, suivi de T+ trié. T- trié e T+ trié Le tableau T est alors trié.
Exemple : (1) Tri rapide à la main de [7, 3, 6, 4, 2, 5, 1] : Appel sur T = [7, 3, 6, 4, 2, 5, 1] e = 4, T1 = [3, 2, 1], T2 = [7, 6, 5] Appel récursif sur T1 = [3, 2, 1] : e = 2, T11 = [1], T12 = [3] T1 devient : T11 + [2] + T12 = [1, 2, 3] Appel récursif sur T2 = [7,6,5] e = 6, T21 = [5], T22 = [7] T2 devient : T21 + [6] + T22 = [5,6,7] T devient : T1 + [4] + T2 = [1,2,3,4,5,6,7]
1. Ecrire une fonction trirapide prenant en paramètre une liste de nombres T et qui renvoie un tableau trié contenant les mêmes éléments que T. 2. Lorsque l élément e est chaque fois choisi médian, quelle et la complexité de l algorithme? 3. Lorsque l élément e est chaque fois choisi extremal, quelle est la compléxité de l algorithme?
Exercice 3 : def TriRapide(T): N = len(t) if N <= 1: return T e = T.pop(N//2) T1, T2 = [], [] for x in T: if x <= e: T1.append(x) else: T2.append(x) return TriRapide(T1) + [e] + TriRapide(T2)
Exercice 3 : Dans le meilleur des cas, lorsque l élément choisi est médian : N N/2 N/2 N/4 N/4 N/4 N/4
Exercice 3 : La profondeur de l arbre est log 2 (N) puisque : N = 2 log 2 (N). De l ordre de N opérations à chaque étage. C(N) = Θ log 2 (N) k=0 Dans ce cas la complexité est en N log(n). 2 k N 2 k = Θ ((log 2(N) + 1) N) = Θ(N log(n))
Exercice 3 : Dans le pire des cas, lorsque l élément choisi est toujours un élément extrémal (min ou max) : N N 1 1 1 N k 1 1
Exercice 3 : La profondeur de l arbre est N. De l ordre de N k opérations à chaque étage k. C(N) = Θ ( n) = Θ (N N n=1 complexité quadratique (N + 1) ) = Θ (N 2 ) 2
Le tri fusion est un autre exemple d algorithme de tri basé sur le principe de Diviser pour régner. Il est basé sur le fait que fusionner deux tableaux triés en un seul tableau trié T1, T2 se fait en temps linéaire (sur le nombre total d éléments, dans le pire et le meilleur des cas) : i1, i2 = 0 T = tableau vide TANT QUE l on n a pas atteint la fin d un des tableaux: SI T1[i1] <= T2[i2] ALORS Insérer T1[i1] à la fin de T incrémenter i1 SINON Insérer T2[i2] à la fin de T incrémenter i2 FIN TANT QUE Insérer les éléments restants en fin de T
On met alors en oeuvre la récursivité : Décomposer le tableau en deux sous-tableaux de même longueur (±1) Appliquer l algorithme récursivement sur chacun des 2 sous-tableaux Puis fusionner les 2 sous-tableaux triés en un tableau trié. 1. Ecrire une fonction fusion prenant en paramètre deux listes triées, et qui les fusionne en une liste triée dans le sens croissant, qu elle renverra. 2. Ecrire une fonction trifusion qui exécute le Tri fusion. 3. Quelle est sa complexité dans le pire des cas? dans le meilleur des cas?
Exercice 4 : correction def fusion(t1,t2): T = [] i1 = i2 = 0 n1, n2 = len(t1), len(t2) while i1 < n1 and i2 < n2 : if T1[i1] <= T2[i2]: T.append(T1[i1]) i1 += 1 else: T.append(T2[i2]) i2 += 1 while i1 < n1: T.append(T1[i1]) i1 += 1 while i2 < n2: T.append(T2[i2]) i2 += 1 return T
Exercice 4 : correction def trifusion(t): N = len(t) if N == 1: return T T1, T2 = trifusion(t[:n//2]), trifusion(t[n//2:]) return fusion(t1,t2) Dans le pire et le meilleur des cas, la fonction fusion étant de complexité linéaire, trifusion est de complexité θ(n log(n)). N N/2 N/2 N/4 N/4 N/4 N/4