CPGE Oujda Spé
Tri (quicksort)
Le tri rapide (en anglais quicksort) est un algorithme
de tri inventé par C.A.R. Hoare en 1961 et fondé sur la
méthode de conception diviser pour régner.
La complexité moyenne du tri
rapide pour n éléments est proportionnelle à n log n, ce
qui est optimal pour un tri par comparaison, mais la complexité dans le pire
des cas est quadratique. Malgré ce désavantage théorique, c'est en pratique un
des tris les plus rapides, et donc un des plus utilisés. Le tri rapide ne peut
cependant pas tirer avantage du fait que l'entrée est déjà presque triée. Dans
ce cas particulier, il est plus avantageux d'utiliser le tri
par insertion
Description
de l'algorithme
La méthode consiste à placer un
élément du tableau (appelé pivot) à sa place définitive, en permutant tous les
éléments de telle sorte que tous ceux qui sont inférieurs au pivot soient à sa
gauche et que tous ceux qui sont supérieurs au pivot soient à sa droite.
Cette opération s'appelle le
partitionnement. Pour chacun des sous-tableaux, on définit un nouveau pivot et
on répète l'opération de partitionnement. Ce processus est répété récursivement, jusqu'à ce que l'ensemble des éléments soit
trié.
Concrètement, pour partitioner un sous-tableau :
Autre Exemple
:




