• Aucun résultat trouvé

Algorithmes de tris

N/A
N/A
Protected

Academic year: 2022

Partager "Algorithmes de tris"

Copied!
19
0
0

Texte intégral

(1)

Algorithmes de tris

Judicaël Courant 2018-W38-6 (22 septembre)

Table des matières

1 Introduction 2

1.1 Généralités . . . 2

1.2 Trier . . . 2

1.3 Remarques . . . 3

2 Tri par sélection 4 2.1 Algorithme . . . 4

2.2 Implantation en Python . . . 5

2.3 Correction de l’algorithme . . . 6

2.4 Complexité . . . 7

2.5 Conclusion . . . 7

3 Tri par insertion 7 3.1 Algorithme . . . 7

3.2 Implantation en Python . . . 8

3.3 Correction . . . 9

3.4 Complexité . . . 9

3.5 Conclusion . . . 11

4 Tri fusion 11 4.1 Algorithme . . . 11

4.2 Implantation en Python . . . 11

4.3 Correction . . . 12

4.4 Complexité . . . 12

4.5 Conclusion . . . 15

5 Tri par pivot 15 5.1 Algorithme . . . 15

5.2 Implantation en Python . . . 16

5.3 Correction . . . 17

5.4 Complexité . . . 17

5.5 Conclusion . . . 19

(2)

1 Introduction

1.1 Généralités

Un problème fondamental

Letriest un problème fondamental de l’algorithmique. En général, son but est de préparer des données pour permettre d’y rechercher rapidement une information.

Motivations

Dans des données triées, il est facile de :

— chercher rapidement si un élément est présent ou non ;

— trouver les doublons ;

— trouver une information associée à une donnée (exemple d’utilisation : index d’un livre) ;

— chercher le k-ième plus grand élément d’un ensemble.

Notion de clé

— Les données qu’on veut trier ne sont pas nécessairement munies d’un ordre total (ca- nonique).

— En général, pour trier des données, on leur appliquera une fonction, appeléeclé de tri oucritère de tri à valeurs dans un ensemble totalement ordonné

— On dira que les données d’un tableau t sont triées (par ordre croissant) si elles sont rangées par ordre croissant des clés.

Exemples

— Tri de données représentant des personnes par nom de famille croissant pour l’ordre lexicographique.

— Tri de données représentant des personnes par date de naissance croissante.

Remarque

Dans la suite de ce document :

— on triera des données déjà munies d’un ordre en Python et on les triera avec cet ordre

— on supposera que la complexité en temps et en espace de la comparaison de deux éléments est constante.

1.2 Trier Cadre

Considérons un algorithme de tri qui prend en entrée un tableau contenant des données u0, . . . , up−1 et :

— soit retourne un nouveau tableau (appelons t0, . . . , tq−1 son contenu) ;

— soit modifie ce tableau (appelons t0, . . . , tq−1 son contenu après l’exécution de l’algo- rithme).

(3)

Ce que trier veut dire

Définition 1 (Tri d’un tableau). On dit que l’algorithme a trié le tableau si les deux propriétés suivantes sont vérifiées :

1. test trié par ordre croissant1 de la clé choisie.

2. tetu contiennent les mêmes éléments avec les mêmes multiplicités.

Une caractérisation Proposition

Deux listes de donnéesu0, . . . , up−1 ett0, . . . , tq−1 contiennent les mêmes éléments avec les mêmes multiplicités si et seulement si p =q et il existe une permutation σ :J0, pJ tel que ti=uσ(i) pour touti∈J0, pJ.

Conséquence

Pour montrer qu’un algorithme trie un tableau, il suffit donc de montrer les deux points suivants :

1. Le tableau obtenu est rangé par ordre croissant.

2. Le tableau obtenu est une permutation du tableau initial.

1.3 Remarques Stabilité

Définition 2 (Stabilité d’un tri). Un tri est dit stable s’il ne modifie jamais l’ordre relatif de deux éléments de même clé.

Utilité des tris stables

Permettre de trier par ordre lexicographique de deux clés en triant pour la deuxième puis la première clé.

Question

On trie la liste suivante par ordre lexicographique de nom :

— Dupont, Pierre,

— Martin, Jacques,

