BCPST2
9 5
0 4 Tris
Dans tout le TP, on suppose que l'on dispose d'une listeLconstituée de nombres (entiers ou ottants) dont on souhaite ordonner ses éléments par ordre croissant. On notenla longueur de cette liste.
Nous allons étudier diérents algorithmes de tris.
Solution: On remarquera que les fonctions de tris n'ont pas de return.
En eet, ces fonctions sont à eets de bords, c'est-à-dire qu'elle modie la liste L passée en argument et que cette modication est eective en dehors de la fonction.
Ce comportement qui peut être destabilisant au début est un comportement normal, qui évite les recopie inutile de liste et permet au langage d'être ecace.
1 re Partie : LE TRI PAR SELECTION
Principe : Le tri par sélection est l'un des tris les plus instinctifs.
Il consiste à trouver la position du plus grand élément ; le plus grand élément est alors échangé avec le dernier élément de la liste. Puis, on recommence ces opérations sur les(n−1)premiers éléments de la liste, et ainsi de suite ... jusqu'à ce qu'il n'y ait plus qu'un élément à trier ; celui-ci est alors le plus petit élément de la liste.
Exemple :
©
Par exemple, si L=[6,2,8,5,4,1], alors on obtient successivement :
(les cases en gris correspondent aux éléments bien positionnés)
6 2 8 5 4 1 on échange 1 et 8
6 2 1 5 4 8 on échange 4 et 6
4 2 1 5 6 8 le 5 est à sa place
4 2 1 5 6 8 on échange 4 et 1
1 2 4 5 6 8 le 2 est à sa place 1 2 4 5 6 8 la liste est triée
1◦) Écrire une fonction rg_max(L) qui prend en argument une liste L et qui renvoie la position dans la liste L du plus grand élément.
Solution:
def rg_max ( L ) : M = L [ 0 ] r = 0
f o r i in range ( len ( L ) ) : i f M< L [ i ] :
M = L [ i ] return r r =i
2◦) En utilisant la fonction précédente, écrire une fonction tri_selection(L) qui trie la liste L selon la méthode de tri par sélection.
Solution:
def tri_selection ( L ) : n = len ( L )
f o r i in range ( n − 1 ,0 , − 1) : #i n t e r v a l l e [ 1 , n [ à l ' envers r = rg_max ( L [ 0 : i +1])
L [ r ] , L [ i ] = L [ i ] , L [ r ]
2 e Partie : LE TRI PAR INSERTION
Principe : Le tri par insertion est un autre algorithme naïf , que la plupart des personnes utilisent naturellement pour trier des cartes (prendre les cartes mélangées une à une sur la table, et former une main en insérant chaque carte à sa place).
Au début, on considère que la liste constituée du seul premier élément est trié ; puis on trie les deux premiers éléments ; ensuite on met le troisième élément à sa place parmi les deux premiers, et ainsi de suite ...
De manière générale, au moment où l'on considère lek-ième élément, les éléments qui le précèdent sont tous déjà triés. Il faut donc trouver le rang où cet élément doit être inséré en le comparant aux autres, puis décaler les éléments an de pouvoir eectuer l'insertion.
Exemple :
©
Par exemple, si L=[6,2,8,5,4,1], alors on obtient successivement :
(les cases en gris correspondent aux éléments déjà traités)
6 2 8 5 4 1
2 6 8 5 4 1 on insère le 2 à sa place 2 6 8 5 4 1 on insère le 8 à sa place 2 5 6 8 4 1 on insère le 5 à sa place 2 4 5 6 8 1 on insère le 4 à sa place
1 2 4 5 6 8 on insère le 1 à sa place, et la liste est triée
Programmation :
Écrire une fonction tri_insertion(L) qui trie la liste L selon la méthode de tri par insertion.
Solution:
def insere ( L , i , j ) :
# i n s e r e l ' é lement de rang i à l a p o s i t i o n j , i c i on suppose j<i a = L . pop ( i ) #e n l è ve l ' é l ément d ' i n d i c e i et l e s t r o c k e dans a L . insert ( j , a )
def tri_insertion ( L ) : n = len ( L )
f o r i in range ( n ) :
j=0 while L [ i ] > L [ j ] and j<i : j +=1
insere ( L , i , j )
3 e Partie : LE TRI BULLE
Principe : Le tri bulle consiste à regarder les diérentes valeurs adjacentes d'une liste, et à les permuter si le premier des deux éléments est supérieur au second.
Plus précisément, les deux premiers éléments de la liste sont comparés : si le premier est supérieur au second, une permutation est eectuée.
Ensuite sont comparés et éventuellement permutés les deuxième et troisième éléments, ..., jusqu'aux élémentsn−1etn. Une fois cette étape achevée, le dernier élément du vecteur est le plus grand.
On recommence l'opération sur lesn−1éléments restants, et ainsi de suite. On s'arrête lorsqu'il n'y a plus qu'un seul élément.
Cet algorithme porte le nom de tri bulle, car le plus grand élément remonte petit à petit vers le haut, comme une bulle ! Exemple :
©
si L=[6,2,8,5,4,1], alors on obtient successivement :
(les cases en gris correspondent aux éléments qui sont comparés et éventuellement échangés à l'étape suivante, et les cases en bleu aux éléments bien positionnés)
6 2 8 5 4 1 on échange 6 et 2
2 6 8 5 4 1 on ne fait rien
2 6 8 5 4 1 on échange 8 et 5
2 6 5 8 4 1 on échange 8 et 4
2 6 5 4 8 1 on échange 8 et 1
2 6 5 4 1 8 après un premier parcours, le 8 est remonté ... ... ...
2 5 4 1 6 8 après un deuxième parcours, le 6 est remonté ... ... ...
2 4 1 5 6 8 après un troisième parcours, le 5 est remonté ... ... ...
2 1 4 5 6 8 après un quatrième parcours, le 4 est remonté ... ... ...
1 2 4 5 6 8 après un cinquième parcours, le 2 est remonté Programmation :
Écrire une fonction tri_bulle(L) qui trie la liste L selon la méthode de tri bulle.
Solution:
def tri_bulle ( L ) : n = len ( L )
f o r i in range ( n −1) : f o r j in range ( n − i − 1) :
i f L [ j]>L [ j +1]:
L [ j ] , L [ j+1] = L [ j +1] , L [ j ]
4 e Partie : LE TRI RAPIDE
Principe : Le tri rapide (ou quicksort en anglais) est un algorithme très ecace, de type dichotomique.
La méthode consiste à choisir un élément au hasard dans la liste, appelé pivot. Ensuite on permute les éléments de telle sorte que tous ceux qui sont inférieurs ou égaux au pivot soient à sa gauche et que tous ceux qui sont strictement supérieurs au pivot soient à sa droite. Cette opération s'appelle le partitionnement. Le pivot est alors à sa place dénitive.
Puis on recommence sur chacune des deux sous-listes, et ainsi de suite ... jusqu'à obtenir des sous-listes vides ou réduites à un élément.
Exemple :
©
Par exemple, si L=[8,2,6,5,4,1,9,3,0,7], alors on obtient successivement :
(les cases en bleu correspondent aux pivots choisis pour passer à la ligne suivante et les cases en gris aux éléments bien positionnés)
8 2 6 5 4 1 9 3 0 7 on choisit 6 comme pivot
le 6 est à sa place dénitive
2 5 4 1 3 0 6 8 9 7 on recommence avec les deux sous-listes obtenues on choisit 4 et 9 comme pivots
2 1 3 0 4 5 6 8 7 9 et ainsi de suite ...
1 0 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9 la liste est triée
Programmation :
Écrire une fonction tri_rapide(L) qui trie la liste L selon la méthode de tri rapide.
Solution:
def partitionne ( L , rg_pivot ) :
# Au dé but on met l e pivot au rang 0 , on l e metttra à sa p l a c e à l a f i n .
# Les p e t i t s é l é ments sont à gauche , l e u r i n d i c e e s t donné par i m i n i
# Les grands à d r o i t e , l e u r i n d i c e e s t donné par imaxi
# Quand imin=imaxi , tous l e s é l é ments auront é t é mis en p l a c e
#( s a u f l e pivot ) n = len ( L )
pivot = L [ rg_pivot ]
L [ 0 ] , L [ rg_pivot ]= L [ rg_pivot ] , L [ 0 ] imini = 1
imaxi = n−1
while imini <= imaxi : i f L [ imini ] < pivot :
imini += 1
# On l a i s s e l e s p e t i t s é l é ments à l e u r s p l a c e s et on l e s compte . e l s e :
L [ imini ] , L [ imaxi]= L [ imaxi ] , L [ imini ] imaxi −= 1
# Les grands é l é ments sont mis à d r o i t e
#et on garde en mé moire l e rang
# pour c o n t i n u e r à r e m p l i r plus tard .
L [ 0 ] , L [ imini −1]= L [ imini −1] , L [ 0 ] # on remet l e pivot à sa p o s i t i o n return imini − 1
def tri_rapide ( L ) : n = len ( L )
i f len ( L ) > 1 :
rg = random . randint (0 , n−1) r = partitionne ( L , rg )
M = tri_rapide ( L [ : r ] ) +[L [ r ]]+ tri_rapide ( L [ r + 1 : ] ) e l s e :
M = [ x f o r x in L ]
return M
5 e Partie : Comparaison des diérentes méthodes
La fonction time() du module time renvoie le temps en secondes écoulé depuis le 1er janvier 1970 à 00 : 00 : 00.
Ainsi, la fonction suivante permet de connaître la vitesse d'exécution de la fonction fct_tri pour trier la liste L.
def duree ( fct_tri , L ) : debut=time ( ) fct_tri ( L ) fin=time ( )
return ( fin−debut )
1◦) Écrire une fonction liste(n) qui crée une liste de longueur n, constituée d'entiers choisis aléatoirement dansJ0 ; 10nK.
Solution:
def liste ( n ) :
return [ random . randint (0 , 10 ∗ n ) f o r i in range ( n ) ]
2◦) Écrire une fonction tps_moyen(fct_tri,n) qui crée 5 listes de longueur n à l'aide de la fonction précédente, calcule le temps nécessaire pour trier ces listes avec la fonction fct_tri et qui renvoie le temps moyen obtenu.
Solution:
def tps_moyen ( fct_tri , n ) : tps = 0
f o r i in range ( 5 ) : L = liste ( n )
tps += duree ( fct_tri , L ) tps = tps /5
return tps
3◦) Appliquer la fonction précédente aux quatre fonctions de tris du TP, et remplir le tableau suivant des diérents temps moyens obtenus :
listes de longueur 50 100 500 1000 2000 5000
tri par sélection tri par insertion tri bulle tri rapide
4◦) Pour une liste de longueurn, on noteT(n)le temps moyen obtenu précédemment pour trier la liste.
Tracer, pour chaque fonction de tris,ln(T(n))en fonction deln(n). Que peut-on en déduire ?