Vous êtes sur la page 1sur 43

Algorithmique Avance II

Lakhdar Sas

CRIL, Universit d Artois
Bureau: C301
sais@cril.univ-artois.fr
http://www.cril.univ-artois.fr/~sais
Plan du cours
Rappels
Tables de hachage
Drcursivation
Arbres binaires
Arbres quilibrs
B-Arbres
graphes
...
Bibliographie :
Introduction to Algorithms par Thomas H. Cormen, Charles
E. Leiserson, and Ronald L. Rivest
Types de donnes et algorithmes par Christine Froidevaux,
Marie-Claude Gaudel, Michle Soria
Utiliser l internet!
Rappels : algorithmique I
Quest-ce que lalgorithmique ?
Dfinition 1 (Algorithme). Un algorithme est suite finie
doprations lmentaires constituant un schma de calcul
ou de rsolution dun problme.
Double problmatique de lalgorithmique ?
1. Trouver une mthode de rsolution (exacte ou
approche) du problme.
2. Trouver une mthode efficace.

=>Savoir rsoudre un problme est une chose, le rsoudre
efficacement en est une autre, ou encore montrer qu il est
correcte !!
Rappels : algorithmique I
Exemple 1:
problme : calculer x
n

donnes : x : rel , n: entier
Mthode 1 : x
0
= 1; x
i
= x* x
i-1
i >0
Mthode 2 : x
0
= 1;
x
i
= x
i/2
* x
i/2
, si i est pair;
x
i
= x*x
i/2
* x
i/2
si i est impair
...
rsultats : y = x
n

Laquelle choisir? et pourquoi?
Plutt la deuxime.
=>Analyse de la complexit des algorithmes

T = log n
T = n
Analyse de la complexit :
Notation de Landau:
On ne s intresse pas en gnral la complexit
exacte, mais son ordre de grandeur.
=> besoin de notations asymptotiques.

Analyse de la complexit :
Exemples :
0 : n=O(n), (prendre n0 =1, c=1)
2n = O(3n) (prendre n
0
=1 c = 2/3)
n
2
+ n-1 = O(n
2
) (prendre n
0
= 1, c =1)
: 1/2 n
2
-3n = (n
2
)
trouver n0 c1, c2 t.q.
c1 n
2
1/2 n
2
-3n c2 n
2
c1 1/2 -3/n c2
la partie droite de l inquation peut tre satisfaite pour n0
= 1 et c2 = 1/2
la partie gauche de l inquation peut tre satisfaite pour
n0 = 7 et c1 = 1/14
=> en choisissant n0 =7 , c1 =1/14, c2 = 1/2
=> 1/2 n
2
-3n = (n
2
)
.
Analyse de la complexit :
Complexit
Dfinition 2 (Complexit). La complexit dun algorithme
est la mesure du nombre doprations fondamentales quil
effectue sur un jeu de donnes. La complexit est exprime
comme une fonction de la taille du jeu de donnes.
Nous notons Dn lensemble des donnes de taille n et C(d)
le cot de lalgorithme sur la donne d.
Complexit au meilleur : Tmin(n) = min dDn C(d).
Cest le plus petit nombre doprations quaura excuter
lalgorithme sur un jeu de donnes de taille fixe, ici n.
Cest une borne infrieure de la complexit de lalgorithme
sur un jeu de donnes de taille n.