— Durand, Jacques,

— Martin, Claude,

— Dupont, Jean.

On obtient :

— Dupont, Pierre,

— Dupont, Jean,

— Durand, Jacques,

— Martin, Claude,

— Martin, Jacques.

1. ou décroissant suivant le contexte.

(4)

L’algorithme de tri utilisé était-il stable ?

— A. Oui.

— B. Non (cas des Dupont).

— C. Non (cas des Martin).

— D. On ne peut pas savoir.

Tri en place

Définition 3(Algorithme en place). On dit qu’un algorithme s’effectueen places’il travaille en utilisant au plus un espace mémoire constant (en plus de ses données d’entrée).

NotePar abus de langage, on considère parfois qu’un tri s’effectue en place s’il utilise un faible es- pace mémoire supplémentaire. En particulier, s’il utilise au plus un espace mémoireO(logn) car en pratique log2nne dépasse pas quelques dizaines .

2 Tri par sélection

2.1 Algorithme Principe

— Étant donné un tableau de nvaleursL[0], …,L[n−1], on cherche le minimum : L[i].

On échangeL[0] etL[i].

— On cherche le minimum deL[1],…,L[n−1], et on l’échange avecL[1].

— . . .

— On cherche le minimun de L[k],…,L[n−1], et on l’échange avec L[k].

— . . .

— On cherche le minimum deL[n−2]etL[n−1]et on l’échange avecL[n−2].

Exemple 2 1 5 9 0 4

| {z }

min=0

: min(T[0 :]) = 0 que l’on échange avecT[0] = 2 0 1 5 9 2 4

| {z }

min=1

: min(T[1 :]) = 1qu’on laisse en place 0 1 5 9 2 4

| {z }

min=2

: min(T[2 :]) = 2que l’on échange avec T[2] = 5 0 1 2 9 5 4

| {z }

min=4

: min(T[3 :]) = 4que l’on échange avec T[3] = 9 0 1 2 4 5 9

min=5|{z}

: min(T[4 :]) = 5qu’on laisse en place 0 1 2 4 5 9

min=9|{z}

: min(T[5 :]) = 9qu’on laisse en place.

QCM Stabilité

Le tri par sélection est-il stable ?

(5)

En place

S’agit-il d’un tri en place ? 2.2 Implantation en Python Échange d’éléments

def swap(t, i, j):

"""Échange les éléments d'indice i et j dans t, tableau.

Précondition : t tableau et i et j entiers dans l'intervalle range(len(t)).

"""

t[i], t[j] = t[j], t[i]

return

Indice de l’élément minimum d’un sous-tableau def imin(t, i):

"""Retourne j tel que t[j] == min(t[i], ..., t[n-1]) où n est la longueur de t

Précondition : t tableau de taille n et i entier avec 0 <= i < n"""

j = i

for k in range(i+1, len(t)):

# t[j] == min(t[i], ..., t[k-1]) if t[k] < t[j]:

j = k return j

QCML’exécution de imin(t, i), où test un tableau de taillenprend un temps

— A. O(1);

— B. O(n−i);

— C. O(n);

— D. O(n2).

Fonction principale def tri_selection(t):

"""Trie le tableau t par ordre croissant"""

n = len(t)

for i in range(n-1):

# les i premiers éléments de t sont triés

# et inférieurs ou égaux aux autres éléments de t.

j = imin(t, i) swap(t, i, j) return

(6)

QCML’exécution de tri_selection(t), où test un tableau de taille nprend un temps

— A. O(n);

— B. O(n(n−1)/2);

— C. O(n(n+ 1)/2);

— D. O(n2).

2.3 Correction de l’algorithme Correction

Proposition

Ce programme est correct : il trie par ordre croissant le tableau t qui lui est passé en argument

Il y a deux points à montrer :

1. Le contenu de t après l’exécution du programme est obtenu par application d’une permutation à son contenu avant l’exécution.

2. Après l’exécution de la procédure,t est trié.

Démonstration

1. Les seules modifications qu’on effectue sur le tableau sont des échanges de deux élé- ments. À la fin du programme on a donc appliqué au tableau la composée d’un nombre fini de transpositions. On a donc simplement permuté les éléments du tableau.

