Exercice 1 : Tri par sélection
Réaliser un algorithme itératif de tri par sélection en utilisant deux fonctions :
- Une fonction 𝑚𝑖𝑛𝑖𝑚𝑢𝑚(𝐿, 𝑘) qui renvoie l’index de la position du minimum d’une liste 𝐿 étudiée à partir de l’index 𝑘.
- Une fonction 𝑡𝑟𝑖(𝐿) qui retourne la liste 𝐿 triée en utilisant la fonction 𝑚𝑖𝑛𝑖𝑚𝑢𝑚 précédente.
Exercice 2 : Tri par sélection récursif
Réaliser un algorithme récursif de tri par sélection en utilisant deux fonctions :
- Une fonction 𝑚𝑖𝑛𝑖𝑚𝑢𝑚(𝐿) qui renvoie, de manière itérative, l’index de la position du minimum d’une liste 𝐿.
- Une fonction 𝑡𝑟𝑖(𝐿) récursive qui retourne la liste 𝐿 triée en utilisant la fonction 𝑚𝑖𝑛𝑖𝑚𝑢𝑚 précédente.
Exercice 3 : Tri par insertion récursif
Ecrire une fonction 𝑡𝑟𝑖_𝑟𝑒𝑐1() récursive qui :
- Prend pour argument une liste 𝐿 de longueur 𝑛 ainsi qu’un entier 𝑖. Au premier appel, on fixe 𝑖 = 1
- Le cas trivial stoppant le tri est celui pour lequel 𝑖 = 𝑛
- Dans les autres cas, cette fonction trie par insertion la liste jusqu’à l’index 𝑖 et effectue un appel récursif pour continuer le tri.
Exercice 4 : Tri par insertion décroissant
Ecrire un programme itératif de tri par insertion décroissant en utilisant la méthode 𝑒𝑛𝑢𝑚𝑒𝑟𝑎𝑡𝑒().
Exercice 5 : Tri par insertion
Nous avons vu dans le cours le tri par insertion en comparant la clé 𝐿[𝑖]
avec la fin de la sous-listes déjà triée. On peut envisager le tri par sélection itératif en comparant la clé en partant du début la sous-liste déjà triée. Proposer un programme permettant de réaliser ce type de tri.
Exercice 6 : Tri rapide et tri par insertion
On pourrait penser que le tri fusion est la meilleure méthode de tri.
Cependant, la situation du pire cas arrive rarement et c’est l’algorithme du tri rapide qui actuellement utilisé pour des grandes listes. Le tri par insertion est quant à lui très utilisé pour des listes de petites tailles (presque déjà triées). C’est le cas de la méthode sort sur python qui utilise les deux méthodes en fonction de la taille de la liste.
Adapter l’algorithme de tri rapide de manière à ce qu’une liste de longueur supérieure à 15 soit triée par un tri rapide et qu’une liste est de longueur inférieure à 15 soit triée par insertion.
Exercice 7 : Tri rapide et liste par compréhension
Ecrire l’algorithme de tri rapide en utilisant des listes par compréhension.
Exercice 8 : Tri rapide et complexité On considère l’algorithme suivant : def tri_rapide(L) :
if len(L)==0 or len(L)==1:
return L else :
L1=[]
L2=[]
for i in range(1,len(L)):
if L[i]>L[0]:
L1.append(L[i]) elif L[i]<L[0]:
L2.append(L[i])
return tri_rapide(L1)+[L[0]]+tri_rapide(L2)
1) Modifier le programme précédent en utilisant une variable globale 𝑁 permettant de mesurer le nombre de comparaisons que cette fonction effectue lors du tri décroissant d’une liste 𝐿.
2) Obtenir le tracé représentant 𝑁 pour quelques listes d’entiers triées de manière décroissante et de longueur 𝑛 = [100,200,300,400 … ,1000].
3) Superposer au graphique précédent la courbe 𝑁(𝑛) attendue mathématiquement.
Exercice 9 : Tri rapide et complexité (suite)
1) Obtenir le tracé représentant 𝑁 pour quelques listes d’entiers triées de manière aléatoire (avec la fonction np.random.randint(0,1000,n)) et de longueur 𝑛 = [100,200,300,400 … ,1000].
2) Superposer au graphique précédent la courbe 𝑁(𝑛) attendue mathématiquement.
Exercice 10 : Tri rapide en place
L’inconvénient du tri rapide présenté en cours est qu’il est gourmand en complexité spatiale car il nécessite la création de deux nouvelles listes à chaque appel.
L’idée reste de proposer une fonction qui positionne les éléments plus grands que le pivot à sa droite et les éléments les plus petits à sa gauche sans créer de nouvelles listes :
def partition(t):
pivot=t[0]
position=0
for i in range (1,len(t)):
if t[i]<pivot:
t[i],t[position+1]=t[position+1],t[i]
position =position+1 if position !=0 :
t[0],t[position]=t[position],t[0]
return position
1) On souhaite tester la fonction précédente avec la liste t=[5,0,9,1,8,2,7,3,6,4]. Ecrire les modifications successives de t.
2) Proposer un algorithme récursif utilisant la fonction 𝑝𝑎𝑟𝑡𝑖𝑡𝑖𝑜𝑛() et qui permet d’obtenir une liste triée.
Exercice 11 : Temps d’exécution des tri fusion et insertion
Pour mesurer le temps d’exécution d’un programme, on importe la fonction 𝑡𝑖𝑚𝑒 du module 𝑡𝑖𝑚𝑒 avec l’instruction :
from time import time
Pour générer un entier tiré au hasard, on importe la fonction 𝑛𝑝. 𝑟𝑎𝑛𝑑𝑜𝑚. 𝑟𝑎𝑛𝑑𝑖𝑛𝑡(𝑑𝑒𝑏𝑢𝑡, 𝑓𝑖𝑛) du module 𝑛𝑢𝑚𝑝𝑦
On souhaite comparer les temps d’exécution du tri insertion et du tri fusion sur deux types de listes : une liste de nombres pris au hasard et une liste de nombre déjà triée.
1) Construire une fonction 𝑙𝑖𝑠𝑡𝑒_𝑎𝑙𝑒𝑎 de la variable 𝑛générant une liste de 𝑛 entiers pris au hasard entre 1 et 10000, bornes comprises.
2) Construire une fonction 𝑙𝑖𝑠𝑡𝑒_𝑡𝑟𝑖𝑒𝑒 de la variable 𝑛générant une liste de 𝑛 entiers triés de manière croissantes ente 0 à 𝑛 − 1, bornes comprises.
3) Mesurer les temps d’exécution des programmes de tri insertion et de tri fusion pour trier des listes déjà triées puis des listes de nombres aléatoires pour 𝑛 = 20000 et 𝑛 = 40000. Conclure.
Exercice 12 : Ordre lexicographique
L’objectif est d’écrire un programme qui trie une liste de mots (type 𝑠𝑡𝑟) suivant l’ordre lexicographique.
1) Ecrire une fonction 𝑜𝑟𝑑𝑟𝑒_𝑎𝑙𝑝ℎ𝑎𝑏𝑒𝑡𝑖𝑞𝑢𝑒 qui prend en argument deux caractères alphabétiques 𝑐1 et 𝑐2 et renvoie −1 si 𝑐1 est avant 𝑐2, 1 si 𝑐2 est avant 𝑐1 et 0 si 𝑐1 = 𝑐2.
2) Ecrire une fonction 𝑜𝑟𝑑𝑟𝑒_𝑙𝑒𝑥𝑖𝑐𝑜𝑔𝑟𝑎𝑝ℎ𝑖𝑞𝑢𝑒 qui prend pour argument deux mots différents 𝑚1 et 𝑚2 et renvoie −1 si "m1" <
"𝑚2" pour l’ordre lexicographique, 0 si "𝑚1 = 𝑚2" et 1 si "𝑚1 >
𝑚2". On utilisera la fonction 𝑜𝑟𝑑𝑟𝑒_𝑎𝑙𝑝ℎ𝑎𝑏𝑒𝑡𝑖𝑞𝑢𝑒
3) Ecrire une fonction 𝑡𝑟𝑖_𝑙𝑒𝑥𝑖𝑐𝑜𝑔𝑟𝑎𝑝ℎ𝑖𝑞𝑢𝑒 qui prend en argument une liste de mots et trie cette liste. On utilisera la fonction 𝑜𝑟𝑑𝑟𝑒_𝑙𝑒𝑥𝑖𝑐𝑜𝑔𝑟𝑎𝑝ℎ𝑖𝑞𝑢𝑒 avec l’algorithme de tri par insertion.