TD6. Algorithmes de tri
Le sujet de ce TD est disponible au format pdf ici.
Les objectifs de ce TD sont les suivants :
- (re) voir deux algorithmes classiques de tri ;
- s'interroger sur le coût de ces algorithmes en fonction des entrées ;
Exercice 1 : tri par insertion
On se propose d'implémenter un tri par insertion. Cet algorithme fonctionne en triant le tableau à partir de son début. Au départ rien n'est trié et à la fin tout est trié. Normal, c'est l'objectif et si ce n'était pas le cas, l'algorithme serait incorrect.
Quel est l'état intermédiaire du système ? En milieu de parcours tout le début du tableau est composé d'éléments ordonnés comme le montre la figure ci dessous. On parle d'invariant de boucle, c'est à dire d'une propriété vraie au début de chaque itération de la boucle.
On prend le premier élément non ordonné et on l'insère à la bonne place dans la partie ordonnée du tableau. Cette insertion augmente la taille de la partie ordonnée de 1 et il suffit donc de la répéter pour obtenir un tableau complètement trié.
Question 1
Programmer un tri par insertion.
Correction question 1
Cliquez ici pour révéler la correction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
Question 2
Quelle est à la complexité en temps de l'algorithme ?
Correction question 2
Cliquez ici pour révéler la correction.
Cet algorithme de tri nous permet d'introduire les notions de meilleur et pire cas en ce qui concerne la complexité en temps d'un programme.
Quelque soit la tableau en entrée de l'algorithme, la boucle externe itère len(tableau) - 1
fois.
Dans le meilleur cas, le tableau est déjà trié et on ne rentre jamais dans la boucle interne.
Le coût est donc dans ce cas O(n - 1)
que l'on simplifie en O(n)
(intuitivement, quand on parle de complexité c'est l'ordre de grandeur qui nous intéresse, et donc on se moque du - 1
. Nous verrons au second semestre dans le cours d'algo que la definition formelle de O
nous autorise cette simplification) avec n
représentant la taille du tableau en entrée.
Dans le pire cas, le tableau est trié mais à l'envers.
La boucle interne est exécutée 1 + 2 + 3 + ... + len(tableau) - 1
fois.
Le coût est donc O(n * (n+1) / 2) = O(n^2)
, toujours avec n
représentant la taille du tableau en entrée.
Exercice 2 : tri par sélection
Un second algorithme simple de tri est le tri par sélection. Comme dans le tri par insertion, le tableau est toujours divisé entre une partie triée et une partie non triée.
Comme l'illustre la figure ci-dessous, au lieu de prendre le premier élément non trié et de l'insérer au bon endroit on sélectionne parmi les éléments non triés celui qui prendra sa place définitive à la fin de la partie triée. Les éléments de la partie triée ne sont donc jamais déplacés.
Question 1
Programmer un tri par sélection.
Correction question 1
Cliquez ici pour révéler la correction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
Question 2
Quelle est à la complexité en temps de l'algorithme ?
Correction question 2
Cliquez ici pour révéler la correction.
Quelque soit le tableau en entrée la boucle interne est exécutée n-1 + n-2 + ... + 1
fois avec n
représentant la taille du tableau en entrée.
Le coût est donc O(n * (n+1) / 2) = O(n^2)
.
Exercice 3 : fusion de tableaux triés
Question 1
Écrire un algorithme qui fusionne deux tableaux triés en un seul également trié.
Correction question 1
Cliquez ici pour révéler la correction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
Question 2
Quelle est la complexité en temps de l'algorithme ?
Correction question 2
Cliquez ici pour révéler la correction.
La complexité est O(n1 + n2)
avec n1
et n2
la longueur de tab1
et tab2
.
En effet, quelque soit l'entrée de l'algorithme, les deux tableaux doivent être entièrement recopiés dans le tableau fusionné.
Exercice 4 : un peu de stabilité (pour aller plus loin)
Un algorithme de tri est stable si deux éléments ayant la même valeur dans le tableau se retrouvent dans le même ordre dans le tableau après le tri. Il faut donc avoir une notion d'ordre en plus de celle définie pour trier le tableau pour parler de stabilité. En général dans le contexte des tableaux, la position initiale des éléments, c'est-à-dire les indices du tableau auxquels ils se trouvent, est utilisée pour définir cet ordre supplémentaire.
Question 1
L'algorithme de tri par insertion est-il stable ? Pourquoi ?
Correction question 1
Cliquez ici pour révéler la correction.
Oui. Comme l'algorithme place les éléments en suivant l'ordre initial, celui-ci est préservé pour les éléments de même valeur.
Question 2
L'algorithme de tri par sélection est-il stable ? Pourquoi ?
Correction question 2
Cliquez ici pour révéler la correction.
Non. Un contre exemple suffit : [4, 4, 1]
Après la première itération on a [1, 4, 4]
avec les deux 4
inversés car le premier 4
a été échangé avec le 1
.
Exercice 5 : peut-on faire mieux ? (pour aller encore plus loin)
Question 1
Quel est l'algorithme de tri utilisé par la méthode list.sort()
et la fonction sorted()
de l'implémentation standard Python ?
Comparez son coût aux algorithmes étudiés dans ce TD.
Correction question 1
Cliquez ici pour révéler la correction.
Timsort parce qu'il est en O(n)
dans le meilleur cas, et en O(nlogn)
au pire.
C'est un algorithme hybride utilisant le tri par insertion et le tri par fusion (qui utilise une fonction de fusion de tableau similaire à notre fonction fusion_tableau
ci-dessus)