2. On va :

— Montrer que l’invariant de boucle est bien vérifié.

— En déduire que le tableautest trié à la fin de l’exécution.

Justification de l’invariant de boucle

1. Au début du premier tour de boucle (pouri= 0), l’invariant de boucle est vérifié : les 0premiers éléments de tsont triés, inférieurs ou égaux auxn suivants.

2. Si au début du touri, l’invariant est vérifié, notonst00, . . . , t0n−1 les éléments du tableau au début du tour de boucle ett000, . . . , t00n−1 ceux à la fin. Alors :

— (t000, . . . , t00i−1) = (t00, . . . , t0i−1)donc lesipremiers éléments det00sont triés par ordre croissant.

— t00i est le plus petit des élémentst0i, . . . , t0n−1qui sont tous plus grands quet00, . . . , t0i−1, donct000, . . . , t00i est trié par ordre croissant.

— Les éléments de t000, . . . , t00i−1 sont plus petits que ceux de t00i+1, . . . , t00n−1 et t00i est plus petit que t00i+1, . . . , t00n−1 donc les i+ 1 premiers éléments de t00 sont tous plus petits que lesn−i−1autres.

L’invariant est donc vérifié au début du tour suivant.

Conclusion

D’après l’invariant de boucle, à la fin de l’exécution :

— lesn−1 premiers éléments detsont triés

— ils sont plus petits que t[n−1](qui est le nième) Donct est trié.

(7)

2.4 Complexité Complexité de imin

— On supposera que les comparaisons d’éléments de tsont des opérations élémentaires.

— Le nombre de d’opérations élémentaires effectuées par imin(t, i)est dominépar le nombre de comparaisons entre éléments det.

— Un appel àimin(t, i) effectuelen(t)-(i+1) comparaisons.

— Donc le temps d’exécution deimin(t, i) est unO(len(t)-i). Complexité de tri_selection

— Le temps d’exécution de tri_selection(t) est dominé par le temps des appels à imin(t, i), car l’appel àswap s’effectue en temps constant.

— Pour un tableau de taille n, il est donc dominé par

n−2

X

i=0

(n−i−1) =

n−1

X

k=1

k= n(n−1) 2 Complexité temporelle du tri par sélection

La complexité en temps de tri_selection(t) pour un tableau t de taille n est donc un O(n2), dans le cas le pire comme dans le cas le meilleur.

Complexité spatiale

— Pas d’appels récursifs : espace de pile utilisé O(1).

— Aucune structure de données auxiliaire : espace mémoire utilisé autre que la pileO(1) Complexité spatiale du tri par sélection

La complexité spatiale du tri par sélection est constante.

2.5 Conclusion

Conclusion sur le tri par sélection

— Le tri par sélection est assez facile à écrire.

— Sa complexité spatiale est constante.

— Il a une complexité temporelleO(n2)dans le cas le pire comme dans le cas le meilleur, il ne permet donc pas de trier de grands tableaux.

— Il ne sait pas tirer parti du fait que les données à trier sont déjà triées, ou presque triées.

3 Tri par insertion

3.1 Algorithme Principe

On procède élément par élément. Si lesipremiers éléments sont triés, on prend le(i+ 1) ième et on l’insère dans le paquet des éléments déjà triés en le faisant glisser vers la gauche et en s’arrêtant dès que :

— on est en première position

— ou l’élément à sa gauche lui est inférieur ou égal

(8)

Exemple

D F A G B

D F A G B

D F A G B

A D F G B

A D F G B

A B D F G Stabilité

Le tri par insertion est-il stable ?

— A. Oui.

— B. Non.

3.2 Implantation en Python Position

def position(t, n):

"""t[:n] étant supposé trié, retourne la position à laquelle insérer t[n] dans t[:n].

Précondition: n < len(t)."""

for k in range(n, 0, -1):

# les éléments de t[k:n] sont str. supérieurs à t[n]

if t[k-1] <= t[n]:

return k

# les éléments de t[:n] sont str. supérieurs à t[n]

return 0 Décalage

def decale(t, k, n):

"""Décale les éléments de t[k:n] d'un cran vers la droite. t[k] est remplacé par la valeur de t[n].

Précondition: 0 <= k <= n < len(t)."""