Analyse de la complexit :
Complexit au pire : Tmax(n) = max dDn C(d). Cest le plus
grand nombre doprations quaura excuter lalgorithme sur
un jeu de donnes de taille fixe, ici n.
Avantage : il sagit dun maximum, et lalgorithme finira donc toujours
avant davoir effectu Tmax(n) oprations.
Inconvnient : cette complexit peut ne pas reflter le comportement
usuel de lalgorithme, le pire cas pouvant ne se produire que trs
rarement, mais il nest pas rare que le cas moyen soit aussi mauvais que
le pire cas.
Analyse de la complexit :
Complexit en moyenne : Tmoy(n) = dDn C(d) / |Dn|
Cest la moyenne des complexits de lalgorithme sur des jeux
de donnes de taille n (en toute rigueur, il faut bien
videmment tenir compte de la probabilit dapparition de
chacun des jeux de donnes).
Avantage : reflte le comportement gnral de lalgorithme si les cas
extrmes sont rares ou si la complexit varie peu en fonction des
donnes.
Inconvnient : la complexit en pratique sur un jeu de donnes particulier
peut tre nettement plus importante que la complexit en moyenne, dans
ce cas la complexit en moyenne ne donnera pas une bonne indication
du comportement de lalgorithme.
En pratique, nous ne nous intresserons qu la complexit au pire et la
complexit en moyenne.
Analyse de la complexit :
Modle de machine
Pour que le rsultat de lanalyse dun algorithme soit
pertinent, il faut avoir un modle de la machine sur
laquelle lalgorithme sera implment (sous forme de
programme). On prendra comme rfrence un modle
de machine accs alatoire (RAM) et processeur
unique, o les instructions sont excutes lune aprs
lautre, sans oprations simultanes.

Analyse de la complexit :
premier algorithme de tri
Illustration : cas du tri par insertion
Problmatique du tri
donnes : une squence de n nombre a1, a2, . . . , an.
rsultats : une permutation <a 1, a 2, . . . , a n> des donnes t.q.
a 1 a 2 , . . . , a n
exemple :

Analyse de la complexit : algorithme de tri
Complexit
attribution d un cot en temps ci chaque instruction, et
compter le nombre dexcutions de chacune des instructions.
Pour chaque valeur de j [2..n], nous notons t j le nombre
dexcutions de la boucle tant que pour cette valeur de j.
Il est noter que la valeur de t j dpend des donnes






Le temps dexcution total de lalgorithme est alors :

Analyse de la complexit : algorithme de tri
Complexit au meilleur : le cas le plus favorable
pour lalgorithme TRI-INSERTION est quand le
tableau est dj tri. Dans ce cas t j = 1 pour tout j.





T(n) peut ici tre crit sous la forme T(n) = an+b, a et
b tant des constantes indpendantes des entres, et
T(n) est donc une fonction linaire de n.

Analyse de la complexit : algorithme de tri
Complexit au pire : le cas le plus dfavorable pour
lalgorithme TRI-INSERTION est quand le tableau est dj
tri dans l ordre inverse. Dans ce cas tj =j pour tout j.






Rappel : donc et
T(n) peut ici tre crit sous la forme T(n)=ax
2
+bx +c, a, b
et c tant des constantes, et T(n) est donc une fonction
quadratique.
Analyse de la complexit : algorithme de tri
Complexit en moyenne : supposons que lon applique
lalgorithme de tri par insertion n nombres choisis au
hasard. Quelle sera la valeur de t j ? Cest--dire, o devra-
t-on insrer A[ j] dans le sous-tableau A[1.. j-1]? En
moyenne tj = j/2.
Si l on reporte cette valeur dans l quation on obtient
galement une fonction quadratique.
meilleur cas : Q(n).
pire cas : Q(n
2
).
en moyenne : Q(n
2
).
En gnral, on considre quun algorithme est plus efficace
quun autre si sa complexit dans le pire cas a un ordre de
grandeur infrieur.
Analyse de la complexit : algorithme de tri
Classes de complexit
Les algorithmes usuels peuvent tre classs en un certain
nombre de grandes classes de complexit :
Les algorithmes sub-linaires dont la complexit est en
gnral en O(logn).
Les algorithmes linaires en complexit O(n) et ceux en
complexit en O(nlogn) sont considrs comme rapides.
Les algorithmes polynomiaux en O(n
k
) pour k > 3 sont
considrs comme lents, sans parler des algorithmes
exponentiels (dont la complexit est suprieure tout
polynme en n) que lon saccorde dire impraticables ds
que la taille des donnes est suprieure quelques dizaines
dunits.
La rcursivit
et le paradigme diviser pour rgner
Rcursivit
De lart dcrire des programmes qui rsolvent des
problmes que lon ne sait pas rsoudre soi-mme !
Dfinition 4 (Dfinition rcursive, algorithme rcursif).
Une dfinition rcursive est une dfinition dans laquelle
intervient ce que lon veut dfinir. Un algorithme est dit
rcursif lorsquil est dfini en fonction de lui-mme.
Rcursivit simple
Revenons la fonction puissance x x
n
. Cette fonction
peut tre dfinie rcursivement :

