PC* Carnot Dijon

publicité
Algorithmes de tris
PC*
La problématique de tri d'un ensemble d'ob jets pouvant être ordonnés est une
question fondamentale dans le traitement des données. De nombreux algorithmes
portant sur de tels ensembles sont facilités si on dispose d'une relation d'ordre et
si on est par exemple capable de ranger les éléments de l'ensemble dans l'ordre
croissant. Cet ordre peut être l'ordre naturel sur l'ensemble des réels, sur l'ensemble
des entiers écrits dans une base b quelconque ou encore l'ordre lexicographique pour
trier des chaînes de caractère.
Dans la suite on va présenter quelques algorithmes usuels de tri et on les appliquera à des listes d'entier. On comparera les performances de ces algorithmes et leur
facilité d'implémentation.
I : Tri par insertion
C'est la manière naturelle de trier par exemple un tas de copie par ordre
de note ou par ordre alphabétique. On prend la copie du haut du tas,
puis la deuxième que l'on place dans l'ordre choisi, puis la troisième que
l'on insère à sa bonne place etc. Ce tri est lent mais on voit évoluer en
haut un tas trié et en dessous un tas non encore trié. Le travail
s'achève avec la dernière copie.
1. Un exemple
Nous utiliserons cette méthode en triant les éléments d'une liste
en plaçant le plus petit terme à gauche et en commençant avec le
premier élément à gauche de la liste.
(a) Utiliser cette méthode pour trier pas à pas la liste
L = [7, 2, 5, 8, 4, 6, 2, 3, 6].
(b) Même étude avec les deux listes L1 = [1, 3, 5, 7, 9, 12] et
L2 = [9, 7, 6, 4, 2, 1]. Que constatez-vous ?
2. Mise en place de l'algorithme
(a) Écrire une procédure Tri_insert qui partant d'une liste a la
retourne triée. On remarquera qu'il n'y pas de copie de liste
mais seulement des échanges d'éléments deux à deux.
On pourra imbriquer deux boucles.
(b) Mettre en évidence dans la boucle principale une structure qui
dépend de l'indice i mais qui reste inchangée à chaque étape.
Cette structure est appelée invariant de boucle.
(c) Mettre en évidence un invariant de la boucle secondaire.
I.P.T. Tris 1
PC*
Algorithmes de tris
3. Performance de l'algorithme
(a) Donner un majorant du nombre d'opérations élémentaires nécessaires pour trier une liste de n éléments.
(b) Préciser le nombre d'opérations dans le pire des cas et dans le
meilleur des cas.
(c) Quelle complexité obtient-on pour cet algorithme ?
(d) On peut estimer qu'en moyenne, pour insérer un nouvel élément sur dans une liste triée de de p éléments, on eectue
p/2 échanges. Donner une estimation moyenne d'une nombre
d'opérations nécessaires au tri.
II : Tri rapide quicksort L'idée essentielle de cette deuxième méthode est de diviser le problème
en deux sous-problèmes de tri de taille plus petite. Pour qu'au nal le
tas soit eectivement trié il sut de découper le paquet en deux parties
A et B avec ∀a ∈ A, ∀b ∈ B, a ≤ b.
Pour trier un jeu de cartes on peut ainsi repérer une carte pivot qui permettra de diviser le paquet en deux sous paquets ayant cette
propriété puis on recommence sur chacun des sous-paquets crées. Pour
obtenir les deux sous-paquets on procède à des comparaisons et à des
échanges. Cette méthode semble intuitivement plus rapide.
1. Exemple
Trier par cette méthode la liste de cartes (de même couleur)
L = [3, 1, 6, 7, D, 9, 5, 8, 10, R, V, 2, 4].
Comptabiliser le nombre d'échanges.
2. Séparer en deux listes
(a) Écrire une procédure Echange(a,i,j) qui échange les termes
a[i] et a[j] de la liste.
(b) Écrire une procédure Partition(a,g,d) a étant une liste, g, d
deux indices avec 0 ≤ g < d ≤ len(a) qui prend a[g] comme
pivot et qui modie la partie [a[g], . . . , a[d]] en une partie de
même longueur mais du type
[éléments plus petits que a[g]], a[g], [éléments plus grands que a[g]].
(c) Écrire une procédure Tri_rapide qui pourra faire appel à ellemême (procédure récursive).
I.P.T. Tris 2
PC*
Algorithmes de tris
3. Performance de l'algorithme
On note C(n) le coût du tri rapide pour un tableau de taille n.
(a) Montrer que dans le pire des cas C(n) = n − 1 + C(n − 1).
(b) Montrer que dans le meilleur des cas C(n) = n − 1 + 2C(n/2).
En déduire alors que C(n) ∼ n ln(n).
4. Amélioration de l'algorithme
Pour se placer dans le meilleur des cas on propose un choix aléatoire
du pivot puis on diminue les appels récursifs par l'utilisation d'une
boucle.
(a) Partition_2(a,g,d)
Python
def Partition_2(a,g,d):
assert g<d
echange(a,g,random.randint(g,d-1))
Sto=a[g]
k=g
for i in range (g+1,d):
if a[ i ]<Sto:
k=k+1
echange(a,i ,k)
if g!=k:
echange(a,g,k)
return k
(b) Tri_rapide_rec2(a,g,d)
Python
def Tri_rapide_rec2(a,g,d)
while g<d-1:
m=Partition_2(a,g,d)
if m-g <d-m-1:
Tri_rapide_rec2(a,g,m)
g=m+1
else :
Tri_rapide_rec2(a,m+1,d)
d=m
I.P.T. Tris 3
Algorithmes de tris
PC*
Python
(c) Tri_rapide2(a)
def Tri_rapide2(a):
Tri_rapide_rec2(a,0,len(a))
Attention : avant l'exécution de ces fonctions il est nécessaire de
charger le module random sous le nom random. On écrira :
import random as random
III : Tri par fusion
On utilise encore un principe de division en deux paquets. Mais on trie
chacun des deux paquets puis on fusionne les deux paquets triés.
1. Écrire un algorithme qui fusionne deux paquets supposés déjà triés.
2. Donner une écriture récursive du tri fusion.Évaluer la complexité
de l'algorithme. Étudier l'exemple d'écriture :
Python
def Fusionner(a,b):
i=j=0
n1,n2,n=len(a),len(b),len(a)+len(b)
L=[]
while i< n1 and j< n2:
if a[ i ]<=b[j]:
L.append(a[i])
i=i+1
else :
L.append(b[j])
j=j+1
if i==n1:
while j <n2:
L.append(b[j])
j=j+1
else :
while i<n1:
L.append(a[i])
i=i+1
return (L)
I.P.T. Tris 4
Algorithmes de tris
PC*
3. Et pour le programme principal :
def Tri_fusion(a):
N=len(a)
if N <=1:
Python
return a
else :
m=N//2
a1=Tri_fusion(a[0:m])
a2=Tri_fusion(a[m:N])
return Fusionner(a1,a2)
IV : Médiane d'une série statistique
Soit L = [x0 , . . . , xn−1 ] une liste de n réels. Notons L′ = [x′0 , . . . , x′n−1 ] la
liste obtenue en triant les éléments de L par ordre croissant. Distinguons
deux cas :
1. Si n est un entier impair, n = 2p + 1, la médiane de L est l'élément
α = x′p . Il y a p éléments de L plus petits que α et p éléments de
L plus grands que α.
2. Si n est un entier pair, on conviendra qu'il y a deux valeurs médianes, x′p−1 et x′p .
Les procédures de tri déjà exposées permettent donc d'obtenir un
calcul de la médiane.
Ecrire une procédure de calcul de médiane d'une série statistique de n éléments avec un coût en O(n ln(n)).
Il est possible d'obtenir la médiane avec un algorithme en O(n). Voici
par exemple les étapes d'un tel algorithme :
1. Écrire une fonction d'argument un tableau de 5 éléments et qui en
retourne la médiane (idem avec au plus 5 éléments).
2. Dénir une fonction qui partant d'un tableau de n éléments le divise
en sous-tableaux de 5 éléments (au plus) et retourne la liste des
médianes de chacun des sous-tableaux obtenus.
3. Déterminer récursivement la médiane des médianes :A.
4. Trier la liste à partir du pivot A obtenu : à savoir des éléments
inférieurs à A,A, des éléments supérieurs à A.
I.P.T. Tris 5
PC*
Algorithmes de tris
5. Recommencer si nécessaire.
Eectuer à la main cette procédure pour le tableau suivant :
L = [12, 5, 45, 7, 84, 13, 24, 56, 8, 9, 14, 17, 42, 30, 24, 50, 9, 13, 22, 44, 17, 8, 45]
On remarquera que dans le cas général c'est la même procédure qui
conduit à rechercher la médiane ou la valeur du k-ème élément d'une
liste.
On pourra obtenir par exemple :
def valeur_indice(a,k):
if k>= len(a):
raise ValueError('position demandée extérieure à la liste ' )
elif len(a)<=5:
return valeur_indice_petit(a,k)
else :
Sto=[]
q=len(a)//5
r=len(a)-5*q
for i in range(0,q):
Sto.append(valeur_indice_petit(a[5*i:5*i+5],2))
if r>0:
Sto.append(valeur_indice_petit(a[5*q:5*q+r],r//2))
Python
Pseudo=valeur_indice(Sto,len(Sto)//2)
K,inf ,sup=Premier_tri(a,Pseudo)
if K==k:
return Pseudo
elif K>k:
return valeur_indice(inf,k)
else :
return valeur_indice(sup,k-K)
I.P.T. Tris 6
Téléchargement