v = t[n]

for i in range(n, k, -1):

t[i] = t[i-1]

t[k] = v Insertion

def insere(t, n):

"""Précondition: t[:n] trié et len(t) >= n+1.

Postcondition: t[:n+1] trié. Les n+1 premiers éléments de t sont préservés mais pas nécessairement leur ordre.

(9)

Les autres éléments de t sont inchangés."""

k = position(t, n) decale(t, k, n)

QCMinsere(t, x), où test un tableau de taillen prend un temps

— A. O(1);

— B. O(n);

— C. O(nlogn);

— D. O(n2). Fonction principale def tri_insertion(u):

"""Trie le tableau u par insertion."""

for k in range(1, len(u)):

# invariant : u[:k] est trié.

insere(u, k)

# u est trié.

QCMtri_insertion(t), oùt est un tableau de taillenprend un temps

— A. O(1);

— B. O(n);

— C. O(nlogn);

— D. O(n2). 3.3 Correction

Correction du tri par insertion Le tableau est trié à la fin

C’est une conséquence directe de l’invariant de boucle.

— Cet invariant est vrai avant l’entrée dans la boucle.

— Il est préservé par chaque tour de boucle.

Le tableau final est une permutation du tableau initial

En effet, le tableau n’est modifié que dans la fonction decalage(t, k, n), qui applique une permutation circulaire àt[k:n+ 1].

3.4 Complexité Calcul de la position

— La boucle dansposition(t, n) ne comporte que des opérations élémentaires

— Donc le temps d’exécution de cette fonction est dominé par le nombre de tours de boucle effectués.

(10)

— Ce dernier est égal au nombre de comparaisons entre éléments de t, qui est compris entre1 etn.

— Il est aussi égal à n−k où kest la valeur retournée.

Décalage

— Le temps d’exécution de decalage(t, k, n) est dominé par le nombre de tours de boucle.

— Ce nombre vaut n−k.

Insertion

— Dansinsere(t, n), on affecte àkla valeur retournée par l’appel àposition(t, n).

— Le temps de calcul de position(t, n) est dominé par n−k.

— Celui dedecale(t, k, n) aussi.

— Donc le temps de calcul deinsere(t, n)est dominé par le nombre de comparaisons effectuées dansposition(t, n).

Complexité du tri

— Le temps d’exécution de tri_insertion(t) sur un tableau t de taille n est donc dominé par le nombre de comparaisons effectuées.

NotonsC(t) ce nombre de comparaisons.

Cas le pire

C(t)≤

n−1

X

k=1

= n(n−1) 2

Cette inégalité est une égalité pour tout tableautinitialement trié par ordre décroissant.

Conclusion

La complexité temporelle du tri par insertion dans le cas le pire sur un tableau de taillen estO(n).

Cas le meilleur

C(t)≥

n−1

X

k=1

=n−1

Cette inégalité est une égalité pour tout tableau tinitialement trié par ordre croissant.

Conclusion

La complexité temporelle du tri par insertion dans le cas le meilleur sur un tableau de taille nest O(n).

Remarque

Cela se produit lorsque le tableau est trié mais aussi lorqu’il est presque trié, à l’exception de kvaleurs, avec kborné indépendamment den.

(11)

Complexité spatiale

— Pas d’appels récursifs : espace de pile utilisé O(1).

— Aucune structure de données auxiliaire : espace mémoire utilisé (autre que la pile) : O(1)

Complexité spatiale du tri par insertion

La complexité spatiale du tri par insertion est constante.

3.5 Conclusion

Conclusion sur le tri par insertion

— Simple à écrire et à justifier.

— Sans doute le tri naïf le plus efficace : travaille en tempslinéairelorsque le tableau est trié ou presque trié (nombre fini d’éléments mal placés).

— Complexité temporelle quadratique et spatiale constante dans le cas le pire.

Tri recommandé pour trier quelques dizaines d’éléments.

4 Tri fusion

4.1 Algorithme Principe

Le principe est de partager le tableau en deux moitiés, de les trier récursivement, puis de fusionner ces deux moitiés triées de façon à obtenir un tableau trié.

QCM Stabilité

Est-ce un tri stable ? En place

