Algorithmes de tri

publicité
PROBLÈME GÉNÉRAL DU TRI
On dispose d’un tableau unidimensionnel ou d’une liste d’éléments d’un ensemble
muni d’une relation d’ordre totale (le plus souvent des nombres avec la relation ≤
mais pas seulement: penser au tri alphabétique)
On cherche à les ordonner (disons par ordre croissant pour fixer les idées)
On peut accepter de créer des listes intermédiaires pour réaliser le tri ou bien
imposer de ne travailler que sur la liste initiale (par des permutations)
Le paramètre de complexité sera la longueur n de la liste. Les opérations à effectuer
seront des comparaisons et des affectations.
On cherche bien sûr à optimiser la complexité en fonction de n quand n est très
grand, mais on peut aussi être amené à rechercher à optimiser la complexité
pour certains types de données particuliers (ex: liste déjà « bien prétriées »)
TRI PAR SÉLECTION: PRINCIPE
 On commence par mettre le plus petit élément à gauche suivant
le même principe que dans l’algorithme de recherche du
minimum (une variable pivot initialisée à 𝑙[0] est comparée aux
valeurs successives de la liste, on échange la valeur rencontrée
avec le pivot lorsqu’elle est plus petite)
 Ensuite on recommence en ignorant le premier élément, etc…
 A l’étape numéro 𝑖, les 𝑖 premiers éléments de la liste sont
correctement ordonnés (il s’agit de l’invariant de boucle qui garantit la
validité de l’algorithme)
TRI PAR SÉLECTION: PSEUDO-CODE
Donnée: une liste l de longueur 𝒏 ≥ 𝟐 (numérotée de 0 à 𝑛 − 1)
Pour 𝒊 = 𝟎 à 𝒏 − 𝟐:
𝑝𝑖𝑣𝑜𝑡: = 𝑙[𝑖]
Pour 𝑗 = 𝑖 + 1 à 𝑛 − 1:
Si 𝑙[𝑗] < 𝑝𝑖𝑣𝑜𝑡:
𝑝𝑖𝑣𝑜𝑡 = 𝑙[𝑗]
échanger 𝑙[𝑗] et 𝑙 𝑖
Renvoyer l
TRI PAR SÉLECTION: COMPLEXITÉ
A l’étape 𝑖 on fait au plus 𝑛 − 𝑖 comparaison et 𝑛 − 𝑖 échanges (chacun comportant
2 affectations de valeurs de la liste)
Il y a donc au maximum 𝑛 − 1 + ⋯ + 1 =
d’affectations de valeurs dans la liste.
𝑛(𝑛−1)
2
comparaisons et le double
 Complexité en 𝑂(𝑛2 )
TRI PAR INSERTION : PRINCIPE
Il s’agit comme dans le tri sélection de procéder en n-1 étape de telle sorte
qu’au terme de l’étape i les i premières valeurs soient bien ordonnées.
La différence réside dans la manière dont on ordonne: à l’étape i, l’élément
𝑥 = 𝑙[𝑖] est comparé à ses prédecesseurs dans la liste en partant du
plus grand ; tant que 𝑥 < 𝑙[𝑗] on continue à parcourir les prédecesseurs
de 𝑙[𝑖] par ordre décroissant.
On s’arrête dès qu’on rencontre un j pour lequel 𝑙[𝑗 − 1] ≤ 𝑥. Dans ce cas
on « insère » 𝑥 entre 𝑙[𝑗 − 1] et 𝑙[𝑗]
TRI PAR INSERTION: PSEUDO-CODE
Donnée: une liste l de longueur 𝒏 ≥ 𝟐 (numérotée de 0 à 𝑛 − 1)
Pour 𝒊 = 𝟎 à 𝒏 − 𝟐:
𝑥: = 𝑙[𝑖 + 𝟏]
𝑗=𝑖
Tant que (𝑗 ≥ 𝟏 et 𝒍[𝒋] > 𝒙 )
𝑗 =𝑗−1
déplacer la valeur 𝑥 entre 𝑙[𝑗 − 1] et 𝑙[𝑗]
∗
Renvoyer l
∗
: tous les termes de la liste à partir de l[j] sont alors décalés vers la droite
TRI PAR INSERTION: COMPLEXITÉ
A l’étape i on fait au plus 𝑖 comparaison et au plus 𝑖 affectation (on intercale un
élément dans la liste et on décale ses successeurs de 1)
Il y a donc au maximum 1 + ⋯ + (𝑛 − 1) =
valeurs dans la liste.
𝑛(𝑛−1)
2
comparaisons et 𝑛 affectations de
 Complexité en 𝑂(𝑛2 )
TRI PAR FUSION : PRINCIPE

Il s’agit d’un algorithme récursif basé sur le principe « diviser pour
régner »

Il repose sur la remarque suivante: si 𝑙1 et 𝑙2 sont deux listes de taille 2
bien triées, alors on peut fusionner 𝑙1 et 𝑙2 en une liste l de taille 𝑛 bien
triée en un temps linéaire (on commence par prendre le minimum de
𝑙1[0] et 𝑙2[0] et on affecte sa valeur à 𝑙[0] puis on l’enlève de sa liste
d’origine ; on recommence alors avec les listes restantes etc…)
𝑛
TRI PAR FUSION : PRINCIPE
On en déduit l’algorithme de tri par fusion:
 On partitionne la liste de taille 𝑛 à trier en deux sous-listes à peu