La rcursivit
et le paradigme diviser pour rgner
Rcursivit multiple
Une dfinition rcursive peut contenir plus dun appel
rcursif. Nous voulons calculer ici les combinaisons C
n
p
en
se servant de la relation de Pascal :

Rcursivit mutuelle
Des dfinitions sont dites mutuellement rcursives si elles
dpendent les unes des autres. a peut tre le cas pour la
dfinition de la parit :


La rcursivit
et le paradigme diviser pour rgner
Rcursivit imbrique
La fonction dAckermann est dfinie comme suit :

La rcursivit
et le paradigme diviser pour rgner
Principe et dangers de la rcursivit
Principe et intrt : ce sont les mmes que ceux de la
dmonstration par rcurrence en mathmatiques. On doit
avoir :
un certain nombre de cas dont la rsolution est connue, ces cas
simples formeront les cas darrt de la rcursion ;
un moyen de se ramener dun cas compliqu un cas plus
simple .
La rcursivit permet dcrire des algorithmes concis et
lgants.

La rcursivit
et le paradigme diviser pour rgner
Difficults :
la dfinition peut tre dnue de sens :
Algorithme A(n)
renvoyer A(n)
il faut tre srs que lon retombera toujours sur un cas
connu, cest--dire sur un cas darrt ; il nous faut nous
assurer que la fonction est compltement dfinie, cest--
dire, quelle est dfinie sur tout son domaine dapplications.

La rcursivit
et le paradigme diviser pour rgner
Non dcidabilit de la terminaison
Question : peut-on crire un programme qui vrifie
automatiquement si un programme donn P termine
quand il est excut sur un jeu de donnes D?
Entre: Un programme P et un jeu de donnes D.
Sortie: vrai si le programme P termine sur le jeu de
donnes D, et faux sinon.
Dmonstration de la non dcidabilit
Supposons quil existe un tel programme, nomm termine,
de vrification de la terminaison. partir de ce programme
on conoit le programme Q suivant :
La rcursivit
et le paradigme diviser pour rgner
programme Q
rsultat = termine(Q)
tant que rsultat = vrai faire attendre une seconde fin tant que
renvoyer rsultat
Supposons que le programme Q qui ne prend pas
darguments termine. Donc termine(Q) renvoie vrai, la
deuxime instruction de Q boucle indfiniment et Q ne
termine pas. Il y a donc contradiction et le programme Q ne
termine pas. Donc, termine(Q) renvoie faux, la deuxime
instruction de Q ne boucle pas, et le programme Q termine
normalement. Il y a une nouvelle fois contradiction : par
consquent, il nexiste pas de programme tel que termine.
Le problme de la terminaison est indcidable!!
La rcursivit
et le paradigme diviser pour rgner
Diviser pour rgner
Principe
Nombres dalgorithmes ont une structure rcursive : pour rsoudre
un problme donn, ils sappellent eux-mmes rcursivement une ou
plusieurs fois sur des problmes trs similaires, mais de tailles
moindres, rsolvent les sous problmes de manire rcursive puis
combinent les rsultats pour trouver une solution au problme
initial.
Le paradigme diviser pour rgner donne lieu trois tapes
chaque niveau de rcursivit :
Diviser : le problme en un certain nombre de sous-problmes ;
Rgner : sur les sous-problmes en les rsolvant rcursivement ou,
si la taille dun sous-problme est assez rduite, le rsoudre
directement ;
Combiner : les solutions des sous-problmes en une solution
complte du problme initial.
La rcursivit
et le paradigme diviser pour rgner
Premier exemple : multiplication nave de matrices
Nous nous intressons ici la multiplication de matrices
carrs de taille n.
Algorithme naf
MULTIPLIER-MATRICES(A, B)
Soit n la taille des matrices carrs A et B
Soit C une matrice carr de taille n
Pour i 1 n faire
Pour j 1 n faire
cij 0
Pour k 1 n faire
cij cij +aik * bkj
renvoyer C
Cet algorithme effectue Q(n
3
) multiplications et autant
dadditions.
La rcursivit
et le paradigme diviser pour rgner
Algorithme diviser pour rgner naf
Dans la suite nous supposerons que n est une puissance exacte de 2.
Dcomposons les matrices A, B et C en sous-matrices de taille n/2
*n/2. Lquation C = A *B peut alors se rcrire :