Est-ce un tri en place ?

4.2 Implantation en Python Fusion de deux tableaux triés def fusion(t1, t2):

"""Retourne un tableau t trié de mêmes élts que t1+t2.

Précondition : t1 et t2 triés par ordre croissant."""

n1, n2 = len(t1), len(t2) n = n1 + n2; t = n * [None]

i1, i2 = 0, 0 for k in range(n):

# t[:k] trié, d'éléments ceux de t1[:i1] + t2[:i2]

# tous inf. ou égaux à ceux de t1[i1:] et t2[i2:]

if i2 >= n2 or (i1 < n1 and t1[i1] <= t2[i2]):

t[k] = t1[i1]; i1 += 1

(12)

else:

t[k] = t2[i2]; i2 += 1 return t

Fonction principale def tri_fusion(t):

"""Retourne une copie de t triée par ordre croissant."""

n = len(t) if n <= 1:

return t.copy() k = n // 2

return fusion(tri_fusion(t[:k]), tri_fusion(t[k:])) 4.3 Correction

Correction de la fusion

C’est une conséquence immédiate de l’invariant de boucle.

— Cet invariant est vrai à l’entrée dans la boucle

— Il est préservé par chaque tour de boucle.

Correction de la fonction principale

On montre, par récurrence forte, que pour tout entier n, elle trie tous les tableaux de taille n:

— C’est évident sur les tableaux de taille 0et1.

— Soitn≥2,tun tableau de taillen. Supposons qu’elle est correcte sur tous les tableaux strictement plus petits. Alors en posantk=bn/2c, on ak < netn−k=dn/2e< n.

Lorsqu’on exécutetri_fusion(t), les deux appels récursifs se font sur des tableaux strictement plus petits que t. Par hypothèse de récurrence, ils retournent donc des tableaux triés contenant les mêmes éléments quet[:k]ett[k:]. La fusion de ces deux tableaux est donc triée et contient les mêmes éléments quet[:k] +t[k:], donc que t.

4.4 Complexité Complexité

L’opération de fusion de deux tableaux de longueurs n1 etn2 demande un tempsO(n1+ n2)dans le cas le pire comme dans le cas le meilleur.

Notons P(n) (resp. M(n)) le temps d’exécution du tri fusion sur un tableau de taille n dans le cas le pire (resp. le meilleur).

Inégalité de complexité

P(n)≤ O

n→+∞(n) +P(bn/2c) +P(dn/2e)

(13)

Croissance de la complexité Rappel de l’inégalité :

P(n)≤ O

n→+∞(n) +P(bn/2c) +P(dn/2e)

Pour résoudre l’inéquation surP, le mieux est de voir ce qu’il se passe pourn= 2k, puis de dire qu’on aP(n)≤P(2dlog2ne)… sauf que rien ne dit que P est croissante !

Pour s’en sortir, deux possibilités :

— Faire l’hypothèse explicitementque P est croissante (c’est mieux que ne rien dire)

— Noter P0(n) le temps d’exécution du tri fusion sur un tableau de taille inférieure ou égaleà ndans le cas le pire. P0 est alors croissante (pourquoi ?)

Croissance de la complexité (bis) Si nous prenons la deuxième solution.

Pour un entier n fixé, P0(n) est le temps d’exécution d’un tableau tn de taille kn avec kn≤n.

Alors on a

P0(n)≤ O

n→+∞(kn) +P0(bkn/2c) +P0(dkn/2e) Inégalité de complexité

P0(n)≤ O

n→+∞(n) +P0(bn/2c) +P0(dn/2e) Autrement dit,P0 est croissante et vérifie la même inégalité queP. Résolution : cas simples

Pour résoudre

P0(n)≤ O

n→+∞(n) +P0(bn/2c) +P0(dn/2e)

On constate que le cas des puissances de2doit être plus simple, donc on poseuk=P0(2k).

Inégalité pour les puissances de 2 On en déduit, pourk au voisinage de+∞ :

uk≤ O

k→+∞(2k) + 2uk−1

Équation homogène

C’est une inégalité du type :

uk−2uk−1 ≤ O

k→+∞(2k) Équation homogène associée

∀k∈N uk = 2uk−1

