mise au point

Transcription

mise au point
Sur la Complexité de Quicksort (très optionnel)
(L1, cours Python n◦ 6)
jpr, Nice, 12 Octobre 2016
1
Le Pire des Cas
L’exercice de TD 6.2 comportait une coquille. La complexité du tri rapide Quicksort n’est pas en n log(n)
dans le pire des cas, mais en moyenne (voire dans le meilleur cas). Dans le pire des cas – par exemple où la
liste L est déjà croissante et où le pivot est le premier élément L[0] – la phase de scission produit avec un
coût O(n) pour L1 la liste vide et pour L2 la liste L[1:]. Le même phénomène se répète pour trier L2 avec
un coût de n − 1, etc et l’on obtient un coût final en 1 + 2 + 3 + ... + n = n(n + 1)/2 = O(n2 ). Le pire des
cas a donc un coût quadratique. Mais il ne nous intéressait pas dans cet exercice.
2
L’Analyse en Moyenne
Nous sommes bien d’accord que cette lecture est optionnelle et hors-programme pour l’examen. Elle enchantera au moins les matheux. Les analyses d’algorithme en moyenne sont en général difficiles, car qui dit
moyenne dit probabilité. J’essaye de faire au plus simple sans trop de dégâts.
Le pivot peut se retrouver au final en position k (0 ≤ k < n) avec une probabilité 1/n. S’il est en position k,
la liste L1 sera de longueur k et la liste L2 sera de longueur n − k − 1.
L1
L2
p
0
k
n−1
Dans ce cas, que vaut le coût cn de trier une liste L de longueur n ? Il faudra payer un coût linéaire pour
l’étape de scission de L en L1 et L2, puis un coût ck pour trier par récurrence L1, puis un coût cn−k−1 pour
trier L2, puis enfin un coût linéaire pour concaténer LT1, le pivot et LT2. Simplifions le coût linéaire en
simplement n (les fameuses mathématiques de combat 1 ). Au final, pour n ≥ 2 :
cn = ck + cn−k−1 + n
Oui mais voilà, le pivot n’a qu’une chance sur n d’être en position k. On fait donc la moyenne pondérée de
ces possibilités et il vient pour le cas moyen :
cn =
n−1
n−1
1X
1X
(ck + cn−k−1 + n) =
(ck + cn−k−1 ) + n
n
n
k=0
k=0
Travaillons un peu au chalumeau cette expression redoutable.
cn =
1
(c0 + cn−1 + c1 + cn−2 + · · · + cn−1 + c0 ) + n
n
En regroupant les deux sommes inversées, on parvient à une forme un peu plus simple (En ) :
cn =
n−1
2X
ck + n
n
k=0
1. Cherchez street fighting math sur Google...
soit encore :
ncn = 2
n−1
X
ck + n2
(En )
k=0
Ah-ah, on utilise alors l’astuce des sommes télescopiques en calculant (En ) − (En−1 ). Les deux sommes vont
en effet se détruire mutuellement et il ne restera que des miettes :
ncn = 2
n−1
X
ck + n2
(En )
k=0
(n − 1)cn−1 = 2
n−2
X
ck + (n − 1)2
(En−1 )
k=0
et en faisant (En ) − (En−1 ), le télescopage produit :
ncn − (n − 1)cn−1 = 2cn−1 + 2n
soit :
ncn = (n + 1)cn−1 + 2n
ou encore :
cn−1
2
cn
=
+
n+1
n
n+1
cn
) dont il est facile de calculer le terme général
ce qui n’est qu’une définition par récurrence de la suite ( n+1
en déroulant la récurrence :
cn−2
2
2
cn−3
2
2
2
c1
2 2
2
2
cn
=
+ +
=
+
+ +
= ··· =
+ ( + + ··· + +
)
n+1
n−1 n n+1
n−2 n−1 n n+1
2
3 4
n n+1
d’où en introduisant la somme harmonique Hn = 1 +
1
2
+
1
3
+ ··· +
1
n
et en tenant compte de c1 = 0 :
3
cn = 2(n + 1)(Hn+1 − ) ≈ 2nHn
2
Or Hn ≈ ln(n) + γ où γ ≈ 0.577 est la constante d’Euler 2 (la somme 1 +
au final (ouf) :
cn ≈ 2n ln n
Je ne sais pas vous, mais moi, ça m’a donné soif tout ça...
2. Voir par exemple sur Internet la Wikipédia à l’article Série Harmonique.
1
2
+
1
3
+ · · · est donc infinie). Donc