En dveloppant cette quation, nous obtenons :

Chacune de ces quatre oprations correspond deux
multiplications de matrices carrs de taille n/2 et une addition de
telles matrices. partir de ces quations on peut aisment driver
un algorithme diviser pour rgner dont la complexit est donne
par la rcurrence :
laddition des matrices carrs de taille n/2 tant en Q(n
2
).
La rcursivit
et le paradigme diviser pour rgner
Analyse des algorithmes diviser pour rgner
Lorsquun algorithme contient un appel rcursif lui-
mme, son temps dexcution peut souvent tre dcrit par
une quation de rcurrence qui dcrit le temps dexcution
global pour un problme de taille n en fonction du temps
dexcution pour des entres de taille moindre.
La rcursivit
et le paradigme diviser pour rgner
La rcurrence dfinissant le temps dexcution dun
algorithme diviser pour rgner se dcompose suivant les
trois tapes du paradigme de base :
1. Si la taille du problme est suffisamment rduite, n c pour une
certaine constante c, la rsolution est directe et consomme un temps
constant Q(1).
2. Sinon, on divise le problme en a sous-problmes chacun de taille
1/b de la taille du problme initial. Le temps dexcution total se
dcompose alors en trois parties :
(a) D(n) : le temps ncessaire la division du problme en sous-
problmes.
(b) aT(n/b) : le temps de rsolution des a sous-problmes.
(c) C(n) : le temps ncessaire pour construire la solution finale
partir des solutions aux sous-problmes.


La rcursivit
et le paradigme diviser pour rgner
La relation de rcurrence prend alors la forme :


o lon interprte n/b soit comme , soit comme



La rcursivit
et le paradigme diviser pour rgner
Rsolution des rcurrences :



Pour une dmonstration de ce thorme voir Rivest-Carmen-etal
Il existe d autres mthodes de rsolution des rcurrences : par substitution,
changement de variables etc.
La rcursivit
et le paradigme diviser pour rgner
Algorithmes de tri
Tri par fusion
Principe
Lalgorithme de tri par fusion est construit suivant le
paradigme diviser pour rgner :
1. Il divise la squence de n nombres trier en deux sous-squences de
taille n/2.
2. Il trie rcursivement les deux sous-squences.
3. Il fusionne les deux sous-squences tries pour produire la squence
complte trie.
La rcursion termine quand la sous-squence trier est de
longueur 1 car une telle squence est toujours trie.
La rcursivit
et le paradigme diviser pour rgner



Complexit
Pour dterminer la formule de rcurrence qui nous donnera la
complexit de lalgorithme TRI-FUSION, nous tudions les
trois phases de cet algorithme diviser pour rgner :
Diviser : cette tape se rduit au calcul du milieu de lintervalle [p..r]
Rgner : lalgorithme rsout rcursivement deux sous-problmes de
tailles respectives n/2
Combiner : la complexit de cette tape est celle de lalgorithme de
fusion qui est de Q(n) pour la construction dun tableau solution de
taille n.

La rcursivit
et le paradigme diviser pour rgner
Par consquent, la complexit du tri par fusion est
donne par la rcurrence :

Pour dterminer la complexit du tri par fusion, nous
utilisons de nouveau le thorme.
Ici a=2 et b=2 donc log
b
a =1, et nous nous trouvons dans le
deuxime cas du thorme
par consquent :

Pour des valeurs de n suffisamment grandes, le tri par fusion
avec son temps dexcution en Q(nlogn) est nettement plus
efficace que le tri par insertion dont le temps dexcution est
en Q(n
2
).
Drcursivation
Drcursiver, cest transformer un algorithme
rcursif en un algorithme quivalent ne contenant
pas dappels rcursifs.
Rcursivit terminale
Dfinition Un algorithme est dit rcursif terminal
sil ne contient aucun traitement aprs un appel
rcursif.
Rcursivit terminale
Exemple :
ALGORITHME P(U)
si C(U)
alors
D(U);P(a(U))
sinon T(U)
o :
U est la liste des paramtres ;
C(U) est une condition portant sur U ;
D(U) est le traitement de base de lalgorithme
(dpendant de U) ;
a(U) reprsente la transformation des paramtres ;
T(U) est le traitement de terminaison (dpendant de U).
Rcursivit terminale
Avec ces notations, lalgorithme P quivaut
lalgorithme suivant :
ALGORITHME P(U)
tant que C(U) faire
D(U);
U a(U)
fintantque
T(U)

Lalgorithme P est une version drcursive de
lalgorithme P.


Rcursivit non terminale
Ici, pour pouvoir drcursiver, il va falloir
sauvegarder le contexte de lappel rcursif,
typiquement les paramtres de lappel engendrant
lappel rcursif.
Originellement, lalgorithme est :
ALGORITHME Q(U)
si C(U) alors
D(U);Q(a(U));F(U)
sinon T(U)

Rcursivit non terminale
Utilisation de piles
Aprs drcursivation on obtiendra donc :
ALGORITHME Q(U)
empiler(nouvel_appel, U)
tant que pile non vide faire
dpiler(tat, V)
si tat = nouvel_appel alors U V
si C(U) alors D(U)
empiler(fin, U)
empiler(nouvel_appel, a(U))
sinon T(U)
si tat = fin alors U V
F(U)
Illustration de la drcursivation de
lalgorithme Q
Exemple dexcution de Q :
Appel Q(U0)
C(U0) vrai
D(U0)
Appel Q(a(U0))
C(a(U0)) vrai
D(a(U0))
Appel Q(a(a(U0)))
C(a(a(U0))) faux
T(a(a(U0)))
F(a(U0))
F(U0)
Exemple dexcution de lalgorithme
drcursiv.
Appel Q(U0)
empiler(nouvel_appel, U))
pile = [(nouvel_appel, U0)]
dpiler(tat, V))
tat nouvel_appel ; V U0 ; pile = []
U U0
C(U0) vrai
D(U0)
empiler(fin, U))
pile = [(fin, U0)]
empiler(nouvel_appel, a(U)))
pile = [(fin, U0) ; (nouvel_appel, a(U0))]
Exemple dexcution de lalgorithme
drcursiv.
dpiler(tat, V))
tat nouvel_appel ; V a(U0) ; pile = [(fin, U0)]
U a(U0)
C(a(U0)) vrai
D(a(U0))
empiler(fin, U))
pile = [(fin, U0) ; (fin, a(U0))]
empiler(nouvel_appel, a(U)))
pile = [(fin, U0) ; (fin, a(U0)) ; (nouvel_appel, a(a(U0)))]
Exemple dexcution de lalgorithme
drcursiv.
dpiler(tat, V))
tat nouvel_appel ; V a(a(U0)) ; pile = [(fin, U0) ; (fin, a(U0))]
U a(a(U0))
C(a(a(U0))) faux
T(a(a(U0)))
dpiler(tat, V))
tat fin ; V a(U0) ; pile = [(fin, U0)]
F(a(U0))
dpiler(tat, V))
tat fin ; V U0 ; pile = []
F(U0)
Drcursivation
Remarques
Les programmes itratifs sont souvent plus efficaces,
mais les programmes rcursifs sont plus faciles crire.
Les compilateurs savent, la plupart du temps,
reconnatre les appels rcursifs terminaux, et ceux-ci
nengendrent pas de surcot par rapport la version
itrative du mme programme.
Il est toujours possible de drcursiver un algorithme
rcursif.