Algorithmique Avancé Et Complexité - Procédures Récursives

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 23

Procédures récursives

Récursivité terminale vs. Non terminale


• Une fonction récursive est dite récursive terminale si
tous ses appels sont récursifs terminaux.

• Un appel récursif est terminal s'il s'agit de la dernière


instruction exécutée dans le corps d'une fonction et
que sa valeur de retour ne fait pas partie d'une
expression.

• Les fonctions récursives terminales sont caractérisées


par le fait qu'elles n'ont rien à faire pendant la phase
de remontée
Récursivité terminale vs. Non terminale
Procédure terminale Procédure non terminale
Procédure AfficherGaucheDroite ( Tab : Procédure AfficherDroiteGauche ( Tab :
Tableau entier, N : entier, i : entier) Tableau entier, N : entier, i : entier)
Si (i<=N) Alors Si (i<=N) Alors
Ecrire(Tab[i]) ; AfficherDroiteGauche (Tab,N,i+1) ;
AfficherGaucheDroite (Tab,N,i+1) ; Ecrire(Tab[i]) ;
Fin Si Fin Si
Fin Fin

Exemple: Tab=(1,2,3,4,5)

AfficherGaucheDroite (Tab, 5,1) Donne le résultat : 1,2,3,4,5

AfficherDroite Gauche (Tab, 5,1) Donne le résultat : 5,4,3,2,1

Donc : l’ordre des appels récursifs affecte la terminaison


d’une fonction récursive
Elimination de la récursivité
Eliminer la récursivité , c’est transformer un
algorithme récursif en un algorithme équivalent ne
contenant pas des appels récursifs.

Elimination de la récursivité terminale simple


• Un algorithme est dit récursif terminal s’il ne
contient aucun traitement après un appel
récursif.
• La récursivité terminale simple peut être
remplacée par une solution itérative.
Elimination de la récursivité terminale simple

Algorithme récursif Algorithme itératif


Procédure ALGOR(X) Procédure ALGOI(X)
Si (COND) Alors Tant que (COND) faire
TRAIT1 TRAIT1
ALGOR(β (X)) X  β (X)
Sinon Fin tant que
TRAIT2 TRAIT2
Fin Si Fin
Fin

• X est la liste des paramètres


• COND est une condition portant sur X
• TRAIT1 est le traitement de base de l'algorithme (dépendant de X)
• β(X) représente la transformation des paramètres
• TRAIT2 est le traitement de terminaison (dépendant de X)
Elimination de la récursivité terminale simple

Exepmle: PGCD (A, B)

Algorithme récursif Algorithme itératif


