Sunteți pe pagina 1din 28

IV.

Apelul recursiv al funciilor

1. Conceptul de recursivitate 2. Recursivitatea direct 3. nregistrarea de activare 4. Relaia dintre recursivitate i iteraie 5. Exemple de programe recursive

Conceptul de recursivitate
Definiii
Un obiect sau un fenomen este definit n mod recursiv dac n definiia sa se face referire la el nsui. Conceptul de recursivitate ofer posibilitatea definirii unei infiniti de obiecte printr-un numr finit de relaii. Recursivitatea a fost introdus n programare n 1960, n limbajul Algol. O funcie este recursiv atunci cnd executarea ei implic cel puin nc un apel ctre ea nsi. Recursivitate direct apelul recursiv se face chiar din funcia invocat. Recursivitate indirect (mutual) apelul recursiv se realizeaz prin intermediul mai multor funcii care se apeleaz circular.

Conceptul de recursivitate
Exemple de funcii recursive
1 fact(n) = n * fact(n-1) fib: N N (Fibonacci) 1 fib(n) = fib(n-2)+fib(n-1) ac: N x N N ac(m, n) = (Ackermann) n+1 ac(m-1, 1) ac(m-1, ac(m, n-1)) dac m = 0, dac n = 0, dac m 0 i n 0. dac n > 0 dac n = 0 sau n = 1 dac n > 1 dac n = 0

Recursivitatea direct
Principii
n limbajul C funciile se pot apela pe ele nsele, adic sunt direct recursive. Pentru o funcionare corect (din punct de vedere logic), apelul recursiv trebuie s fie condiionat de o decizie care, la un moment dat n cursul execuiei, s mpiedice continuarea apelurilor recursive i s permit astfel revenirea din irul de apeluri. Lipsa acestei condiii sau programarea ei greit va conduce la executarea unui ir de apeluri a crui terminare nu mai este controlat prin program i care, la epuizarea resurselor sistemului, va provoca o eroare de execuie : Depirea stivei de date.

Recursivitatea direct
Exemplu schematic
void p (list de parametri){ ... a, b; ... if (cond) p(...) ... sau: p(...); while (cond) { ...p(...) } sau: do ... p(...)... while (cond) } Condiia care trebuie testat este specific problemei de rezolvat. Programatorul trebuie s o identifice n fiecare situaie concret i, pe baza ei, s redacteze corect apelul recursiv. Revenirea din apeluri se face n ordine invers.

nregistrarea de activare
nregistrare de activare. Aceasta conine, printre altele, locaiile rezervate pentru valorile parametrilor (argumentelor), pentru variabilele automatice i pentru rezultatul returnat de funcie. La fiecare apel se creaz o nou nregistrare de activare, incluznd un nou set de parametri i de variabile automatice. Zona de memorie n care se face aceast rezervare se numete stiva de date. n cazul apelurilor recursive vor exista n memorie, simultan, mai multe nregistrri de activare pentru aceeai funcie recursiv, fiecare coninnd cte un set de parametri i de variabile automatice (locale). Dei locaiile poart acelai nume n fiecare set, ele reprezint entiti diferite i au valori distincte. Pe parcursul unui apel nu sunt accesibile dect locaiile din nregistrarea de activare creat la apelul curent.
Unei funcii i se "acord", la apel, o zon de memorie numit

Stiva de date a programului pe parcursul unui apel recursiv


variabile locale a b variabile locale a b variabile locale a b

apel 3 p

nreg. de activ 3 nreg. de activ 2 nreg. de activ 1

apel 2 p

apel 1 p
(din fct. principal)

Exemplu de program recursiv


Enunul problemei
Se cere s se scrie un program care s citeasc un cuvnt de pe mediul de intrare i s-l afieze att normal ct i n ordinea invers a literelor. Cuvntul va fi urmat de spaiu. Citirea se va efectua caracter cu caracter, ntr-o singur variabil de tip char. Model de execuie : Linia de intrare : Linia de ieire :

ABC ABC

CBA

Exemplu de program recursiv


Soluia problemei
#include <stdio.h> void Invers( void ){ char ch; ch = getchar( ); printf("%c",ch); if (ch != ) Invers(); printf("%c",ch); } void main(void ){ Invers( ); }

Dinamica stivei la execuie, pentru linia de intrare dat ca exemplu

apel 4 apel 3 apel 2 apel 1 (f.p.)

ch= ch=C ch=B ch=A

Calculul recursiv al factorialului


Dinamica stivei n cazul apelului factrec(4)
double factrec (unsigned n){ if (n <= 1) return 1; else return nfactrec(n-1); }
factrec (1) factrec (2) factrec (3) factrec (4) 1 21 32 4*6