dont une solution est 2k

k∈N.

(14)

Variation de la constante Inégalité :

uk−2uk−1 ≤ O

k→+∞(2k) Une solution de l’équation homogène associée : 2k

k∈N. Méthode de variation de la constante

On pose pour tout k∈N, λk =uk/2k (méthode de variation de la constante) et on divise l’inégalité précédente par2k. On obtient :

λk−λk−1 ≤ O

k→+∞(1) Variation de la constante (fin)

λk−λ0

k

X

i=1

i−λi−1)

≤ O

k→+∞(k)(k) (cours sur les séries)

etλk ≥0, doncλk=O(k). D’oùukk2k=O(k2k). Solution du cas général

P0(n)≤P0(2dlog2ne) =udlog

2ne=O(dlog2ne2dlog2ne) =O(nlogn) CommeP est à valeurs positives, on en déduit :

Complexité temporelle du tri fusion dans le cas le pire P(n) =O(nlogn)

Cas le meilleur

Dans le cas le meilleur, on peut de la même façon montrer que le temps d’exécution est M(n) = Ω(nlogn).

Remarque

M(n) = Ω(nlogn) etM(n)≤P(n)

etP(n) =O(nlogn)

D’où M(n) = Θ(nlogn) etP(n) = Θ(nlogn)

(15)

4.5 Conclusion

Conclusion sur le tri fusion Complexité temporelle

Dans le cas le meilleur comme dans le cas dans le pire, le tri fusion demande un temps d’exécution O(nlogn), où nest la longueur du tableau à trier.

Complexité spatiale (admise)

La complexité spatiale du tri fusion sur un tableau de taille n est O(n) : car on a besoin d’espace mémoire pour stocker les morceaux de tableau et pour effectuer la fusion.

NoteLe tri fusion est le tri à implanter si vous cherchez un algorithme plus efficace que les tris naïfs et que vous ne voulez pas y passer trop de temps.

Remarque 1 : Timsort

La fonctionsortedde Python utilise une version améliorée du tri fusion appeléeTimsort.

Remarque 2 : tri fusion itératif Celui-ci consiste à fusionner

— les éléments d’indices2ket2k+ 1, le tableau se trouve ainsi composé de sous-tableaux de taille2 commençant à l’indice2k, pour toutk, et tous triés.

— les tableaux d’indices 4k,4k+ 1 avec ceux d’indices 4k+ 2,4k+ 3, pour obtenir des sous-tableaux de taille4commençant à chaque indice de la forme 4k

— etc.

Facile à écrire si le tableau est de taille 2p, sinon il convient de faire attention pour les derniers éléments.

Remarque 3 : tri fusion externe

Le tri fusion est très utile pour un tableau tellement gros qu’il ne peut tenir en mémoire vive :

— on stocke le tableau dans un fichier ;

— on découpe le fichier en morceaux qui tiennent en RAM ;

— on trie chaque morceau (et on le stocke dans un fichier) ;

— on fusionne deux-à-deux les morceaux (on lit les premiers octets des fichiers à trier et on écrit dans un fichier résultat) pour obtenir des fichiers de taille double ;

— on fusionne deux à deux ces fichiers de taille double pour obtenir des fichiers de taille quadruple ;

— etc.

5 Tri par pivot

5.1 Algorithme Introduction

— Aussi appelé tri rapideou quicksort.

(16)

— Inventé par Tony Hoare en 19602.

— Toujours un des tris les plus rapides en pratique aujourd’hui…

— … à condition de l’optimiser de façon subtile.

— En pratique, il est préférable d’utiliser notre tri fusion plutôt que notre tri par pivot naïf.

Principe

Étant donné un tableautetgetddeux indices avec0≤g≤d<len(t),tri_rapide(t, g, d)va trier la portion du tableau d’indices appartenant àJg,dJ.

Sid−g≤1, il n’y a rien à faire. Sinon :

— On choisit une valeurvdu tableau appeléepivotet on réordonne la portion du tableau à trier de façon à ce que :

1. Le pivot arrive à une positioni∈Jg,dJ.

2. Toutes les valeurs strictement plus petite que v sont avant l’indice i, les autres après.

On dit qu’on apartitionnéla partie à trier.

