Transparent Algo S 1
Transparent Algo S 1
Transparent Algo S 1
Sylvain Daudé
HAI101I / HA8203I
Objet du cours
Ce cours porte sur les algorithmes récursifs et itératifs ainsi que leur efficacité
(complexité en temps). Deux sections sont dédiées aux algorithmes de
recherche et de tri. Une synthèse du cours est disponible en ligne sur Moodle.
Principale référence
T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein. Introduction à
l’algorithmique. Ed. Dunod.
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
Remarques
Certains problèmes sont bien posés mais trop complexes pour un
algorithme ("indécidables" ou "incalculables"). Exemple : problème de
l’arrêt.
Certains algorithmes nécessiteraient trop de ressources pour être
concrètement utilisés. Exemple : le jeu d’échecs : 300 millions d’années
pour connaître les conséquences de chaque coup.
Dans ces deux cas, on adopte des méthodes approchées, les
heuristiques.
Sylvain Daudé (UM - FDS) Algorithmique 1 6 / 118
Vocabulaire sur un exemple en pseudo-code
Signature ou prototype
produit ← 1
variables : emplacements nommés
de stockage
pour i de 1 à n faire
finPour
Renvoyer produit
Fin
Définitions
Algorithme itératif : contient au moins une boucle (pour, tant que).
Algorithme récursif : partiellement défini à partir de lui-même et d’un cas
de base.
Remarque
Un algorithme peut être ni itératif ni récursif, les deux à la fois ou seulement
un des deux : tout est possible.
D ÉBUT
res ← 1
pour i de 1 à n faire
res ← res * i
fin pour;
Renvoyer res
F IN
D ÉBUT
si n = 0 alors
Renvoyer 1
sinon
Renvoyer n × FactRec(n − 1)
fin si;
F IN
Correction
types : nombre, entier
constantes : 0, 1
variables : res
appels : PuisRec(n-1,x) (appel récursif)
opérations : res←1 (affectation), res*x, res←res*x, n=0, n-1, x ×
PuisRec(n-1,x)
expressions : tous les précédents sauf affectations, x, n (paramètres), i
(indice de boucle)
Attention !
L’indice de boucle et les paramètres ne sont pas des variables.
Définition
Une structure conditionnelle modifie le déroulement de l’algorithme selon
qu’une condition est vérifiée ou non.
Syntaxe
si a alors En Python :
instructions I if a :
sinon si b1 alors Instructions I
instructions SS1 elif b1 :
... Instructions SS1
sinon si bn alors (...)
instructions SSn elif bn :
sinon Instructions SSn
instructions S else :
fin si; Instructions S
D ÉBUT D ÉBUT
i←0 i←0
si i<3 alors si i<3 alors
i←5 i←5
sinon si i≥4 alors fin si;
i←7 si i≥4 alors
fin si; i←7
afficher i fin si;
F IN afficher i
F IN
Correction : 5
Correction : 7
Syntaxe
tant que expression faire
instructions
fin tq;
En Python :
while expression :
instructions
Remarque
La boucle Tant que est plus générale que la boucle Pour.
Sylvain Daudé (UM - FDS) Algorithmique 1 18 / 118
A vous de jouer !
D ÉBUT D ÉBUT
i←0 i←5
tant que 2*i+1<10 faire tant que i 6= 0 faire
i ← i+1 i ← i-2
fin tq; fin tq;
Renvoyer i Renvoyer i
F IN F IN
Syntaxe
pour i de a à b [par pas de c] faire
instructions
fin pour;
# Par défaut, c=1 ;
# Pas d’itération si i dépasse strictement la valeur b.
En Python :
for i in range (a, B, c) :
Instructions
# Par défaut, a=0 et c=1 ;
# Pas d’itération si i dépasse ou prend la valeur B
Correction : 6 4 12 8 18 12 24 16 30
20
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
Définition
Dans ce cours, les "structures linéaires" (tableaux 1D, listes, piles, files)
contiennent des données de même type "alignées" les unes derrière les
autres.
1 tableaux 1D
2 listes
3 piles
4 files
Définition
Un tableau 1D statique contient un nombre fixe d’éléments appelé taille du
tableau. Chaque élément du tableau est repéré par un entier naturel situé
entre 0 et la taille du tableau -1, appelé l’indice de l’élément. Un tableau ne
peut pas être vide.
Exemple de représentation
Si le tableau T contient les valeurs 1, 6 et 10 dans cet ordre, on peut le noter
T [i] 1 6 10
[1,6,10] et le représenter ainsi :
i 0 1 2
On a taille(T)=3, T[0]=1, T[1]=6, T[2]=10.
P ROCÉDURE :
doubleTab (ES T : tableau d’entier)
S PÉCIFICATIONS :
Double les valeurs de T.
D ÉBUT
pour i de 0 à Taille(T)-1 faire
T[i] ← 2*T[i]
fin pour;
F IN
F ONCTION : creeTabCarres
(E n : entier) : tableau d’entier
S PÉCIFICATIONS : Crée le
tableau des n premiers carrés (n ∈ N)
D ÉBUT
pour i de 0 à Taille(T)-1 faire
T[i] ← i*i
fin pour;
Renvoyer T
F IN
D ÉBUT
cpt ← 0 ;
pour i de 0 à taille(T)-1 faire
si T[i]=e alors
cpt ← cpt + 1
fin si;
fin pour;
Renvoyer cpt
F IN
Définition
Une liste est soit vide, soit constituée d’un élément, sa tête, suivie d’une liste,
sa queue.
Constantes : la liste vide [ ]
Fonctions prédéfinies :
Tête d’une liste non vide : F ONCTION : tête(E liste) : élément
Queue d’une liste non vide : F ONCTION : queue(E liste) : liste
Construction avec tête et queue : F ONCTION : cons (E élément, E liste) :
liste
Test qu’une liste est vide : F ONCTION : estVide(E liste) : booléen
Exemple
L ← cons (1, cons (2, cons(3, [] ))) —–> L vaut [1, 2, 3], tête 1, queue [2,3]
A ← tête(queue(queue(L))) ——> A vaut 3
Test si L a au moins 2 éléments : non(estVide(L)) et non
estVide(queue(L))
F ONCTION :
longueur (E L : liste d’entier) : entier
S PÉCIFICATIONS :
Renvoie la longueur de la liste L.
D ÉBUT
cpt, M ← 0, L ;
tant que non estVide(M) faire
cpt, M ← cpt+1, queue(M)
fin tq;
Renvoyer cpt
F IN
F ONCTION :
doubleListe (E L : liste d’entier) : liste
d’entier
S PÉCIFICATIONS :
Renvoie une nouvelle liste contenant les
valeurs de L doublées.
D ÉBUT
si estVide(L) alors
Renvoyer [ ]
sinon
Renvoyer
cons(2*tête(L),doubleListe(queue(L)))
fin si;
F IN
F ONCTION : cptEgauxLIte (E e :
F ONCTION : cptEgauxLRec (E e :
entier, E L : liste d’entiers) : entier
entier, E L : liste d’entiers) : entier
S PÉCIFICATIONS : Compte le nombre
S PÉCIFICATIONS : Compte le nombre
d’éléments de L égaux à e (itératif)
d’éléments de L égaux à e (récursif)
Variable : cpt: entier ; M: liste d’entiers
D ÉBUT
si estVide(L) alors
D ÉBUT Renvoyer 0
M,cpt L,0 ;
sinon si tête(L)=e alors
tant que non estVide(M) faire Renvoyer 1+
si tête(M)=e alors cptEgauxLRec(e,queue(L))
cpt cpt+1
sinon
fin si; Renvoyer
M queue(M) cptEgauxLRec(e,queue(L))
fin tq; fin si;
Renvoyer cpt
F IN
F IN
Remarque
En Python, les listes sont représentées par des tableaux.
liste vide : []
tête(liste) : liste[0]
queue(liste) : liste[1:]
cons(élément, liste) : [élément]+liste
estVide(liste) : not liste
Définition
Une pile est soit vide, soit constituée d’un élément, son sommet, suivi d’une
pile. On accède à ce qui vient d’être empilé. La lecture du sommet est
destructive.
Constantes : la pile vide [ ]
Fonctions prédéfinies :
Suppression du sommet d’une pile non vide et retour de sa valeur :
F ONCTION : depiler(ES pile) : élément
Ajout d’un élément au sommet d’une pile :
P ROCÉDURE : empiler(E élément, ES pile)
Test qu’une pile est vide :
F ONCTION : estVide(E pile) : booléen
Exemple
P ← [ ] ; empiler (1, P) ; empiler(2, P) —-> P vaut [1, 2] (sommet : 2)
A ← depiler(P) —-> A vaut 2, P vaut [1]
P ROCÉDURE :
doublePile (ES P : pile d’entier)
S PÉCIFICATIONS :
Double les valeurs de P.
D ÉBUT
Q←[];
tant que non estVide(P) faire
empiler(2*depiler(P), Q)
fin tq;
tant que non estVide(Q) faire
empiler(depiler(Q), P)
fin tq;
F IN
D ÉBUT
Q, R ← [ ], [ ] ;
tant que non estVide(P) faire
empiler(depiler(P), Q)
fin tq;
tant que non estVide(Q) faire
empiler(depiler(Q), R)
fin tq;
tant que non estVide(R) faire
empiler(depiler(R), P)
fin tq;
F IN
Sylvain Daudé (UM - FDS) Algorithmique 1 39 / 118
Piles en Python
Remarque
En Python, les piles sont représentées par des tableaux.
pile vide : []
depiler(P) : P.pop()
empiler(e, P) : P.append(e)
estVide(P) : not P
Définition
Une file est soit vide, soit constituée d’un élément, sa tête, suivi d’une file, sa
queue. On accède au premier élément enfilé. La lecture du sommet est
destructive.
Constantes : la file vide [ ]
Fonctions prédéfinies :
Suppression de la tête d’une file non vide et retour de sa valeur :
F ONCTION : defiler(ES file) : élément
Ajout d’un élément en fin de file :
P ROCÉDURE : enfiler(E élément, ES file)
Test qu’une file est vide :
F ONCTION : estVide(E file) : booléen
Exemple
F ← [] ; enfiler(1, F) ; enfiler (2, F) ; enfiler (3, F) —-> F = [1, 2, 3] (tête : 1)
A ← defiler(F) —-> A vaut 1, F vaut [2, 3]
D ÉBUT
P←[];
tant que non estVide(F) faire
empiler(defiler(F), P)
fin tq;
tant que non estVide(P) faire
enfiler(depiler(P), F)
fin tq;
F IN
Remarque
En Python, les files sont représentées par des tableaux.
file vide : []
defiler(F) : F.popleft() [utilise la collection deque]
enfiler(e, F) : F.insert(0,e)
estVide(F) : not P
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
Un bon algorithme...
... est un algorithme qui se termine, c’est à dire qui effectue un nombre fini
d’opérations. Cette section présente une méthode pour prouver qu’un
algorithme se termine.
Méthode
Pour prouver qu’un algorithme itératif se termine, on prouve :
qu’il y a un nombre fini d’itérations
que chaque itération se termine.
Exemple
D ÉBUT
res ← 1
pour i de 1 à n faire
res ← res * i
fin pour;
Renvoyer res
F IN
D ÉBUT
p, res ← n, 0
tant que p>0 faire
p, res ← p div 2, res + 1
fin tq;
Renvoyer res
F IN
F ONCTION :
F (E n : entier) : entier
F ONCTION :
G (E a,b : entier) : entier
Méthode
Pour prouver qu’un algorithme récursif se termine, on montre :
qu’il y a un nombre fini d’appels récursifs ;
qu’en dehors des appels récursifs, il y a un nombre fini d’opérations.
D ÉBUT
si n=0 alors
Renvoyer 1
sinon
Renvoyer n*F2(n-1)
fin si;
F IN
F ONCTION :
F (E n : entier) : entier
F ONCTION :
G (E a,b : entier) : entier
En dehors des appels récursifs, il
S PÉCIFICATIONS : a, b ∈ N∗ avec y a un nombre fini d’opérations.
a ≤ b. Calcule quelque chose.
a diminue de 1 à chaque appel,
D ÉBUT 1 est un diviseur de b donc le cas
si b mod a = 0 alors de base est atteint en au plus
Renvoyer a a − 1 appels récursifs. Il y a donc
sinon un nombre fini d’appels récursifs.
Renvoyer G(a-1,b) L’algorithme se termine.
fin si;
F IN
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
Un bon algorithme...
... est un algorithme valide, c’est à dire conforme aux spécifications. Cette
section présente une méthode pour prouver qu’un algorithme est valide.
Vocabulaire
équation de récurrence : égalité reliant les valeurs des variables d’une
itération à la suivante ;
invariant de boucle : propriété censée être vraie à chaque itération.
Méthode
1 "lire" les équations de récurrence sur l’algorithme ;
2 chercher un invariant de boucle ;
3 prouver l’invariant par récurrence ou induction ;
4 écrire l’invariant à la fin des itérations.
Z0 0 Zi+1 Zi + Ai + Bi Zi i3
2 Preuve de l’invariant :
A0 1 3×0+1
B0 0 3 × 02
Initialisation : Pour i = 0,
C0 = n = n − 0
: Inv0 est vrai.
3
Z0 0 0
: Pour 0<i ≤
Récurrence n, onsuppose Invi vrai. On
utilise
ER et Invi :
Ai+1 Ai + 3 3i + 1 + 3 3(i + 1) + 1
Bi+1 Bi + 2Ai + 1 3i 2 + 2(3i + 1) + 1 3(i + 1)2
Ci+1 = Ci − 1
= =
n − i − 1 n − (i + 1)
3 2 3
Zi+1 Zi + Ai + Bi i + 3i + 1 + 3i (i + 1)
Donc Invi+1 est vrai.
Conclusion : l’invariant est vrai pour toutes les itérations.
3 Conclusion : à la fin du TantQue, Ci = 0 = n − i donc i = n.
La valeur renvoyée est Zn = n3 : l’algorithme est valide.
Sylvain Daudé (UM - FDS) Algorithmique 1 62 / 118
A vous de jouer ! Compléter la preuve
1 Eq. récurrence : res0 =T [0]
F ONCTION : somTab resi+1 =resi + T [i + 1]
(E T : tableau d’entiers) : entier 2 Invariant : Invi : "resi est la somme des
valeurs T [0] à T [i]".
S PÉCIFICATIONS : Renvoie 3 Preuve de l’invariant (n=taille(T )) :
la somme des valeurs de T. Initialisation : pour i = 0,
res0 = T [0] somme des valeurs de
Variable : res : entier T [0] à T [0]. Inv0 est vrai.
Récurrence : on suppose Invi vrai
D ÉBUT pour 0 ≤ i ≤ n − 2.
res ← T[0] Comme resi+1 =resi + T [i + 1]
pour i de 1 à et resi =T [0] + . . . + T [i]
taille(T)-1 faire on a resi+1 =T [0] + . . . + T [i + 1].
res ← res+T[i] Donc Invi+1 est vrai.
fin pour; Conclusion : l’invariant est vrai
Renvoyer res pour toutes les itérations.
F IN 4 A la fin du pour, l’algorithme renvoie
resn−1 = T [0] + . . . + T [n − 1] :
l’algorithme est valide.
Sylvain Daudé (UM - FDS) Algorithmique 1 63 / 118
Validité d’un algorithme récursif
Vocabulaire
équation de récurrence : égalité reliant le résultat de l’algorithme avec
celui des appels récursifs ;
invariant de récursion : propriété censée être vraie à chaque appel
récursif. Le plus souvent, c’est simplement le résultat principal de
l’algorithme.
Méthode
1 "lire" les équations de récurrence sur l’algorithme ;
2 poser l’invariant de récursion ;
3 prouver l’invariant par récurrence ou induction ;
4 la conclusion de la récurrence prouve l’algorithme.
1 Équation de récurrence :
F 2(0) =1 et F 2(n) = n × F 2(n − 1)
F ONCTION : pour n > 0.
F2 (E n : entier) : entier 2 Invariant proposé :
Invn : "F 2(n) = n!"
S PÉCIFICATIONS : calcule
la factorielle d’un entier n ∈ N.
3 Preuve de l’invariant :
Initialisation : Pour n = 0, on a
D ÉBUT F 2(0) = 1 = 0! donc Inv0 est vrai.
Récurrence : Pour n>0,
si n=0 alors
on suppose Invn−1 vrai,
Renvoyer 1
c’est à dire F 2(n − 1) = (n − 1)!
sinon
Alors F 2(n) = n × F 2(n − 1) =
Renvoyer n*F2(n-1)
n × (n − 1)! = n!.
fin si; Donc Invn est vrai.
F IN Conclusion : pour tout n ∈ N,
F 2(n) = n!
4 L’algorithme est valide.
Équations de récurrence :
si n < a alors F 4(a, n) = 0
et si n ≥ a alors F 4(n) = n + F 4(a, n − 1)
F ONCTION : Invariant :
F4 (E a, n : entier) : entier) Invn : "F 4(a, n) = somme de [a, n]".
Initialisation : Pour n < a, F (a, n) =0.
S PÉCIFICATIONS : Renvoie Comme [a, n] est vide, sa somme vaut 0.
la somme des entiers de [a, n] Donc Inv0 est vrai.
Récurrence : Pour n ≥ a,
D ÉBUT on suppose Invn−1 vrai :
Renvoyer cond(n<a, 0, F 4(a, n − 1) = somme de [a, n − 1].
n+F4(a,n-1)) Comme F 4(a, n) = n + F 4(a, n − 1),
F IN F 4(a, n) est la somme de [a, n].
Donc Invn est vrai.
Conclusion : pour tout n ∈ N,
F 4(a, n) renvoie la somme de [a, n].
L’algorithme est valide.
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
Un bon algorithme...
... est un algorithme efficace, c’est à dire qu’il effectue un nombre raisonnable
d’opérations et utilise un espace de calcul raisonnable.
Définitions
Complexité en temps : nombre d’opérations élémentaires en fonction
des paramètres d’entrée.
Complexité en espace : nombre de données à mémoriser en fonction
des paramètres d’entrée.
Algorithme efficace : algorithme de faible complexité.
Algorithme optimal : algorithme de complexité minimale (impossible de
faire mieux).
Ce cours se limite :
à la complexité en temps, appelée abusivement "complexité" ;
au cas où elle s’exprime en fonction d’un entier n "taille de l’entrée" :
taille d’un tableau, longueur d’un entier...
F ONCTION :
F4 (E n : entier) : entier
Définitions
Soient Tn et Un deux suites numériques.
Tn domine Un s’il existe C ∈ R et n0 ∈ N tels que ∀n ≥ n0 , Un ≤ CTn .
Tn et Un sont du même ordre si Tn domine Un et Un domine Tn .
Tn domine strictement Un si elle domine Un sans être du même ordre.
Notations de Landau
Tn domine Un : Tn = Ω(Un ) ou Un = O(Tn )
Tn et Un sont du même ordre : Tn = θ(Un ) ou Un = θ(Tn )
Tn domine strictement Un : Tn = ω(Un ) ou Un = o(Tn )
O Ω
o θ ω Non comparable
Propriétés
Soient Tn et Un deux suites strictement positives à partir d’un certain rang.
si Tn /Un a pour limite 0, alors Un domine strictement Tn .
si Tn /Un a une limite finie >0, alors Tn et Un sont du même ordre.
si Tn /Un a pour limite +∞, alors Tn domine strictement Un .
Remarques
Réciproque de la deuxième propriété fausse ;
Deux suites qui ne se dominent pas l’une l’autre sont "non comparables
asymptotiquement".
Catégories de complexité
Un algorithme est dit :
à coût constant siTn = O(1) (abus de langage) ;
logarithmique si Tn = O(log(n)) ;
linéaire si Tn = θ(n) ;
semi-linéaire si Tn = θ(nlog(n)) ;
quadratique si Tn = θ(n2 ) ;
polynomial si Tn = O(na ) avec a entier naturel ;
exponentiel si Tn = θ(an ) avec a > 1.
Propriétés
Croissances comparées : chaque suite est strictement dominée par la
suivante :√
1, log(n), n, n, nlog(n), n2 , n3 , 2n , 3n , n!
tous les logarithmes sont du même ordre de grandeur :
log10 (n) = θ(log2 (n)) = θ(ln(n)) etc.
o, O, ω, Ω, θ sont multiplicatives sur les suites strictement positives :
si a = o(a0 ) et b = o(b0 ) alors ab = o(a0 b0 ) et de même pour O, ω, Ω, θ.
Propriété
Si des boucles pour imbriquées contiennent un nombre borné d’opérations
élémentaires, alors leur complexité est du même ordre que le produit du
nombre de valeurs prises par les indices de boucle.
Exemple
D ÉBUT
pour i de 1 à n faire
pour j de 1 à i faire
op élém
fin pour;
fin pour;
F IN
i prend n valeurs = O(n), j prend i valeurs = O(n), donc l’ensemble des deux
boucles a une complexité O(n2 ).
Exemple
D ÉBUT
pour i de 1 à n faire
pour j de 1 à i faire
op élém
fin pour;
fin pour;
F IN
n X
i n
X X n(n + 1)
Nombre d’opérations : 1= i= =θ(n2 )
2
i=1 j=1 i=1
Complexités en fonction de n ?
1 Pour i de 1 à n faire 1 Itérations à coût borné,
Pour j de 1 à n faire n valeurs pour les deux pour :
som ← som+1 Tn = θ(n2 )
2 Pour i de 1 à n faire 2 Itérations à coût borné,
Pour j de 1 à n faire n valeurs pour les trois pour :
Pour k de 1 à n faire Tn = θ(n3 )
som ← som+1 3
Pn Pn
Tn = i=1 j=i 2 =
Pn
3 Pour i de 1 à n faire 2 Pi=1 n − i + 1 =
n
Pour j de i à n faire 2 i 0 =1 i 0 = n(n + 1)
som ← som + 1 √
4 i 2 < n équivaut
√ ài < n
4 i ← 0 ; TantQue i²<n faire donc Tn = 2 n
i ← i+1
Exemple
D ÉBUT
si n=0 alors
Renvoyer 1
sinon
Renvoyer n × F 2(n − 1)
fin si;
F IN
T0 = 1
Equation de récurrence :
Tn = 4 + Tn−1 si n > 0
Résolution : Tn = 4n + 1 = θ(n).
D ÉBUT
Renvoyer cond(n=0, true, cond(n=1, false, estPair(n-2)))
F IN
Master Theorem
Master Theorem : s’il existe quatre entiers a ≥ 0, b > 1, d ≥ 0 et n0 > 0 tels
que, pour tout n ≥ n0 , T (n) ≤ aT (d bn e) + O(nd ), alors
d
si bd > a
O(n )
d
T (n) = O(n log n) si bd = a
log a
si bd < a
O(n log b )
Remarques
Le M.T. donne un ordre de complexité sans équation de récurrence
lorsque les appels se font avec une taille d’entrée divisée.
a représente le nombre d’appels récursifs dans le corps de l’algorithme,
b l’entier par lequel la taille de l’entrée est divisée à chaque appel,
O(nd ) les opérations de l’algorithme en plus des appels récursifs.
Exemple
F ONCTION : F3 (E n, m : entier) :
entier
A partir de n=1 (n0 = 1),
D ÉBUT F3 fait 1 appel récursif (a = 1)
si n=0 alors au rang b n/2 c (b = 2).
Renvoyer 0 En dehors des appels, il effectue
sinon si n mod 2 = 0 alors O(1) = O(n0 ) opérations (d = 0).
Renvoyer 2*F3(n/2,m)
sinon On est dans le cas bd = a donc
Renvoyer Tn = O(n0 log(n)) = O(log(n)) :
n+2*F3((n-1)/2,m) l’algorithme est logarithmique.
fin si;
F IN
P ROCÉDURE : F (E n : entier)
S PÉCIFICATIONS : n ∈ N
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
Principe
Pour chercher un élément e dans un tableau T non trié, on le parcourt case
par case jusqu’à trouver e ou atteindre la fin du tableau.
Variable : i : entier
D ÉBUT
i←0
tant que i<taille(T) et T[i]6=e faire
i ← i+1
fin tq;
si i=taille(T) alors
Renvoyer -1
sinon
Renvoyer i
fin si;
F IN
Sylvain Daudé (UM - FDS) Algorithmique 1 89 / 118
Complexité de la recherche linéaire
D ÉBUT
i←0
tant que i<n et T[i]6=e faire
i ← i+1
fin tq;
si i=taille(T) alors
Renvoyer -1
sinon
Renvoyer i
fin si;
F IN
Principe
Pour chercher un élément e dans un tableau T trié, on compare e à la valeur
au milieu du tableau puis on continue à le chercher dans la bonne moitié du
tableau.
D ÉBUT
deb, fin ← 0, taille(T)-1
tant que deb≤fin faire
mil ← (deb+fin) div 2
si T[mil]<e alors
deb ← mil+1
sinon
fin ← mil-1
fin si;
fin tq;
Renvoyer cond(deb<taille(T) et T[deb]=e, deb, -1)
F IN
Sylvain Daudé (UM - FDS) Algorithmique 1 92 / 118
A vous de jouer !
D ÉBUT
deb, fin ← 0, taille(T)-1
tant que deb≤fin faire
mil ← (deb+fin) div 2
si T[mil]<e alors i debi mili fini T [debi ..fini ]
deb ← mil+1 0 0 ? 8 [1,2,3,3,3,4,6,9,10]
sinon 1 0 4 3 [1,2,3,3 ]
fin ← mil-1 2 2 1 3 [ 3,3 ]
fin si; 3 2 2 1
fin tq;
Renvoyer L’algorithme renvoie deb=2.
cond(deb<taille(T) et
T[deb]=e, deb, -1)
F IN
D ÉBUT
deb, fin ← 0, taille(T)-1
tant que deb≤fin faire
mil ← (deb+fin) div 2
i debi mili fini T [debi ..fini ]
si T[mil]<e alors
deb ← mil+1 0 0 ? 8 [1,2,3,3,3,4,6,9,10]
sinon 1 5 4 8 [ 4,6,9,10]
fin ← mil-1 2 5 6 5 [ 4 ]
fin si; 3 6 5 5
fin tq; deb = 6 et T [deb] 6= 5 :
Renvoyer l’algorithme renvoie -1.
cond(deb<taille(T) et
T[deb]=e, deb, -1)
F IN
D ÉBUT
deb, fin ← 0, taille(T)-1
tant que deb≤fin faire
mil ← (deb+fin) div 2 fin-deb+1 est un entier naturel ;
si T[mil]<e alors
deb ← mil+1 il décroît strictement à chaque itération ;
sinon le tant que s’arrête lorsqu’il vaut 0 ou
fin ← mil-1 moins.
fin si; Il y a donc un nombre fini d’itérations.
fin tq; Chaque itération se termine.
Renvoyer Donc l’algorithme se termine.
cond(deb<taille(T) et
T[deb]=e, deb, -1)
F IN
1 Généralités
2 Structures linéaires
7 Algorithmes de tri
Motivation
Trier permet de traiter plus efficacement ensuite.
On étudie 2 sortes de tris de tableaux 1D :
par valeurs : les données à trier ne peuvent prendre que certaines
valeurs : tri en Ω(n)
par comparaison : les données sont quelconques : tri en Ω(n log n)
Notation
n : taille du tableau à trier
i j k Cpt T
D ÉBUT
Initialisation :
Initialiser Cpt avec des 0
pour i de 0 à n-1 faire ? ? ? [0,0,0,0] [1,0,3,2,2,2]
Cpt[T[i]] ← Cpt[T[i]] + 1 Boucle pour :
fin pour; 0 ? ? [0,1,0,0] [1,0,3,2,2,2]
j,k ← 0,0 1 ? ? [1,1,0,0] [1,0,3,2,2,2]
tant que j<n faire 2 ? ? [1,1,0,1] [1,0,3,2,2,2]
si Cpt[k]=0 alors Après la boucle pour :
k ← k+1 ? 0 0 [1,1,3,1] [1,0,3,2,2,2]
sinon Boucle tant que :
T[j] ← k ; j ← j+1 ; ? 1 0 [0,1,3,1] [0,0,3,2,2,2]
Cpt[k] ← Cpt[k]-1 ? 1 1 [0,1,3,1] [0,0,3,2,2,2]
fin si; ? 2 1 [0,0,3,1] [0,1,3,2,2,2]
fin tq; A la fin de l’algorithme :
F IN ? 6 3 [0,0,0,0] [0,1,2,2,2,3]
Dans le cas général, on doit comparer les valeurs entre elles pour les
ordonner.
Nombre de comparaisons nécessaires : au moins n log n/4 (preuve dans
le cours) donc complexité Ω(n log n).
Il est possible de faire θ(n log n), par exemple avec le tri fusion.
L’ordre de complexité n log n est donc une borne inférieure d’un tri de
tableau par comparaison.
D ÉBUT
pour i de 0 à n-2 faire
pour j de n-1 à i+1 par pas de -1 faire
si T[j]<T[j-1] alors
T[j],T[j-1] ← T[j-1],T[j]
fin si;
fin pour;
fin pour;
F IN
i j T
P ROCÉDURE : TriBulles (ES
? ? [5,6,2,4,3,1]
T : tableau de nombres de taille n)
0 5 [5,6,2,4,1,3]
0 4 [5,6,2,1,4,3]
S PÉCIFICATIONS : Trie le tableau
0 3 [5,6,1,2,4,3]
T par ordre croissant.
0 2 [5,1,6,2,4,3]
0 1 [1,5,6,2,4,3]
D ÉBUT
Le plus petit est bien placé.
pour i de 0 à n-2 faire
pour j de n-1 à i+1 1 5 [1,5,6,2,3,4]
par pas de -1 faire 1 4 [1,5,6,2,3,4]
si T[j]<T[j-1] alors 1 3 [1,5,2,6,3,4]
T[j],T[j-1] ← 1 2 [1,2,5,6,3,4]
T[j-1],T[j] Les 2 plus petits sont bien placés.
fin si; A la fin :
fin pour; 4 5 [1,2,3,4,5,6]
fin pour;
F IN
Principe
Algorithme de type "Diviser pour mieux régner" :
On trie chaque moitié du tableau
On fusionne les deux moitiés triées.
Il faut 2 algorithmes : l’algorithme de tri et l’algorithme de fusion
D ÉBUT
recopier T[deb1..fin1] dans T1 et T[fin1+1..fin2] dans T2
i,j ← 0,0
pour k de deb1 à fin2 faire
si j>=taille(T2) ou (i<taille(T1) et T1[i]<=T2[j])
alors
T[k] ← T1[i] ; i ← i+1
sinon
T[k] ← T2[j] ; j ← j+1
fin si;
fin pour;
F IN
Sylvain Daudé (UM - FDS) Algorithmique 1 110 / 118
A vous de jouer ! Trace de fusion([2,6,8,4,9], 0, 2, 4)
k i j T1 T2 T
P ROCÉDURE : fusion (ES T:
? 0 0 [2,6,8] [4,9] [?,?,?,?,?]
t. de nb, E deb1,fin1,fin2: entier)
0 1 0 [2,6,8] [4,9] [2,?,?,?,?]
1 1 1 [2,6,8] [4,9] [2,4,?,?,?]
Variable : i,j : entiers ; T1,T2 :
2 2 1 [2,6,8] [4,9] [2,4,6,?,?]
t. de fin1-deb1+1 et fin2-fin1 nb
3 3 1 [2,6,8] [4,9] [2,4,6,8,?]
4 3 2 [2,6,8] [4,9] [2,4,6,8,9]
D ÉBUT
recopier T[deb1..fin1] dans T1
recopier T[fin1+1..fin2] dans T2
i,j 0,0
pour k de deb1 à fin2 faire
si j>=taille(T2) ou
(i<taille(T1) et
T1[i]<=T2[j]) alors
T[k] T1[i] ; i i+1
sinon
T[k] T2[j] ; j j+1
fin si;
fin pour;
F IN
Sylvain Daudé (UM - FDS) Algorithmique 1 111 / 118
Algorithme de tri fusion
D ÉBUT
si deb<fin alors
mil ← (deb+fin) div 2
triFusionAux(T,deb,mil)
triFusionAux(T,mil+1,fin)
fusion(T,deb,mil,fin)
fin si;
F IN
P ROCÉDURE : triFusionAux
[5,6,2,4,3,1]
(ES T : tableau de nombre; E deb, fin :
entier) : tableau de nombre
[5,6,2] [4,3,1]
si deb<fin alors
mil (deb+fin) div 2
[5,6] [3,4]
triFusionAux(T,deb,mil)
triFusionAux(T,mil+1,fin) [2,5,6] [1,3,4]
fusion(T,deb,mil,fin)
fin si;
F IN [1,2,3,4,5,6]
Quelles valeurs de deb et fin faut-il choisir pour trier tout le tableau ?
deb=0, fin=taille(T)-1
S PÉCIFICATIONS : Trie T
D ÉBUT
TriFusionAux(T,0,taille(T)-1)
F IN
Tri par insertion : pour k allant de 1 à taille(T)-1, T[k] est inséré parmi
T[0] à T[k-1]. Adapté lorsque les données sont presque triées.
Complexité : θ(n2 ).
Tri par sélection : pour k allant de 0 à taille(T)-2, on cherche le plus petit
élément parmi T[k] à T[taille(T)-1] et on l’échange avec T[k]. Adapté pour
de petits ensembles de données.
Complexité : θ(n2 ).
Tri par tas : On structure les données dans un arbre binaire dont les
nœuds sont partiellement ordonnés ("tas"). Le sommet de l’arbre est le
plus grand élément, on l’extrait et on refait un tas à partir des nœuds
restants.
Complexité : θ(n log n).
Tri rapide ou tri par pivot : on sépare un tableau entre les éléments
plus petits et plus grand qu’un certain élément du tableau ("pivot") et on
trie chaque moitié récursivement.
Complexité : θ(n2 ) dans le pire des cas, θ(n log n) en moyenne.
Insertion : [1, 3, 5, 2, 9, 4, 0]
[1, 3, 5, 2, 9, 4, 0]
"pour k allant de 1 à taille(T)-1, [1, 3, 5, 2, 9, 4, 0]
T[k] est inséré parmi T[0] à T[k-1]." [1, 2, 3, 5, 9, 4, 0]
[1, 2, 3, 5, 9, 4, 0]
[1, 2, 3, 4, 5, 9, 0]
[0, 1, 2, 3, 4, 5, 9]
Un tri est stable s’il laisse les éléments égaux dans le même ordre.
utile pour trier successivement selon plusieurs critères ;
exemples : bulles, insertion, fusion ;
sinon, possibilité de mémoriser l’emplacement initial des éléments.
Un tri est en place s’il ne nécessite pas d’espace supplémentaire
important et modifie directement la structure à trier.
important si on dispose de peu de mémoire ;
exemples : bulles, sélection, insertion, tas.