CINI – Li115 1 Semaine 9 Algorithmes de tri ● Introduction ● Tri à bulle ● - principe ● - algorithme ● - efficacité ● Tri par sélection ● - principe, algorithme, efficacité ● Tri par insertion ● - principe, algorithme, efficacité
CINI – Li115 2 Pourquoi trier ? ● La recherche d'une donnée dans un ensemble trié est plus rapide ● Remplir un tableau en maintenant l'ordre des éléments n'est pas toujours possible ● → extraction de données à partir d'un fichier : l'ordre n'est pas toujours celui qu'on veut ● Le nombre de données à trier peut être très important ● → nécessité d'avoir un algorithme de tri efficace
CINI – Li115 3 Rappel : échanger le contenu des cases i et j dans un tableau ● Hypothèses : le tableau tab contient des éléments de type type. i et j sont des indices valides de tab. ● ( 0 ≤ i, j < taille de tab ). ● void echanger(type tab[], int i, int j) { ● type aux; ● aux = tab[i]; ● tab[i] = tab[j]; ● tab[j] = aux; ● }
CINI – Li115 4 Un premier algorithme : le tri « à bulle » ● Principe : le plus grand élément du tableau est une « bulle » que le programme doit faire remonter à la surface. ● La remontée est progressive : un élément remonte s'il est plus grand que celui qui est au- dessus. ● A la fin du premier parcours, le plus grand élément est « en haut ». ● L'algorithme relance un nouveau parcours, dans lequel la dernière case examinée lors du parcours précédent n'est plus prise en compte, jusqu'à ce qu'il n'y ait plus qu'une seule case à parcourir N - 1
CINI – Li115 5 Tri à bulle : première itération N if ( tab[i-1] > tab[i]) { echanger (tab, i-1, i); } i parcourt les indices de 1 à (N- 1)
CINI – Li115 6 Tri à bulle : deuxième itération 0 N i ne parcourt plus que les indices de 1 à (N- 2)
CINI – Li115 7 Tri à bulle : les dernières itérations 0 N N N ● Lorsqu'on a comparé (et éventuellement échangé) le contenu ● des deux dernières cases à traiter, le tableau est trié.
CINI – Li115 8 ● Répéter, en diminuant la taille du tableau d'une case à chaque fois ● → le programme utilise une variable ifin pour représenter le dernier indice de la partie du tableau à examiner. ifin prend des valeurs de (N-1) à 1 ● parcourir l'ensemble du tableau en comparant les cases consécutives ● → le programme utilise une variable i qui parcourt les indices de 1 à ifin. Pour chaque valeur de i, il compare les contenus de tab[i] et tab[i-1]. – si deux cases consécutives ne sont pas « dans l'ordre », échanger leur contenu ● → le programme appelle la fonction qui permet de permuter les contenus de deux cases dans un tableau. Tri à bulle : le programme
CINI – Li115 9 Tri à bulle : la fonction de tri if ( tab[i-1] > tab[i]) { echanger (tab, i-1, i); } for (i=1; i <= ifin; i++) { } for (ifin = taille-1; ifin > 0; ifin--) { } void tri_bulle(int tab[], int taille) { /* trie par ordre croissant le tableau tab contenant taille éléments */ int i, ifin; }
CINI – Li Tri à bulle : une amélioration ● La fonction tri_bulle parcourt (taille -1) fois le tableau, même si celui-ci est trié. ● Qu'est-ce qui caractérise un tableau trié ? ● tab[i-1] ≤ tab[i], pour tout i ∈ [1..N-1] ● → la condition associée au if est toujours fausse ● → pour détecter si le bloc d'instructions du if est exécuté, il faut ajouter au programme une variable dont la valeur est modifiée lors de l'exécution de ce bloc ● L'algorithme ne relance un parcours du tableau que si la totalité du tableau n'a pas été traitée et si le parcours précédent n'a pas détecté que le tableau était trié ● - le nombre de parcours n'est pas connu → boucle while
CINI – Li Le tri à bulle amélioré ● void tri_bulle(int tab[], int taille) { ● int i, ifin = taille-1; ● bool trie = false; ● while ((ifin > 0) && (! trie)) { ● trie = true; ● for (i=1; i <= ifin; i++) { ● if ( tab[i-1] > tab[i]) { ● echanger (tab, i-1, i); ● trie = false; ● } ● ifin--; ● }
CINI – Li Complexité ● L'efficacité d'un algorithme se mesure en nombre d'opérations effectuées. ● Pour un algorithme de tri : ● - opérations de comparaison ● - opérations d'affectation ● Le nombre d'opérations dépend du rangement initial. ● Lorsque le tableau est petit, le nombre d'opérations sera faible quel que soit l'algorithme utilisé et quel que soit le rangement initial. ● Il faut essayer d'évaluer le nombre d'opérations en fonction de la taille du tableau pour déterminer l'efficacité d'un algorithme lorsque le nombre de données à trier est important (plusieurs milliers).
CINI – Li Complexité du tri à bulle Combien de comparaisons ? ● (N-1) à la première itération ● (N-2) à la deuxième itération ● … ● Au total : N (N-1) / 2 comparaisons ● Combien d'affectations ? ● - un échange donne lieu à 3 affectations ● - aucun échange dans le cas d'un tableau trié ● - N (N-1) / 2 échanges si le tableau est rangé en ordre inverse ● - en moyenne, la moitié des comparaisons donnent lieu à un échange → N (N-1) /4 échanges, 3 fois plus d'affectations ● Peu efficace avec un grand nombre de données : ● N = → échanges en moyenne
CINI – Li Comment améliorer ? ● On peut placer le maximum dans la dernière case en un seul échange ● → on recherche la position du maximum dans le tableau ● → on échange le contenu de la case qui contient le maximum avec celui de la dernière case → Tri par sélection du maximum
CINI – Li Tri par sélection du maximum ● Principe : ● L'algorithme parcourt le tableau pour chercher l'indice de la case qui contient l'élément maximum. ● L'algorithme échange le contenu de cette case avec celui de la dernière case du tableau (si elle ne contient pas déjà le maximum). ● L'algorithme répète ces opérations en éliminant à chaque fois du parcours la dernière case examinée lors du parcours précédent. ● Il s'arrête lorsque la partie de tableau sur laquelle il travaille ne contient plus qu'une seule case.
CINI – Li N i_max = 0 max = 3 i_max = 2 max = echanger (tab, i_max, ifin); 4 7 i_max = 3 max = 7 Tri par sélection : première itération
CINI – Li Tri par sélection : l'exécution complète ● Initialement ● Échange tab[3] et tab[5] ● Échange tab[2] et tab[4] ● Pas d'échange ● Échange tab[0] et tab[2] ● Échange tab[0] et tab[1] ● ● numéro de case
CINI – Li Tri par sélection : le programme ● Répéter, en diminuant la taille du tableau d'une case à chaque fois – → le programme utilise une variable ifin pour représenter le dernier indice de la partie du tableau à examiner. ifin prend des valeurs de (N-1) à 1 ● parcourir le tableau pour rechercher la position du plus grand élément – → le programme suppose initialement que le max est dans la 1ère case. A chaque nouvelle case parcourue, il compare le contenu de la case avec la valeur du max. – Si le contenu est supérieur, le programme note l'indice de la case (position du nouveau max). ● si cet élément n'est pas dans la dernière case, échanger cet élément avec celui qui se trouve dans la dernière case
CINI – Li void tri_selection (int tab[], int taille) { int i, ifin, i_max; for (ifin = taille-1 ; ifin > 0 ; ifin--) { /* recherche de l’indice de la case contenant le max dans un tableau d'indices [0.. ifin] */ i_max = 0; for (i = 1 ; i <= ifin ; i++) { if (tab[i] > tab[i_max]) { i_max = i; } if (i_max != ifin) { /* le max n’est pas deja dans la derniere case */ echanger(tab, i_max, ifin); }
CINI – Li Complexité du tri par sélection Combien de comparaisons ? ● au cours d'une itération, toutes les cases sont comparées au maximum courant → (N-1) comparaisons à la 1ère itération → (N-2) comparaisons à la 2ème itération → … ● au total : N (N-1)/2 comparaisons Combien d'affectations ? ● Le placement définitif d'un élément s'effectue avec un seul échange → au plus (N-1) échanges, 3 fois plus d'affectations Pour N = éléments, comparaisons et échanges ● → Plus efficace que le tri à bulle ● (autant de comparaisons, moins d'échanges)
CINI – Li Peut-on diminuer le nombre de parcours ? ● Pourquoi placer un seul élément à chaque parcours ? ● Idée d'amélioration : au fur et à mesure du parcours, on trie les éléments parcourus par ordre croissant ● → on commence par placer le 2ème élément par rapport au 1er ● → on place le 3ème élément dans la liste formée par les deux premiers : la liste des 3 éléments doit être triée par ordre croissant ● → etc. ● On trie tout le tableau en le parcourant une seule fois
CINI – Li Tri par insertion ● Principe : ● Lorsqu'on insère un élément à sa place dans une liste triée, on obtient une liste triée. ● Lors du traitement de la case d'indice i : – - on a déjà rangé dans l'ordre le contenu des cases d'indice 0 à (i- 1) – - on insère le contenu de la case i dans cette liste triée → on décale le contenu des cases d'indice < i, dont la valeur est supérieure à tab[i] ● → on a besoin d'une variable auxiliaire pour stocker temporairement le contenu de la case i
CINI – Li Traitement de la case i du tableau 3 i aux 67 i jjj 1 1
CINI – Li Traitement de la case i du tableau ● On décale le contenu des cases d'indice entre (i -1) et 0 dont le contenu est supérieur à aux ● - on doit faire le parcours du tableau de (i-1) à 0 ● - on ne peut pas faire la comparaison avec tab[i] ● aux = tab[i] ; ● j = i-1 ; ● while ((j >= 0) && (tab[j] > aux)) { ● tab[j+1] = tab[j] ; ● j--; ● } ● tab[j+1] = aux ; !
CINI – Li Tri du tableau ● On répète le traitement de la case i, pour i variant ● de 1 (insertion dans la liste constituée de tab[0]) à (taille – 1) ● void tri_insertion (int tab[], int taille) { ● int i, j, aux; ● ● for (i = 1; i < taille; i++) { ● aux = tab[i] ; ● j = i-1; ● while ((j >= 0) && (tab[j] > aux)) { ● tab[j+1] = tab[j] ; ● j--; ● } ● tab[j+1] = aux ; ● }
CINI – Li Tri par insertion : l'exécution complète ● Initialement ● Après insertion de tab[1] ● Après insertion de tab[2] ● Après insertion de tab[3] ● Après insertion de tab[4] ● Après insertion de tab[5] ● ● numéro de case
CINI – Li Complexité du tri par insertion ● Combien de comparaisons ? ● Ici, le nombre de comparaisons dépend du rangement initial ● (N-1) comparaisons au total si le tableau est déjà trié ● N (N-1)/2 comparaisons si le tableau est rangé en ordre inverse ● Combien d'affectations ? ● 2 (N-1) affectations si le tableau est déjà trié (copie dans et depuis aux) ● (N-1) (N+4)/2 affectations si le tableau est rangé en ordre inverse (i+2 affectations à l'itération i) ● →L'efficacité dépend fortement de l'ordre initial des éléments En moyenne, bien qu'on ne fasse qu'un seul parcours du tableau, le tri par insertion est seulement un peu meilleur que le tri par sélection
CINI – Li En pratique N t( s )
CINI – Li Quelques remarques ● La complexité d'un algorithme est importante quand on manipule de grands ensembles de données ● Wikipedia : ● « le tri par sélection est rapide quand on a moins de 7 éléments » ● « le tri par insertion est le plus rapide et le plus utilisé pour des listes de moins de 15 éléments » ● Pour des quantités de données aussi faibles, n'importe quel algorithme fait l'affaire ● Il existe des algorithmes de tri plus compliqués mais plus efficaces que ceux présentés ici ● - tri rapide (quick sort) ● - tri fusion ● -...