— On trie récursivement les portions du tableau d’indices appartenant àJg, iJetJi+ 1,dJ.

Algorithme de partition

On parcourt le sous-tableau à partitionner de la gauche vers la droite de façon à avoir, à chaque itération :

— le pivot và l’indiceg;

— des valeurs strictement plus petites quevpour les indices de Jg+ 1,mJ;

— des valeurs supérieures ou égales à vpour les indices de Jm,iJ;

— des valeurs qui restent à répartir pour les indices de Ji,dJ.

(cf dessin et exercice du drapeau hollandais) QCM

Stabilité

Est-ce un tri stable ? En place

Est-ce un tri en place ?

5.2 Implantation en Python Implantation de la fonction partition def partition(t, g, d):

assert g < d v = t[g]

m = g+1

for i in range(g+1, d):

2. Hoare a aussi inventé les notions de pré-condition, post-condition et d’invariant de boucle.

(17)

# en g on a v, en range(g+1,m), des valeurs < v

# en range(m, i), des valeurs >= v if t[i] < v:

swap(t, i, m) m += 1

# replacer v si au moins une valeur est < v : swap(t, g, m-1) # ne fait rien si m == g+1.

# on retourne la position finale du pivot return m-1

Programme principal Le tri rapide s’écrit alors : def tri_rapide_rec(t, g, d):

if d - g <= 1: return i = partition(t, g, d) tri_rapide_rec(t, g, i) tri_rapide_rec(t, i+1, d) def tri_rapide(t):

tri_rapide_rec(t, 0, len(t)) 5.3 Correction

Correction de la fonction de partition

C’est une conséquence immédiate de l’invariant de boucle.

— Cet invariant est vrai à l’entrée dans la boucle

— Il est préservé par chaque tour de boucle.

Correction de tri_rapide_rec

— Évidente pour les sous-tableaux de taille 0ou 1.

— Soitn≥2, etgetddélimitant un sous-tableau de taillen. Supposonstri_rapide_rec sur tous les sous-tableaux strictement plus petits. Alors après l’exécution de i = partition(t, g, d), on a g≤i < det :

1. tous les éléments de t[g:i]sont inférieurs ou égaux à t[i]

2. tous ceux det[i+ 1 :d]sont supérieurs ou égaux.

De plus,i−g < d−g=netd−(i+ 1)< d−i≤d−g=n. Donc, par hypothèse de récurrence, les appels récursifs trient respectivement les sous-tableaux de t d’indices dans Jg, iJ et Ji+ 1, dJ. À l’issue de ces appels récursifs, les propriétés 1. et 2. sont encore vérifiées par ces sous-tableaux et comme ils sont de plus triés,t[g :d] est trié et possède les mêmes éléments qu’initialement.

5.4 Complexité

Domination par le nombre de comparaisons

(18)

— Comme pour les tris par insertion et par sélection, la complexité temporelle est domi- née par le nombre de comparaisons effectuées.

— L’appel àpartition(t, g, d)effectue toujoursd−g−1comparaisons et partitionne le sous-tableau à trier en deux sous-tableaux de taillep etq avec p+q=d−g−1.

Cas le pire

— On peut montrer que, pour un sous tableau de longueur n, le cas où un des sous- tableaux construit par la partition est vide et l’autre de longueur n−1 est un pire

— C’est notamment le cas si le tableau de départ ne contient que des éléments égaux.cas.

Remarquons que le nombre de comparaisons effectuées par tri_rapide_recpour un tableau trié de taillenne dépend pas du contenu du tableau mais juste denet notons C(n) ce nombre.

∀n∈N C(n) =n−1 +C(n−1) D’où∀n∈N C(n)−C(0) =

n

X

k=1

(k−1) =n(n−1)/2 =O(n2)

Cas le pire (bis)

Le cas où le tableau initial est trié par ordre décroissant constitue également un cas où la complexité est O(n2).

Exercice

Le démontrer (il faudra s’intéresser à deux appels récursifs successifs pour comprendre).

Cas le meilleur

— À l’inverse, le cas où le sous-tableau est coupé en deux moitiés de tailles à peu près égales est un cas le meilleur.

