Recursivitate
Un subprogram este recursiv dacă se autoapelează direct sau prin intermediul altor subprograme.
Un subprogram recursiv conține:
- o condiție de oprire (altfel, apelurile ar fi infinite);
- un apel recursiv care apropie funcția de această condiție.
Exemple de operații matematic recursive:
- ridicarea la putere: an = a * an-1 = a * a * an-2 = ...
- factorial: n! = n * (n-1)! = ...
- suma cifrelor: sumCif(n) = n%10 + sumCif(n/10) = ...
- șirul lui Fibonacci: fib(n) = fib(n-1) + fib(n-2) = ...
Exemplu: factorialul unui număr
Implementarea în C++ cu o funcție recursivă s-ar face în următorul mod:
int factorial(int n) {
if (n == 0) return 1; // conditie de oprire
return n * factorial(n - 1); // apel recursiv
}
Observăm în subprogram:
- condiția de oprire (cazul de bază) este atunci când n este 0;
- în instrucțiunea
returnse așteaptă rezultatul funcției reapelate; - în fiecare funcție se înmulțește valoarea din parametru cu rezultatul returnat de apelul recursiv.
Recursiv vs. Iterativ
De multe ori, un algoritm scris recursiv (cu autoapeluri ale funcției) este o adaptare a celui iterativ (cu bucle). Să considerăm un exemplu comun întâlnit:
Să se scrie un subprogram care determină rezultatul operației an, unde a și n sunt două numere întregi.
Funcția iterativă:
int putere (int a, int n){
int p = 1;
for(int i = 1; i <= n; i++) {
p *= a;
}
return p;
}
Funcția recursivă:
int putere (int a, int n){
if(n == 0) return 1;
return a * putere(a, n - 1);
}
În ambele subprograme, transmitem cele 2 date de intrare ca parametru, prin valoare.
Se observă că varianta recursivă este o alternativă mai elegantă, mai ales în acest context unde însăși operația de înmulțire repetată a aceluiași număr a de n ori este un procedeu matematic recursiv.
Afișarea în subprograme recursive
Uneori, ar putea fi nevoie să realizăm diferite afișări în interiorul funcției recursive. În funcție de poziția afișării față de apelul recursiv, putem determina diferite modele de afișare. Spre exemplu:
Afișarea înainte de apelul recursiv
Atunci când afișarea se situează înainte de apelul recursiv, vom vedea rezultatele în ordinea în care se execută (apelează) subprogramele.
void f(int n){
if(!n) return;
cout << n % 10;
f(n / 10);
}
n = 123456; f(n):
654321
Ca și caz de bază (condiția de oprire a recursivității), vom returna atunci când parametrul n ajunge la valoarea 0.
Afișarea după apelul recursiv
Dacă se afișează după apelul recursiv, vom vedea rezultatele în ordinea inversă executării funcțiilor apelate recursiv.
void f(int n){
if(!n) return;
f(n / 10);
cout << n % 10;
}
n = 123456; f(n):
123456
Putem ține cont de această particularitate pentru a oferi o soluție elegantă în anumite scenarii, precum următoarele: