Les algorithmes de tris
Le problème
Étant donné une liste de n nombres naturels, il faut la trier dans l'ordre croissant.
Il existe plusieurs algorithme de tris :
Le tri par insertion.
Le tri par sélection.
Le tri bulle.
Le tri à peigne.
Le tri Shaker.
Le tri Shell.
Le tri Gnome.
Le tri par tas.
Le tri fusion.
Le tri rapide.
...
On ne va pas tous les étudier...
Voici une simulation visuelle de quelques algorithmes de tris :

Simulation : Le tri par insertion
Le programme ci-dessous vous permet de comprendre comment fonctionne un tri par insertion :
Et pour ceux qui n'aurait pas encore compris :
Au début, on compare le 1er et le second en les échangeant si nécessaire
Puis on compare le 3ème avec le second (on les échange si c'est nécessaire ) et on compare avec le 1er ( on les échange si nécessaire)
Dès que l'échange n'est pas nécessaire on passe à l'étape suivante : ( le terme est alors à sa place)
On compare le 4ème avec le 3ème (etc...)
De manière plus globale :
Au bout d'un certain 'temps', on considère le kème élément.
Il faut le comparer à ses précédents ( en les échangeant si nécessaire ) et dès que cela n'est plus nécessaire on passe à l'élément suivant ( k+1)
Voici l'algorithme : avec deux boucles bornées
liste ( contient une liste de n nombres entiers aléatoirement choisis entre 0 et 100)
afficher la liste
pour k variant de 1 à n-1 ( n-1 car liste[n-1] est le dernier puisque liste[0] est le premier )
pour j variant de k à 1 ( on décrémente de 1 )
si liste[j-1]>liste[j] alors on les échange
afficher la liste (triée)
Ou bien celui-ci : avec une boucle bornée et une boucle non bornée.
On place un terme dans une variable temporaire (tmp).
On le compare avec ses précédents en les décalant d'un rang (à droite) s'ils lui sont supérieurs.
Dans celui-ci il n'y a pas d'échange, mais un simple déplacement de termes ( voir la première visualisation)
liste ( contient une liste de n nombres entiers aléatoirement choisis entre 0 et 100)
afficher la liste
pour k variant de 1 à n-1 ( n-1 car liste[n-1] est le dernier puisque liste[0] est le premier )
tmp=liste[k]
j=k
tant que j>0 et tmp<liste[j-1]
on remplace liste[j] par liste[j-1]
j=j-1
liste[j]=tmp
afficher la liste (triée)
Vérifions la terminaison, la correction et la complétude de cet algorithme :
Pour le 1er algorithme : Les boucles étant bornées, la terminaison est assurée .
Pour le second : La boucle non bornée se termine car j est décrémenté de 1 tant qu'il est plus grand que 0.
Les algorithmes nous donnent bien une liste triée par ordre croissant...( des test nous le confirmerons, est-ce suffisant ?)
Dans ces algorithmes, pour une valeur de k, la partie de la liste à gauche de l'élément liste[k] est triée.
Et tant que la liste contient des nombres comparables la complétude est assurée
Pour ce qui est du coût de cet algorithme :
En supposant le pire des cas ( où il faut effectuer des échanges (décalages) jusqu'au bout - le cas d'une liste déjà triée dans l'ordre décroissant)
En supposant également qu'un échange (décalage) vaut pour une opération...
pour k = 1 , on effectue 1 échange ( ou un décalage )
pour k=2, 2 échanges (ou 2 décalages)
pour k=3, 3 échanges (ou 3 décalages)
....
pour k= n-1 , n-1 échanges (ou n-1 décalages)
Donc en tout :
On dit dans ce cas que le coût est d'ordre
( ou quadratique )
Écrire les programmes correspondant en Python
Rajouter un compteur d'échanges...
Exemple : Corrigé
Un programme possible :
# algorithme1
from random import*
liste=[]
compteur=0
for i in range(0,20,1):
el=randint(0,100)
liste.append(el)
print(liste)
print(len(liste))
L=len(liste)
for k in range(1,L,1):
for j in range(k,0,-1):
if liste[j-1]>liste[j]:
temp=liste[j]
liste[j]=liste[j-1]
liste[j-1]=temp
compteur=compteur+1
print(liste)
print("le nombre d'echange :" , compteur)
# algorithme2
from random import*
liste=[]
compteur=0
for i in range(0,20,1):
el=randint(0,100)
liste.append(el)
print(liste)
print(len(liste))
L=len(liste)
for k in range(1,L,1):
tmp=liste[k]
j=k
while j>0 and tmp<liste[j-1]:
liste[j]=liste[j-1]
j=j-1
compteur=compteur+1
liste[j]=tmp
print(liste)
print("le nombre d'echange :" , compteur)
Simulation : Le tri par sélection
En observant le tri par sélection ci-dessous :
Réalisez un algorithme de tri par sélection
Établir sa terminaison et sa complétude
Étudiez son coût
Corrigé
Un programme possible
from random import *
liste=[]
compteur=0
for i in range(0,20,1):
el=randint(0,100)
liste.append(el)
print(liste)
print(len(liste))
L=len(liste)
compteur=0
for k in range(0,L-1,1):
for j in range(k+1,L,1):
if liste[j]<liste[k]:
temp=liste[k]
liste[k]=liste[j]
liste[j]=temp
compteur=compteur+1
print(liste)
print("echange : ",compteur)
Complément : Pour les courageux
Réalisez un tri à bulle.
Un projet possible :
En vous inspirant du programme ci-dessous (Processing), comprendre et créer des visualisations de différents programmes de tris.
index=1
j=1
liste=[]
liste1=[]
couleur=[]
def setup():
size(800,300)
for i in range(0,30,1):
el=int(random(1,100))
liste.append(el)
liste1.append(el)
frameRate(10)
textSize(12)
for i in range(0,30,1):
col=(int(random(0,256)),int(random(0,256)),int(random(0,256)))
couleur.append(col)
def draw():
background(0)
for i in range(0,30,1):
fill(255)
text(liste1[i],25*i+30,50)
for i in range(0,30,1):
fill(255)
text(liste[i],25*i+30,150)
fill(couleur[i][0],couleur[i][1],couleur[i][2])
rect(25*i+30,160,5,liste[i])
tri_insertion()
def tri_insertion():
global liste,j,index
if j>0 and liste[j-1]>liste[j]:
temp=liste[j]
liste[j]=liste[j-1]
liste[j-1]=temp
temp2=couleur[j]
couleur[j]=couleur[j-1]
couleur[j-1]=temp2
j=j-1
else:
index=index+1
j=index
if index==len(liste):
noLoop()
# visualisation
if index<len(liste):
noFill()
stroke(255, 0, 0)
fill(255, 0, 0)
ellipse(index*25+37, 145, 25, 25)
noStroke()
fill(255)
text(liste[index], index*25+30, 150)
noFill()
stroke(0,255, 0)
fill(0, 255, 0)
ellipse(j*25+37, 145, 25, 25)
fill(245,157,15)
ellipse((j-1)*25+37,145,25,25)
noStroke()
fill(0,0,255)
text(liste[j], j*25+30, 150)
text(liste[j-1], (j-1)*25+30, 150)