— En notant C0(n) le nombre de comparaisons effectuées dans le meilleur des cas pour un tableau de taillen, on peut montrer

∀n∈N C0(n) =n−1 +C0

n−1 2

+C0

n−1 2

— On peut en déduire C0(n) =O(nlogn) (commencer par s’intéresser aux tableaux de taille 2k−1).

Conclusion (provisoire)

— Dans le cas le pire : O(n2). Ce cas se produit notamment lorsque tous les éléments sont égaux ou lorsqu’ils sont triés par ordre croissant ou décroissant.

— Dans le cas le meilleur :O(nlogn)(en pratique souvent plus rapide que le tri fusion).

— En moyenne3 : O(nlogn) (en pratique souvent plus rapide que le tri fusion) ; écart- typeO(n).

3. Espérance du temps de calcul pour des donnéesdistinctes, mélangées aléatoirement de façon équipro- bable (résultat hors-programme).

(19)

Réparer les défauts

1. Mieux choisir le pivot. Deux méthodes classiques :

a) La médiane du premier élément, du dernier élément et de l’élément du milieu du tableau (idéal si le tableau est déjà trié, pas mauvais en général sinon).

b) En prendre un au hasard dans le tableau (théoriquement efficace mais très coû- teux en pratique).

2. Partitionnement trois voies : les éléments strictement plus petits que le pivot, les strictement plus grands et les égaux.

Complexité avec ces améliorations Avec le partitionnement trois voies :

— O(nlogn)en moyenne pour une permutation aléatoire de données, non nécessairement distinctes.

Avec le pivot aléatoire :

— O(nlogn) en moyenne pour des données fixées toutes distinctes.

Avec pivot aléatoire et partitionnement trois voies :

— O(nlogn) en moyenne pour des données fixées non nécessairement distinctes.

Dans tous les cas :

— O(nlogn) dans le cas le meilleur ;

— O(n2) dans le cas le pire.

Complexité en espace

— C’est la profondeur maximale des appels récursifs.

— O(logn) dans le cas le meilleur.

— O(n)dans le cas le pire. Peut être ramenée à O(logn) grâce à une idée due à R. Sed- gewick4.

5.5 Conclusion

Conclusion sur le tri par pivot

— Souvent préféré au tri par fusion pour son efficacité pratique.

— L’implanter correctement est très délicat (cas enn2).

— À éviter sur des données d’origine mal intentionnée.

Complexité temporelle du tri par pivot

— O(nlogn) dans le cas le meilleur et en moyenne.

— O(n2) dans le cas le pire.

Complexité spatiale du tri par pivot

— Dans le cas le meilleur : O(logn)

— Dans le cas le pire : O(n) pour la version naïve, O(logn) avec l’optimisation de Sed- gewick.

4. Pour cela, après avoir partitionné, on commence par trier (récursivement) la partition la plus petite avant de trier l’autre,sansappel récursif. Voir le livreInformatique pour tous en CPGEpour plus de détails.

Références

Documents relatifs

Dans le tri rapide, après les 2 tris partiels, tous les éléments de la partie gauche sont inférieurs au pivot, alors que tous les éléments de la partie droite... naturelle, et

On peut donc calculer un produit scalaire uniquement à partir des distances. T.Pautrel - cours: produits scalaires dans le plan - niveau

A l’équivalence les réactifs ont été introduits dans les proportions stœchiométriques, tous les ions hydroxyde présents ont réagi avec les ions oxonium versés.. Avantages :

Quelles sont les modifications à apporter au système de production de l’activité « Carton ondulé » de CARTBOX pour mettre en place le.. « Lean

De même, dans les seconde et troisième parties consacrées aux effets des chan- gements dans le système d’information de gestion, avec notamment l’automatisation

12) Comment peut-on diminuer ou supprimer l’alimentation d’un matériau ? - En chauffant les matières au-dessus d’une certaine température.. Le flux magnétique est l’ensemble

o écrire, en respectant les critères d’évaluation, un texte court expliquant l’expression « Voir loin, c’est voir dans le passé », texte qui sera à rendre sur feuille pour

Exit, voice and loyalty a ainsi pour objectif d’étudier les conditions de développement, conjoint ou non, des deux modes d’action, leur efficacité respective dans