près de longueur 𝑛/2
 On trie chacune des deux sous-listes en appelant la fonction de
tri (récursivité)
 On fusionne les deux sous-listes ainsi triées
TRI PAR FUSION : PSEUDO-CODE
Donnée: une liste l de longueur 𝒏 ≥ 𝟐 (numérotée de 0 à 𝑛 − 1)
On suppose disposer d’une fonction fusion qui fabrique une liste bien ordonnée à
partir de deux sous-listes
Si 𝑛 = 1:
Renvoyer 𝑙
Sinon:
créer 𝑙1 sous- liste comprenant les 𝑝 = [𝑛/2] premiers termes de 𝑙1 et 𝑙2 sous-liste
de taille 𝑛 − 𝑝 comprenant les derniers termes de 𝑙
𝑙𝑏𝑖𝑠1, 𝑙𝑏𝑖𝑠2 = 𝑡𝑟𝑖(𝑙1), 𝑡𝑟𝑖(𝑙2)
Renvoyer 𝑓𝑢𝑠𝑖𝑜𝑛(𝑙1𝑏𝑖𝑠, 𝑙2𝑏𝑖𝑠)
TRI PAR FUSION:COMPLEXITÉ (pire des cas)

Le nombre de comparaisons maximum effectuées pour fusionner deux listes
triées (tailles 𝑝 et 𝑞) est min(𝑝, 𝑞) et le nombre max d’affectations dans la
nouvelle liste est 𝑝 + 𝑞. Par suite le nombre d’opérations élémentaires est en 𝐴 ×
𝑚𝑎𝑥 𝑝, 𝑞 avec (𝐴=constante)

Quand on travaille à une instance de la récursivité avec une liste de taille 𝑘 on
effectue un appel à fusion qui s’appliquera aux deux « demi-listes » dont la taille
est au plus 𝑘 2 + 1 et même 𝑘 2 lorsque k est pair

En tenant compte de l’appel à fusion cela donne une majoration de la complexité
𝐶𝑛 du tri d’une liste de taille 𝑛:
𝑪𝒏 ≤ 𝟐 ∗ 𝑪 𝒏/𝟐 +𝟏 + 𝐀 ∗ (
𝒏
𝟐
+ 𝟏)
(on peut enlever les +1 lorsque n est pair)
 Complexité en 𝑂(𝑛 log 𝑛)
TRI RAPIDE (QUICKSORT) : PRINCIPE

Il s’agit d’un algorithme récursif

On choisit un élément dans la liste (le pivot): on peut choisir par exemple le
dernier terme mais aussi un élément aléatoire (dans le cas d’un élément
aléatoire on commence par effectuer une permutation pour placer le pivot en fin
de liste)

On effectue des comparaisons et permutations jusqu’à ce que tous les éléments
inférieurs au pivot soient en début de tableau et les autres à la fin (on termine en
insérant le pivot à sa place)

Le pivot partitionne ainsi le tableau en deux éléments de longueur plus petite que
l’on trie par appel récursif à la fonction.
TRI RAPIDE : LE PARTITIONNEMENT
Revenons sur le cœur de l’algorithme çàd le partitionnement des valeurs de la liste
en fonction de leur place vis-à-vis du pivot:
Après avoir placé le pivot en fin de liste, on parcourt les éléments de la liste de
gauche à droite et on les compare au pivot:
Si l[i] ≤ pivot: on ne fait rien
Sinon: on parcourt les successeurs de l[i] (tant que c’est posssible) jusqu’à
rencontrer un terme ≤ pivot; lorsque c’est le cas on permute ce terme avec l[i]
On passe au terme d’indice i + 1 (attention: notons que ce terme n’a pas nécessairement la même
valeur qu’au début de l’étape précédente)
TRI RAPIDE: TERMINAISON ET VALIDITÉ
 La terminaison repose sur le fait qu’au terme de l’étape de
partitionnement les deux sous-listes délimitées par le pivot sont
de taille strictement inférieures à celles de la liste dont on est
parti.
 La validité vient du fait que le pivot est « à sa place » au terme de
chaque partitionnement (c’est l’invariant de boucle)
 La validité globale de l’algorithme s’en déduit bien puisqu’au
bout de k étapes il y a au moins k éléments bien placés
TRI RAPIDE: COMPLEXITÉ

Si on fixe le choix du pivot (par exemple dernier terme de la liste à chaque étape):
 la complexité dans le pire des cas est en 𝑂(𝑛2 ) (comparer avec l’algorithme par fusion)
 La complexité en moyenne est en 𝑂(𝑛𝑙𝑜𝑔𝑛)
 Si on choisit le pivot aléatoirement à chaque étape (loi uniforme):
 Complexité moyenne (en moyennant sur le choix du pivot) pour tout jeu
de données en 𝑶(𝒏 𝐥𝐨𝐠 𝒏)
 Complexité dans le pire des cas (avec pire choix de pivot à chaque étape)
en 𝑶(𝒏𝟐 )
Téléchargement