Fonction PGCD(a,b) : entier Fonction PGCD(a,b) : entier
Si (a =b) Alors Retourner(a) Si (a =b) Alors Retourner(a)
Sinon Sinon
Si (a>b) Retourner (PGCD (a-b,b)) Tant que (a<>b) Faire
Sinon Retourner (PGCD (a,b-a)) si (a>b alors a  a-b
Fin Si sinon b b-a
Fin Si Fin Tant Que
Fin Retourner (a)
Fin Si
Fin
Elimination de la récursivité non
terminale simple
• Dans un algorithme récursif non terminal, l’appel
récursif est suivi d’un traitement  il reste un
traitement à reprendre après l’appel récursif

• Il va falloir donc sauvegarder, sur une pile les


paramètres de l’appel engendrant l’appel récursif.

 La récursivité non terminale simple peut être


remplacée par une solution itérative utilisant une
pile.
Elimination de la récursivité non terminale
simple
Algorithme récursif Algorithme itératif
Procédure ALGOR(X) Procédure ALGOI(X)
Si (COND) Alors Pile.init()
TRAIT1 Tant que (COND) Faire
ALGOR(β (X)) TRAIT1
TRAIT2 Pile.empiler(X)
Sinon X β (X)
TRAIT3 Fin Tant que
Fin Si TRAIT3
Fin Tant que (Non_vide_pile()) Faire
Pile.dépiler(U)
TRAIT2
Fin Tant que
Fin
Elimination de la récursivité non terminale
simple
Algorithme récursif Algorithme itératif
Procédure AfficherDroiteGauche ( Tab : Procédure ALGOI(X)
Tableau entier, N : entier, i : entier) Pile.init()
Si (i<=N) Alors Tant que (i<=N) Faire
AfficherDroiteGauche (Tab,N,i+1) ; /* TRAIT1 */
Ecrire(Tab[i]) ; Pile.empiler(Tab[i])
Fin Si i  i+1
Fin Fin Tant que
/* TRAIT3 */
Tant que (Non_vide_pile()) Faire
TRAIT1 = Ø Ecrire(Pile.dépiler() )
TRAIT2 = Ecrire(Tab[i]) /* TRAIT2 */
TRAIT3 = Ø Fin Tant que
Fin
Paradigme « diviser pour régner »
• Principe
• Plusieurs algorithmes ont une structure récursive : pour résoudre un
problème donné, ils s’appellent eux-mêmes récursivement une ou
plusieurs fois sur des problèmes très similaires, mais de tailles moindres,
résolvent les sous problèmes de manière récursive puis combinent les
résultats pour trouver une solution au problème initial.

• Le paradigme « diviser pour régner » donne lieu à trois étapes à chaque


niveau de récursivité :
• Diviser : le problème en un certain nombre de sous-problèmes ;
• Régner : sur les sous-problèmes en les résolvant récursivement ou, si la
taille d’un sous-problème est assez réduite, le résoudre directement ;
• Combiner : les solutions des sous-problèmes en une solution complète du
problème initial.
Paradigme « diviser pour régner »
Exemple : multiplication de matrices carrées
Dans cet exemple, on se propose de multiplier 2 matrices carrées A et B de
taille n * n chacune, où n est une puissance exacte de 2. C est la matrice
résultante.
Procédure Multiplication(A, B : matrices, n: entier)
Pour i  1 à n faire
Pour j  1 à n faire
cij  0
Pour k  1 à n faire
cij  cij +aik * bkj
renvoyer C
Fin
Cet algorithme effectue O(n3) multiplications et autant d’additions.
Paradigme « diviser pour régner »
Exemple : multiplication de matrices carrées
Algorithme « diviser pour régner »

• La décomposition des matrices A, B et C en sous-matrices de


taille n/2 * n/2, permet de réécrire l'équation C = AB comme
suit:

• Le développement de cette équation donne :


r = ae+bf ; s = ag+bh; t = ce+df et u = cg+dh
Paradigme « diviser pour régner »
• Chacune de ces quatre opérations correspond à :
- deux multiplications de matrices carrées de taille n/2 2T(n/2)
- et une addition de telles matrices  n2/4

• A partir de ces équations on peut dériver un algorithme «diviser


pour régner » dont la complexité est donnée par la récurrence :

T(n) = 8T(n/2)+O(n2)
Analyse des algorithmes
« diviser pour régner »
Lorsqu’un algorithme contient un appel récursif
à lui-même, son temps d’exécution peut souvent
être décrit par une équation de récurrence qui
décrit le temps d’exécution global pour un
problème de taille n en fonction du temps
d’exécution pour des entrées de taille moindre.
Analyse des algorithmes
« diviser pour régner »
• Cette récurrence se décompose suivant les trois étapes du
paradigme de base :
- Si la taille du problème est suffisamment réduite, n ≤ c pour une
certaine constante c, la résolution est directe et consomme un
temps constant O(1).
- Sinon, on divise le problème en a sous-problèmes chacun de
taille 1/b de la taille du problème initial. Le temps d'exécution
total se décompose alors en trois parties :
- D(n) : le temps nécessaire à la division du problème en
sous-problèmes.
- aT (n/b) : le temps de résolution des a sous-problèmes.
- C(n) : le temps nécessaire pour construire la solution finale
à partir des solutions aux sous-problèmes.
Analyse des algorithmes
« diviser pour régner »
La relation de récurrence prend alors la forme :

Soit la fonction f (n) la fonction qui regroupe D(n) et C(n).


La fonction T(n) est alors définie :
• T(n) = a.T(n / b) + f (n)
• T (n) = a.T (n / b) + c.nk
Analyse des algorithmes
« diviser pour régner »
Théorème de résolution de la récurrence :
si a > bk  T(n) = O(nlogb a )
si a = bk  T(n) = O(nk logbn)
si a < bk  T(n) = O( f (n)) = O(nk )

• Résolution de la relation de récurrence pour l’exemple de la


multiplication de matrices :
T(n) = 8 T(n/2) + O(n2)
a = 8 , b = 2, k = 2  a > bk
Logba = 3
T(n) = O(n3)
Analyse des algorithmes
« diviser pour régner »
• Application : algorithme de recherche dichotomique
Fonction RechDicho(Tab :Tableau, borneinf :entier, bornesup :entier, elem :entier) : bool
Si (borneinf<=bornesup) alors
mil (borneinf+bornesup) DIV 2 ;
Si (Tab[mil]=elem) Alors
retourner (vrai)
Sinon
Si (Tab[mil]>elem) Alors
Retourner (RechDicho(Tab, borneinf, mil-1, elem))
Sinon
Retourner(RechDicho(Tab, mil+1, bornesup, elem))
Fin Si
Fin Si
Sinon
Retourner (Faux)
FinSi
Analyse des algorithmes
« diviser pour régner »
Application : algorithme de recherche dichotomique
Analyse de la complexité :
T(n) = T(n/2) + cte
a = 1 , b = 2, k = 0  a = bk
a = bk  T(n) = O(nk logbn)
T(n) = O(log2n)
Analyse des algorithmes
« diviser pour régner »
Exemple 1: T(n)=9T(n/3)+n
a=9,b=3,k=1
a > bk
Logba = 2 T(n) = O(n²)

Exemple 2 : T(n)=T(2n/3)+1
• a = 1 , b = 3/2 , k = 0
• a = bk
• T(n) = O(nklog n) = O(log n)
Autres résolutions de récurrence
Equations de récurrence linéaires:
T(n)=aT(n-1)+ f(n)

Exemple : Tours de Hanoi


T(n) = 2 T(n-1) + 1 
Autres résolutions de récurrence
Equations de récurrence linéaires sans second membre (f(n) = cte)

A une telle équation, on peut associer un polynôme:

La résolution de ce polynôme nous donne m racines ri ( avec m<=k).


La solution de l’équation de récurrence est ainsi donnée par :

 Cette solution est en général exponentielle


Autres résolutions de récurrence
Exemple : La suite de Fibonacci

T(n) = T(n-1) + T(n-2)


On pose P(x) = X² - X – 1

Vous aimerez peut-être aussi