Algorithmes de tri et complexité

publicité
Algorithmes de tri et complexité
Plan
1.
RECHERCHE D'UN ELEMENT DANS UN TABLEAU ..................................................................................2
1.1. RECHERCHE SEQUENTIELLE D'UN ELEMENT .......................................................................................................2
1.2. RECHERCHE DICHOTOMIQUE DANS UN TABLEAU TRIE .........................................................................................2
1.3. COMPLEXITE D'UN ALGORITHME........................................................................................................................3
2.
TRI PAR SELECTION DIRECTE ....................................................................................................................3
3.
TRI PAR INSERTION ......................................................................................................................................5
4.
TRI PAR PERMUTATION (TRI "BULLE") ....................................................................................................6
5.
TRI PAR DEPLACEMENTS DECROISANTS (TRI DE SHELL) ....................................................................7
6.
TRI PAR SEGMENTATION (QUICKSORT DE HOARE) ...............................................................................7
7.
TRI PAR COMPTAGE.................................................................................................................................. 11
1.
RECHERCHE D'UN ELEMENT DANS UN TABLEAU
La nécessité de trier des données résulte du besoin de les retrouver rapidement. Par
exemple, pour chercher l'adresse et le numéro de téléphone d'une personne, on parcours un
fichier jusqu'à trouver l'enregistrement qui le concerne.
On supposera par la suite que tous les éléments du fichier sont rangés dans un tableau en
mémoire centrale (ce qui n'est pas toujours possible).
1.1. Recherche séquentielle d'un élément
Pour rechercher séquentiellement un élément dans un tableau, on passe en revue tous les
éléments de ce tableau, un à un, jusqu'à trouver la position de l'élément recherché (appelé
encore indice). Un recherche séquentielle est plus ou moins rapide selon que l'élément
recherché est situé au début ou en fin du tableau. En moyenne, on passe en revue la moitié
des éléments. Si N est le nombre d'élément du tableau, le nombre moyen de comparaisons
sera N/2.
Exercice: écrire l'algorithme de recherche
1.2. Recherche dichotomique dans un tableau trié
Lorsque le tableau est trié numériquement ou alphabétiquement, la méthode de recherche
dichotomique permet de diviser le temps de recherche par 2 à chaque étape. L'algorithme est
le suivant:
1)
Initialiser la section de recherche à l'ensemble du fichier.
2)
Comparer le nom cherché au nom de l'élément situé au milieu de la section de
recherche
3)
-
si le nom cherché est plus petit que le nom de l'élément situé au milieu, on
recommence la recherche dans la première moitié
-
si le nom cherché est plus grand que le nom de l'élément situé au milieu, la
recherche se poursuit dans la seconde moitié.
-
sinon on a trouvé l'élément recherché
Recommencer en (2) tant que l'élément cherché n'est pas trouvé ou que la section de
recherche est constitué d'au moins un élément.
Lorsqu'il y a par exemples 1024 éléments, on retrouve l'élément recherché après au plus 10
comparaisons (210 = 1024) alors qu'il en faut en moyenne 512 dans le cas d'une recherche
séquentielle. Comme on le voit, l'algorithmique a une très grande importance dans la
programmation. Notons dès à présent que les algorithmes utilisés dépendent beaucoup de
l'organisation et des structures de données adoptées.
1.3. Complexité d'un algorithme
Pour évaluer l'efficacité d'un algorithme, on introduit la notion du complexité comme étant
l'ordre de grandeur des opérations à effectuer pour N données.
Dans le cas de la recherche séquentielle dans un tableau de N éléments non triés, on a vu
que le nombre moyen d'opérations est N/2. L'ordre de grandeur est donc en O(N) ce qui
signifie que le temps de traitement est proportionnel au nombre d'élément. Dans le cas de la
recherche dichotomique dans un tableau trié, l'ordre de grandeur est en O(Log2 N).
La complexité des algorithmes permet de les comparer entre eux. Un algorithme de
complexité O(N) est réputé meilleur qu'un algorithme en O(N2). Pour de grandes valeurs de
N, cela parait évident et c'est souvent le cas. Pour des petites valeurs de N, ce n'est pas
toujours vrai.
2.
Tri par SELECTION DIRECTE
On recherche l'élément le plus petit et on le range dans le second tableau. Cette méthode
utilise 2 tableaux. Le tableau trié résultant n'est pas le tableau de départ qui conserve l'ordre
initial des éléments à trier. Avec cette méthode, il faut se rappeler les éléments déjà pris.
BETA
ALPHA
OMEGA
ALPHA
GAMMA
BETA
ALPHA
OMEGA
BETA
-----------GAMMA
Si N est le nombre d'éléments du tableau, cette méthode effectue N recherches du plus petit
élément suivant dans le tableau à trier. Sachant que la recherche séquentielle d'un élément
demande en moyenne N/2 comparaisons, cette méthode demandera N2/2 comparaisons.
La complexité de cet algorithme est donc en O(N2) ce qui signifie que son temps de
traitement est proportionnel au carré du nombre d'éléments à trier.
Une amélioration consiste à n'utiliser qu'un seul tableau et procéder par permutation. Le
début du tableau est considéré trié. On recherche le plus petit élément dans la section non
triée du tableau. Cet élément est permuté avec le premier élément de la section non triée. La
partie triée du tableau s'enrichit alors d'un élément.
BETA
OMEGA
ALPHA
GAMMA
ALPHA
OMEGA
BETA
GAMMA
ALPHA
BETA
OMEGA
GAMMA
ALPHA
BETA
GAMMA
OMEGA
Exercice: écrire l'algorithme de tri par sélection directe avec un seul tableau.
3.
Tri par INSERTION
On n'utilise ici qu'un seul tableau. On considère à un instant donné que les P premiers
éléments du tableau sont déjà triés. On recherche alors la place de l'élément numéro (P+1)
parmi les P éléments déjà triés. L'élément est inséré à la place ainsi trouvée, ce qui a pour
conséquence le déplacement des éléments plus grands que lui.
+--------+
+--------+
+--------+
+--------+
¦ BETA
¦ BETA _ ¦ <-+
¦ ALPHA
¦ ALPHA
+--------¦
Ã--------Â
¦
Ã--------Â
Ã--------Â
¦ OMEGA
¦ OMEGA_ ¦
¦
¦ BETA
¦ BETA
+--------¦
+--------¦
¦
Ã--------Â
Ã--------Â
¦ ALPHA
¦ ALPHA
¦ OMEGA_ ¦<--+
¦ GAMMA
Ã--------Â
¦
¦ ?
¦
¦ ?-+
¦
¦
+--------¦
¦--------¦
+--------¦
¦ GAMMA
¦ GAMMA
¦ GAMMA
¦
+--------+
¦
+--------+
¦
¦ ?-+
+--------+
¦ OMEGA
¦
¦
¦
¦
+--------+
Notons que le déplacement des éléments, de la partie déjà triée, plus grands que l'élément à
insérer peut s'effectuer au fur et à mesure des comparaisons. Si TAB[ i ] désigne l'élément
numéro i du tableau, l'algorithme d'insertion s'écrit alors :
EL_A_INSERER <- TAB[ P+1 ]
INDICE <- P
Tant que (TAB[ INDICE ] > EL_A_INSERER) ET ( INDICE >= 1) faire
TAB[ INDICE+1 ] <- TAB[ INDICE ]
INDICE <- INDICE-1
Fin (Tant que)
TAB[ INDICE+1 ] <- EL_A_INSERER
Exercice: écrire l'algorithme complet du tri par insertion.
4.
Tri par PERMUTATION (Tri "Bulle")
Dans le tri par permutation, appelé encore tri par inversion, l'idée est de passer en revue le
minimum de fois possible les éléments du tableau. Ainsi à chaque parcours, on va comparer
les paires d'éléments juxtaposés et les permuter si besoin de telle manière à rapprocher les
éléments les plus petits vers le début du tableau et les éléments les plus grands vers la fin.
+--------+
+--------+
+--------+
+--------+
¦ BETA
¦ BETA
¦ BETA
¦ BETA
¦
¦
¦
¦
+--------¦
+--------¦
+--------¦
+--------¦
¦ OMEGA
¦ OMEGA
¦ ALPHA
¦ ALPHA
¦
¦<--+
+--------¦
+--------¦
¦ ALPHA
¦ ALPHA
¦
¦
¦<--+
¦
+--------¦
+--------¦
¦ OMEGA
¦ GAMMA
¦<--+
+--------¦
¦--------¦
+--------¦
¦ GAMMA
¦ GAMMA
¦ GAMMA
¦
+--------+
¦
+--------+
¦
¦
¦
+--------¦
¦<--+
¦ OMEGA
+--------+
¦
+--------+
Ici un deuxième passage en revue est nécessaire pour achever le tri:
+--------+
+--------+
¦ BETA
¦ ALPHA
¦<--+
+--------¦
¦ ALPHA
¦
¦<--+
¦
Le nombre de passages K nécessaire
+--------¦
pour obtenir un tableau trié étant
¦ BETA
compris entre 1 et N
¦
+--------¦
+--------¦
de comparaisons
¦ GAMMA
¦ GAMMA
étant
¦
¦
de l'ordre
à
et le nombre
chaque passage
de N,
on
peut
+--------¦
¦--------¦
simplement dire
¦ OMEGA
¦ OMEGA
du tri par permutation est compris
¦
+--------+
¦
+--------+
que la complexité
O(N) et O(N2).
Une amélioration consiste à ne pas déposer systématiquement le deuxième élément d'une
paire juxtaposée. En effet, il est possible que cet élément, qui appartient à la paire suivante,
doit être une nouvelle fois permuté. L'élément qui n'est pas déposé crée ainsi un trou qui se
déplace dans le tableau d'où le nom de tri "Bulle" donné à cet algorithme de tri.
Exercice: écrire l'algorithme par permutation 1) sans tenir compte de cette amélioration, 2) en
tenant compte de cet amélioration.
5.
Tri par DEPLACEMENTS DECROISANTS (tri de SHELL)
Dans le tri précédent, les éléments parcourent au pas à pas le chemin qui les mène à leur
place définitive. L'idée de l'algorithme que proposa SHELL en 1959 est de déplacer les
éléments plus rapidement dans le tableau. On ne compare plus une paire constituée de 2
éléments juxtaposées mais 2 éléments éloignés d'une certaine distance D.
Les éléments TAB[ I ] et TAB[ I+D ] sont comparés et éventuellement permutés. L'algorithme
du tri précédent est ainsi appliqué à D sous suites d'éléments du tableau à trier. La distance
D, grande au début, est diminuée après chaque série de tris Bulle jusqu'à parvenir à la valeur
unité. On s'arrange pour choisir des distances successives qui ne font pas intervenir des
suites d'éléments déjà triés. La nouvelle valeur de D ne doit pas être en particulier un diviseur
de l'ancienne valeur. On choisit en général des valeurs premières entre elles, exemple: 63,
31, 15, 7, 3 puis 1.
Exercice: écrire l'algorithme de tri de SHELL.
6.
Tri par SEGMENTATION (Quicksort de Hoare)
Une valeur "pivot" est choisie parmi les valeurs à trier. Diverses manières de choisir la valeur
du pivot existent. La suite d'éléments est réorganisée de telle manière à ce que les valeurs
inférieures au pivot se trouve avant celui-ci et les valeurs supérieures après. Il reste ensuite à
trier les 2 parties obtenues. Cet algorithme, récursif et de complexité O(N.Log2N), peut
s'exprimer ainsi:
Pour TRIER une section d'éléments d'indices compris entre KD et KF, faire :
Choisir à priori un élément PIVOT, on prend le premier élément du tableau par
exemple
REPARTIR en fonction de ce pivot. La position du pivot change: il n'y a pas forcément
autant d'éléments plus petits que d'éléments plus grands que le pivot. A la fin de la répartition
la position du pivot est KP
-
TRIER la section d'indices compris entre 1 et KP
-
TRIER la section d'indices compris entre KP+1 et KF
Pour REPARTIR une section par rapport à un pivot
Au début la section des plus petits et la section des plus grands sont vides.
Tant que des éléments n'appartiennent pas à une section des plus petits ou des plus grands,
Faire :
Parcourir la section des plus petits du premier vers le milieu et repèrer le premier
élément plus grand ou égal que le pivot
Parcourir la section des plus grands du dernier vers le milieu et repèrer le premier
élément plus petit ou égal que le pivot
-
Permuter les 2 éléments précédemment repérés
Exemple: Dans la suite d'éléments suivants, le premier élément est choisi pour pivot. Le
premier élément plus grand que le pivot a pour valeur 20. Le premier élément, dans le
parcours inverse, plus petit que le pivot a pour valeur 08. Ces 2 sont permutés.
Pivot
+---------------------------------------+
¦ 09 ¦ 20 ¦ 14 ¦ 10 ¦ 06 ¦ 08 ¦ 60 ¦ 11 ¦
+---------------------------------------+
_
_
KD
KF
La section des plus petits possède à ce stade 2 éléments (09 et 20). La section des plus
grands en comprend 3 (08, 60 et 11). Ils restent des éléments à répartir, on continue les
parcours: l'indice KD augmente (parcours vers la droite) et l'indice KF diminue (parcours vers
la gauche). Les éléments de valeurs 14 et 06 sont alors permutés.
+---------------------------------------+
¦ 09 ¦ 08 ¦ 06 ¦ 10 ¦ 14 ¦ 20 ¦ 60 ¦ 11 ¦
+---------------------------------------+
_
_
KF
KD
Il n'y a plus d'élément à répartir, on applique de manière récursive l'algorithme sur les
sections 1..KF et KD..N.
Remarque: Le fait de choisir pour pivot le premier élément de la section est pénalisant
lorsque les éléments sont déjà prétriés. Le pivot peut être choisi de manière aléatoire parmi
les éléments de la section à répartir. L'idéal serait de choisir le pivot de telle manière que les
sous-sections créées soient de même longueur.
Programme PASCAL
Program TRI_RAPIDE;
(*$M 16384, 0, 65536 *)
Const
NbMAX=999;
Var
TAB :ARRAY[ 1..NbMAX ] Of Integer;
NbT, NN, TE :Integer;
Procedure TRIER(KDeb, KFin :Integer);
Var KK, KD, KF, PVT, ELT :Integer;
Begin
KD:=KDeb; KF:=KFin; PVT:=TAB[ KDeb ];
Repeat
While TAB[ KD ] < PVT Do KD:=KD+1;
While TAB[ KF ] > PVT Do KF:=KF-1;
If KD <= KF Then Begin
ELT:=TAB[ KD ]; TAB[ KD ]:=TAB[ KF ]; TAB[ KF ]:=ELT;
KD:=KD+1; KF:=KF-1;
End;
Until KD > KF;
If KDeb < KF Then TRIER(KDeb, KF);
If KFin > KD Then TRIER(KD, KFIN);
End; (* TRIER *)
Begin (* LECTURE DES ELEMENTS A TRIER *)
NN:=0;
Write(' Nombre d''éléments ( < ',NbMAX,' ): '); ReadLN(NbT);
WHile (NN < NbT) Do Begin
NN:=NN+1;
Write(' Entrer élément n°', NN, ' : '); ReadLN( TAB[ NN ]);
End;
If NbT > 1 Then TRIER(1, NbT);
(* TRI *)
For NN:=1 To NbT Do WriteLN(NN:3, TAB[ NN ]:4); (* RESULTAT *)
End.
7.
Tri par COMPTAGE
Elements à trier non égaux (?clés unique) ... à vérifier
On se sert d'un tableau auxiliaire de compteurs TCPT. Pour chaque éléments du tableau à
trier on compte le nombre d'éléments qui lui sont inférieurs.
La première étape est la création du tableau des compteurs? On proicède comme cidessous:
Pour J de 1 à N Faire
Pour I de j+1 à N Faire
si TABT[ J ] < TABT[ I ] alors incrémenter TCPT[ I ]
sinon incrémenter TCPT[ J ]
Dans la 2ième étape, on utilise le tableau des compteurs pour mettre chaque élément à sa
position correcte:
Pour NC de 0 à N-1 Faire
Recherche l'élément de compte NC:
J=NC+1
Tant que TCPT[ J ] <> NC incrémenter J
Permuter TABT[ NC+1 ] et TABT[ J ]
Permuter TCPT[ NC+1 ] et TCPT[ J ]
Téléchargement