Calculul recursiv al funciei putere


1 an = a x an-1 n>0 n=0

#include <stdio.h> double putere(double a, unsigned n) { return n = = 0 ? 1 : a*putere(a, n-1); } int main(void) { printf(1.5 la puterea 5 = %f \n, putere(1.5, 5)); return 0; }

Relaia dintre recursivitate i iteraie - Comparaie


Iteraia execuia repetat a unei secvene de instruciuni o nou iteraie se execut doar n urma evalurii unei condiii (la nceput sau sfrit) fiecare iteraie se execut pn la capt i apoi se trece, eventual, la o nou iteraie se recomand atunci cnd algoritmul de calcul este exprimat printr-o formul iterativ Recursivitatea execuia repetat a unei funcii un nou apel recursiv se execut tot n urma evalurii unei condiii (pe parcurs) funcia recursiv se apeleaz din nou, nainte de terminarea apelului precedent se recomand doar atunci cnd problema este prin definiie recursiv (recursivitatea consum resurse n exces)

Relaia dintre recursivitate i iteraie - Comparaie


Ce este mai indicat de utilizat, apelul recursiv sau calculul iterativ? Dei, aparent, cele dou variante sunt echivalente, se impune totui urmtoarea remarc general: dac algoritmului de calcul i corespunde o formul iterativ este de preferat s se foloseasc aceasta n locul apelului recursiv, ntruct viteza de prelucrarea este mai mare i necesarul de memorie mai mic. De exemplu, calculul recursiv al numerelor lui Fibonacci este complet ineficient ntruct numrul de apeluri crete exponenial odat cu n (pentru n = 5 sunt necesare 15 apeluri). n principiu, orice algoritm recursiv poate fi transformat ntr-unul iterativ. Aceast transformare se poate ns realiza mai uor sau mai greu. Sunt situaii n care, n varianta iterativ, programatorul trebuie s gestioneze, n mod explicit, stiva apelurilor, salvndu-i singur valorile variabilelor de la un ciclu la altul, ceea ce este dificil i ngreuneaz mult nelegerea algoritmului (ex. funcia Invers).

Relaia dintre recursivitate i iteraie - Comparaie


double factrec (unsigned n){ if (n <= 1) return 1; else return nfactrec(n-1); } double factiter(unsigned n) { double f=1 ; int i ; for (i=2; i<=n; i++) f*=i; return f; }

Relaia dintre recursivitate i iteraie - Comparaie


Pornind de la performanele calculatoarelor actuale i de la faptul c principala resurs a devenit timpul programatorului, recursivitatea are astzi domeniile ei bine definite, n care se aplic cu succes. n general se apreciaz c algoritmii a cror natur este recursiv trebuie formulai ca funcii recursive: prelucrarea structurilor de date definite recursiv (liste, arbori), descrierea proceselor i fenomenelor n mod intrinsec recursive. S-au elaborat de asemenea metode recursive cu mare grad de generalitate n rezolvarea problemelor de programare (exemplu: backtracking) pe care programatorii experimentai le aplic cu mult uurin, ca nite scheme, aproape n mod automat.

Exemplu
Rdcina patrat
x: a0=1, an+1 = (an+x / an) / 2 Cnd s-a atins precizia dorit ( |an+1-an|< ), x este an. #include <stdio.h> #include <math.h> double rad(double x, double an) { return x < 0 ? 0 : fabs((x/ an-an)/2)<0.0001 ? an : rad(x, (an+x/ an)/2); } int main(void) { printf(radical din 7 este %f \n, rad(7.0, 1.0)); return 0; }

Algoritmul lui Euclid de aflare a celui mai mare divizor comun a dou nr. ntregi
Formularea recursiv, n cuvinte, a algoritmului: Dac unul dintre numere este zero, c.m.m.d.c. al lor este cellalt numr. Dac nici unul dintre numere nu este zero, atunci c.m.m.d.c. nu se modific dac se nlocuiete unul dintre numere cu restul mpririi sale cu cellalt. Algoritmul poate fi implementat sub forma urmtoarei funcii recursive: unsigned cmmdc (unsigned m, unsigned n){ if (n==0) return m; else return cmmdc(n, m % n); }

Algoritmul lui Euclid


Exemplu numeric: cmmdc(18, 27)

m 18 27 18 9

n 27 18 9 0

cmmdc cmmdc (27, 18 % 27) cmmdc (18, 27 % 18) cmmdc (9, 18 % 9) cmmdc=9

Algoritmul lui Euclid


Varianta 2
cmmdc(m, n)= m m=n cmmdc(m-n, n) m>n cmmdc(m, n-m) m<n

