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 xn
donnes : x : rel , n: entier Mthode 1 : x0 = 1; xi = x* xi-1 i >0 Mthode 2 : x0 = 1; xi = xi/2 * xi/2 , si i est pair; xi = x*xi/2 * xi/2 si i est impair ... rsultats : y = xn

T=n

T = log n

Laquelle choisir? et pourquoi?


Plutt la deuxime.

=>Analyse de la complexit des algorithmes

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 n0 =1 c = 2/3) n2+ n-1 = O(n2) (prendre n0 = 1, c =1) : 1/2 n2 -3n = (n2) trouver n0 c1, c2 t.q.

c1 n2 1/2 n2 -3n c2 n2
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 n2 -3n = (n2)

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 :
Complexit

algorithme de tri

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)=ax2 +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 devrat-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(n2). en moyenne : Q(n2).

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 :
Classes de complexit

algorithme de tri

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(nk) 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. Revenons la fonction puissance x xn. Cette fonction peut tre dfinie rcursivement :

Rcursivit simple

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 Cnp 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. Supposons quil existe un tel programme, nomm termine, de vrification de la terminaison. partir de ce programme on conoit le programme Q suivant :

Dmonstration de la non dcidabilit

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(n3) 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(n2).

La rcursivit et le paradigme diviser pour rgner


Analyse des algorithmes diviser pour rgner

Lorsquun algorithme contient un appel rcursif luimme, 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 sousproblmes. (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 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.

Principe

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 logba =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(n2).

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

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)

Appel Q(U0)

Exemple dexcution de lalgorithme drcursiv.


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.