Solution récursive avec fonction
unique
def Trirapide(L) :
Solution avec partitionnement :
def partitionner(T,premier,dernier) :
def triRapide(T,premier,dernier)
:
Choix du
pivot et complexité
Pivot
arbitraire
Une manière simple de choisir le
pivot est de prendre toujours le premier élément du sous-tableau courant (ou le
dernier).la complexité moyenne du tri rapide en sélectionnant le pivot de cette
façon est Θ(n log n). Cependant, la complexité
dans le pire cas est Θ(n2).
Si on prend comme pivot le milieu du
tableau, le résultat est identique.
Pivot
aléatoire
Si on utilise la méthode donnée dans
la description de l'algorithme, c'est-à-dire choisir le pivot aléatoirement,
alors la complexité moyenne du tri rapide est Θ(n
log n) sur n'importe quelle entrée. Dans le pire cas, la complexité est Θ(n2). Néanmoins, l'écart
type de la complexité est seulement Θ(n), ce qui signifie que
l'algorithme s'écarte peu du temps d'exécution moyen.
Dans le meilleur cas, l'algorithme
est en Θ(n log n).
Pivot
optimal
En théorie, on pourrait faire en
sorte que la complexité de l'algorithme soit Θ(n
log n) dans le pire cas en prenant comme pivot la valeur médiane du sous-tableau. L'algorithme BFPRT
ou médiane des médianes permet en effet de
calculer cette médiane de façon déterministe en temps linéaire. Mais
l'algorithme n'est pas suffisamment efficace dans la pratique, et cette
variante est peu utilisée.
(En théorie des probabilités et en statistiques, une médiane d'un ensemble de valeurs (échantillon, population, distribution de
probabilités) est une valeur m qui permet de couper l'ensemble des
valeurs en deux parties égales : mettant d'un côté une moitié des valeurs,
qui sont toutes inférieures ou égales à m et de l'autre côté l'autre moitié des
valeurs, qui sont toutes supérieures ou égales à m (s'il y a un nombre impair de valeurs,
la valeur centrale sera mise des deux côtés). Intuitivement, on peut dire que
la médiane est le point milieu de l'ensemble1, qu'elle
divise en deux moitiés. C'est une caractéristique de position de la
série. On peut déterminer une médiane pour un ensemble de valeurs non
numériques1 pour
autant qu'on puisse choisir un critère d'ordonnancement de ces valeurs.)
Complexit´e du tri rapide(calcul mathématique)
L’´etude math´ematique est en section
suivante @[Complexit´es du tri rapide].
Pour calculer la complexit´e de l’algorithme du tri rapide
on part de la d´efinition r´e- cursive du tri rapide. Soit C (T )
le nombre de comparaisons pour trier un tableau
T et soit P (T ) le nombre de comparaisons pour partitionner T selon
le pivot. Si la partition
r´epartit les ´el´ements dans deux tableaux
T1 et T2, on obtient :
C (T ) = P (T ) + C (T1) + C (T2)
Si
T est de taille n, le
nombre de comparaisons pour r´ealiser la partition ne peut ˆetre plus petit que n − 1 (il faut comparer chacun des autres ´el´ements au pivot pour d´ecider du sous-tableau dans lequel il doit se trouver). On supposera donc que P (T ) = n −
1. Dans l’algorithme partitionner, le nombre de comparaisons pour partitionner un tableau de taille
n peut aller jusqu’`a n + 1 mais cela ne change pas l’ordre de grandeur du r´esultat final.
Mais pour ce qui est de la place du pivot, et donc des tailles de T1 et T2, la situation
est tr`es variable :
1.
Si
le
pivot est le plus petit
(ou
plus
grand)
´el´ement du tableau,
l’un
des
deux
tableaux de la partition aura une taille nulle et l’autre
aura une taille de n − 1.
2. Si le pivot est l’´el´ement
m´edian, les deux tableaux T1 et T2 auront pour
taille
(environ) la moiti´e de n.
3.
En g´en´eral si la place du pivot est k, il reste `a traiter r´ecursivement un tableau de taille
k − 1 et un tableau de taille n − k.
Complexit´e au pire Le
premier
cas
est
le
pire
des
cas
possibles (et il se produit
lorsque le tableau initial est tri´e – en ordre croissant ou d´ecroissant). Dans ce cas, pour
un tableau de taille n, le nombre de comparaison pour trier est :
Cmax(n) = n − 1 + Cmax(n −
1)
Et avec le cas initial Cmax(1)
= 0, la r´ecurrence se r´esout en :
Cmax(n) = n(n −
1)/2 = O(n2)
Complexit´e au meilleur Le second cas
est
le
meilleur
des
cas
possibles. En effet,
s’il se r´ep`ete `a toutes les ´etapes r´ecursives, on a finalement pour le nombre minimal de comparaisons pour trier un tableau
de taille n :
Cmin(n) = n − 1 + Cmin(bn/2c) + Cmin(dn/2e)
Et avec le cas initial Cmin(1)
= 0, cette r´ecurrence se r´esout en :
Cmin(n) ∼
n lg n = Ω(n lg n)
La r´esolution de cette r´ecurrence donne :
Cmoy (n) ∼
2n lg n = Θ(n lg n)
Conclusion
Ainsi la complexit´e
moyenne
du
tri
rapide
est
en
Θ(n lg n). Par
exemple
pour un tableau d’un million d’´el´ements, le tri par insertion n´ecessite de un milliard de comparai- sons en moyenne alors que le tri
rapide n’en fera en moyenne qu’une vingtaine
de millions (106 ≈
220 donc lg 106 ≈ 20).
Toutefois dans le pire cas, le tri rapide est en O(n2). Et cela est d’autant plus gˆenant
que ce cas apparaˆıt pour des tableaux
tri´es. Il existe
cependant plusieurs parades
pour
´eviter
le
cas
le
pire,
en
faisant en sorte que le pivot
se
place
toujours
dans
la
r´egion m´ediane du tableau
pour avoir un tri en temps proportionnel
`a n lg n (voir
plus
loin
@[Rendre le tri rapide efficace]).
Le tri rapide est consid´er´e en g´en´eral comme le tri le plus efficace et c’est par exemple
le tri utilis´e dans la plupart des syst`emes d’exploitation.
Solution1 :
partitionner(tableau T, premier,
dernier, pivot)
échanger T[pivot]
et T[dernier]
j := premier
pour i de premier
à dernier - 1
si T[i] <=
T[dernier] alors
échanger
T[i] et T[j]
j := j + 1
échanger
T[dernier] et T[j]
renvoyer j
tri_rapide(tableau t, entier
premier, entier dernier)
début
si premier <
dernier alors
pivot := choix_pivot(t, premier, dernier)
pivot :=
partitionner(t, premier, dernier, pivot)
tri_rapide(t ,premier, pivot-1)
tri_rapide(t, pivot+1, dernier)
fin si
fin
Solution recurive
avec fonction unique
def Trirapide(L) :
if L == [ ] :
return [ ]
else :
n = len(L)-1 #on va balayer la liste L et r_epartir
les valeurs
L1 = [ ]
L2 = [ ]
for k in range(1,n+1) :
if L[k]<=L[0]
:
L1.append(L[k]) #L1
re_coit les _el_ements plus
petits
else :
L2.append(L[k]) #L2
re_coit les _el_ements plus
grands
L = Trirapide(L1)+[L[0]]+Trirapide(L2)
return L
Exemple :
>>>T=Trirapide([10,39,21,2,8,6,1])
>>>print(T) vaut
[1; 2; 6; 8; 10; 21; 39]
Solution 3 avec
partitionnement :
def partitionner(T,premier,dernier) :
p=T[dernier]
j=premier
for i in range(premier,dernier) :
if T[i]<=p
:
T[i],T[j]=T[j],T[i]
j=j+1
T[dernier],T[j]=T[j],T[dernier]
return j
def triRapide(T,premier,dernier)
:
if premier
< dernier :
pivot=partitionner(T,premier,dernier)
triRapide(T,premier,pivot-1)
triRapide(T,pivot+1,dernier)