#include <stdio.h> unsigned cmmdc(unsigned m, unsigned n) { return m==n ? m:m>n ? cmmdc(m-n,n) : cmmdc(m, n-m); } int main(void) { printf(cmmdc(18, 27) este %u\n, cmmdc(18, 27)); return 0; }

Algoritmul lui Euclid


Varianta 3
#include <stdio.h> #include <string.h> #include <stdlib.h> unsigned cmmdc1(unsigned m, unsigned n){ if (n==0) return m; else return cmmdc1(n, m%n); } unsigned cmmdc2(unsigned m, unsigned n){ return m == n? m : m>n? cmmdc2(m-n,n) : cmmdc2(m, n-m); }

Algoritmul lui Euclid


Varianta 3
int main(void){ char *varianta=(char*)malloc(100); unsigned (*p)(unsigned,unsigned); scanf("%s",varianta); if(!strcmp(varianta,"cmmdc1")) p=&cmmdc1; else p=&cmmdc2; printf("%s(18, 27) = %u\n",varianta, (*p)(18, 27)); free(varianta); return 0; }

Generarea permutrilor
Enunul problemei
Se consider o secven de n numere naturale distincte. Se cere s se genereze toate secvenele reprezentnd permutrile celor n numere. Se pornete de la primele n numere naturale ordonate cresctor ntr-un tablou ai, i=1,n. Prin apelul recursiv al funciei Permut se reduc n cascad permutri (k) la permutri (k-1), efectundu-se operaiile descrise n algoritmul urmtor (n pseudocod):

Generarea permutrilor
Algoritmul de rezolvare al problemei
** procedura Permut (K ; ) este intreg K, I dac K = 1 atunci * Tiprete soluia altfel Permut(K-1); pentru I := 1 la K-1 execut * schimb ntre ele A[I] cu A[K] * Permut (K-1); * schimb ntre ele A[I] cu A[K] sfrit

Generarea permutrilor
Programul
#include <stdio.h> #define N 4 int a[N+1]; void Tipareste(){ int i; for (i=1; i<=N; i++) printf("%d ", a[i]); printf("\n"); }

Generarea permutrilor
Programul
void Permutare(int k) { int i,x; if (k == 1) Tipareste(); else { Permutare(k-1); for (i=1; i<=k-1; i++) { x = a[i]; a[i] = a[k]; a[k] = x; Permutare(k-1); x = a[i]; a[i] = a[k]; a[k] = x; } }}

Generarea permutrilor
Programul
int main(int argc, char *argv[ ]) { int i; for (i=1; i<=N; i++) a[i] = i; Permutare(N); return 0; }

Generarea permutrilor
Concluzii
Pentru a parcurge n mod corect i complet toate permutrile prin nlocuirea fiecrui element ak cu toate elementele ai , la revenirea din cel de-al doilea apel recursiv trebuie refcut starea iniial a tabloului a. Aceast situaie sau situaii similare apar frecvent n programele recursive, atunci cnd procedura recursiv efectueaz modificri asupra unor variabile globale (externe funciilor). Se observ c programul conine chiar operaiile descrise anterior n pseudocod, la care se adaug condiia de oprire a apelrii recursive (k = 1, caz n care s-a obinut o secven final i se tiprete). n concluzie, descrierea trecerii de la un pas la pasul urmtor, calitativ identic, mpreun cu condiia de oprire, sunt suficiente pentru implementarea n program a unui algoritm recursiv( similitudine cu metoda induciei matematice ).

S-ar putea să vă placă și

  • Cap. 9-CTI
    Cap. 9-CTI
    Document34 pagini
    Cap. 9-CTI
    Catalin Manole
    Încă nu există evaluări
  • Continutul Cursului CTI
    Continutul Cursului CTI
    Document9 pagini
    Continutul Cursului CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 8-CTI
    Cap. 8-CTI
    Document25 pagini
    Cap. 8-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 7-CTI
    Cap. 7-CTI
    Document46 pagini
    Cap. 7-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 5-CTI
    Cap. 5-CTI
    Document66 pagini
    Cap. 5-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 6-CTI
    Cap. 6-CTI
    Document92 pagini
    Cap. 6-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 1-CTI
    Cap. 1-CTI
    Document39 pagini
    Cap. 1-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 3-CTI
    Cap. 3-CTI
    Document51 pagini
    Cap. 3-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 2-CTI
    Cap. 2-CTI
    Document62 pagini
    Cap. 2-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 9-CTI
    Cap. 9-CTI
    Document34 pagini
    Cap. 9-CTI
    Catalin Manole
    Încă nu există evaluări
  • Cap. 7-CTI
    Cap. 7-CTI
    Document46 pagini
    Cap. 7-CTI
    Catalin Manole
    Încă nu există evaluări