Pe scurt
Recursivitatea apare când un subprogram se apelează pe sine pentru o problemă mai mică. Sunt necesare un caz de oprire și un pas care se apropie de el. Pentru factorial, și când . Astfel .
| Ce | Exemplu |
|---|---|
| Definiția recursivă | n! = n · (n − 1)!, cu 0! = 1 |
| Funcția recursivă | int fact(int n) { if (n == 0) return 1; return n * fact(n - 1); } |
| Condiția de oprire | cazul rezolvat direct, fără autoapel (n == 0) |
| Autoapelul | trebuie să se apropie de condiția de oprire (n - 1) |
| Instrucțiuni înainte de autoapel | se execută la „coborâre”, în ordinea apelurilor |
| Instrucțiuni după autoapel | se execută la „revenire”, în ordinea inversă |
| Numărul de apeluri | se numără și apelul inițial |
În detaliu
Condiția de oprire și autoapelul
O funcție recursivă are două părți: cazul simplu, rezolvat direct, și cazul general, în care funcția se autoapelează pentru o problemă mai mică. Fiecare autoapel trebuie să se apropie de cazul simplu: la fact(n - 1), n scade cu 1 la fiecare apel, deci ajunge la 0. Dacă cele două părți sunt corecte, funcția e corectă pentru orice n, la fel ca la o demonstrație prin inducție.
Stiva apelurilor
Fiecare apel are propriile copii ale parametrilor și ale variabilelor locale, păstrate pe stivă. Când fact(3) ajunge la fact(2), apelul fact(3) rămâne „în așteptare”, cu n = 3, până când fact(2) returnează. Apelurile se închid în ordinea inversă deschiderii: ultimul deschis, fact(0), se termină primul. De aceea, instrucțiunile scrise înaintea autoapelului se execută la coborâre (3, 2, 1), iar cele scrise după el, la revenire (1, 2, 3).
Recursiv sau iterativ
Orice funcție recursivă se poate scrie și iterativ, cu o structură repetitivă. Varianta recursivă e potrivită când problema e definită recursiv (factorial, cmmdc, parcurgerea unui graf, backtracking), dar fiecare apel costă timp și memorie pe stivă. Când două autoapeluri recalculează aceleași valori, numărul de apeluri explodează: F(n) = F(n − 1) + F(n − 2), calculat naiv, face 109 apeluri pentru F(10) și 1 664 079 pentru F(30) (exemplul 4).
Exemplu — numărul de apeluri pentru F(5). Dacă numără apelurile, , iar pentru . Rezultă , și . Calculul include și apelul inițial, nu numai autoapelurile.
Recursivitate directă și indirectă
Dacă subprogramul se apelează pe sine, recursivitatea este directă. Dacă A îl apelează pe B, iar B îl apelează pe A, recursivitatea este indirectă; și aici trebuie să existe o condiție de oprire comună.
Factorialul, limitele lui și varianta iterativă
Exemplu — factorialul lui 4. Implementarea factorialului păstrează câte un cadru pe stiva apelurilor, până la atingerea cazului de bază:
#include <iostream>
using namespace std;
long long factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
int main() {
int n;
cin >> n;
cout << factorial(n);
return 0;
}
Pentru intrarea 4, apelurile coboară factorial(4) → factorial(3) → factorial(2) → factorial(1) → factorial(0). Cazul de bază întoarce 1, iar rezultatele urcă: . Ieșirea este 24. Fiecare apel păstrează propria valoare a parametrului n până când apelul interior se încheie. Timpul este proporțional cu , iar stiva ocupă spațiu proporțional cu . Programul presupune : pentru argumentul s-ar îndepărta de 0 și apelurile nu s-ar opri, iar pentru valoarea depășește tipul long long.
Fiecare apel păstrează propria valoare a parametrului n până când apelul interior se încheie. Când se ajunge la cazul de bază, nu mai apare un apel nou; la revenire, fiecare nivel face o singură înmulțire. Pentru un n admis, timpul este , iar stiva de apeluri ocupă spațiu. Programul presupune : la n<0, argumentul s-ar îndepărta de 0 și apelurile nu s-ar opri, iar peste 20 valoarea nu încape în tipul folosit. Varianta iterativă înmulțește de la mic la mare, fără apeluri pe stivă, cu memorie auxiliară .
Exemple rezolvate
Exemplul 1: factorialul, cu apelurile urmărite
Calculează 5! recursiv.
fact(5)= 5 ·fact(4)= 5 · 4 ·fact(3)= … = 5 · 4 · 3 · 2 · 1 ·fact(0).fact(0)returnează 1, apoi valorile se înmulțesc la revenire: 1, 1, 2, 6, 24, 120.
#include <iostream>
using namespace std;
int fact(int n) {
if (n == 0)
return 1; // conditia de oprire
return n * fact(n - 1); // autoapelul, pentru o valoare mai mica
}
int main() {
cout << fact(5) << "\n";
return 0;
}
Programul afișează:
120
Exemplul 2: suma cifrelor, recursiv
Suma cifrelor lui n este ultima cifră plus suma cifrelor lui n / 10, iar suma cifrelor lui 0 este 0.
#include <iostream>
using namespace std;
int sc(int n) {
if (n == 0)
return 0;
return n % 10 + sc(n / 10);
}
int main() {
cout << sc(52816) << "\n";
return 0;
}
Programul afișează:
22
Aceeași funcție, scrisă iterativ, este în lecția despre subprograme.
Exemplul 3: ordinea afișărilor
Ce afișează apelul f(3)?
#include <iostream>
using namespace std;
void f(int n) {
if (n > 0) {
cout << n; // la coborare
f(n - 1);
cout << n; // la revenire
}
}
int main() {
f(3);
cout << "\n";
return 0;
}
- La coborâre se afișează 3, 2, 1, câte o cifră înaintea fiecărui autoapel.
f(0)nu face nimic. La revenire, fiecare apel își afișează propriul n, de la ultimul deschis la primul: 1, 2, 3.
321123
Exemplul 4: câte apeluri face Fibonacci, scris naiv
Șirul lui Fibonacci are F(1) = F(2) = 1 și F(n) = F(n − 1) + F(n − 2). Câte apeluri face funcția recursivă pentru F(10)?
- Fiecare apel pentru n > 2 face două autoapeluri, iar valorile mici se recalculează de multe ori: F(8) se calculează de 2 ori, F(7) de 3 ori.
- Numărând toate apelurile, inclusiv pe cel inițial, se obțin 109 apeluri pentru F(10) = 55.
#include <iostream>
using namespace std;
int apeluri = 0;
int fib(int n) {
apeluri++;
if (n <= 2)
return 1;
return fib(n - 1) + fib(n - 2);
}
int main() {
int n;
cin >> n;
int r = fib(n);
cout << r << " " << apeluri << "\n";
return 0;
}
Pentru intrarea 10, programul afișează valoarea și numărul de apeluri:
55 109
Pentru 30 afișează 832040 1664079. O buclă care ține doar ultimii doi termeni face 28 de pași pentru același rezultat.
Exemplul 5: nivel de examen, o funcție recursivă pe cifre
Se consideră funcția de mai jos. Ce valoare returnează f(2468013) și câte apeluri ale lui f se fac în total?
#include <iostream>
using namespace std;
int nr = 0;
int f(int n) {
nr++;
if (n == 0)
return 0;
if (n % 2 == 0)
return f(n / 10) + 1;
return f(n / 10);
}
int main() {
cout << f(2468013) << " " << nr << "\n";
return 0;
}
- Funcția elimină câte o cifră la fiecare apel și adaugă 1 când ultima cifră e pară, deci numără cifrele pare.
- Cifrele pare ale lui 2468013 sunt 2, 4, 6, 8 și 0: funcția returnează 5.
- Numărul are 7 cifre, deci sunt 7 apeluri care elimină câte o cifră, plus apelul final cu n = 0: 8 apeluri.
5 8
Pentru admitere
Pentru intrarea 4, stiva primește în ordine argumentele 4, 3, 2, 1, 0. Revenirea calculează în ordine 1, 1, 2, 6, 24, iar rezultatul este 24. Varianta iterativă pornește cu produs = 1 și înmulțește succesiv cu 1, 2, 3, 4; obține tot 24, în timp proporțional cu și cu spațiu auxiliar constant. Pentru orice recursie, verifică atât cazul de bază, cât și faptul că argumentul fiecărui apel se apropie de el. Când o funcție afișează înainte sau după apelul recursiv, ordinea afișărilor diferă: afișarea dinaintea apelului produce valorile în ordinea apelurilor, iar cea de după, în ordinea revenirilor. Pentru void scrie(int n) cu afișarea înainte de apel, scrie(3) produce 321; cu afișarea după apel, produce 123. Dacă un apel are două apeluri recursive, ca la Fibonacci, desenează arborele apelurilor și numără frunzele.
Exemplu în formatul examenului. Ce se afișează în urma executării programului de mai jos?
#include <iostream>
using namespace std;
int f(int n) {
if (n <= 0) return 0;
return n % 10 + f(n / 10);
}
int main() {
cout << f(2345);
return 0;
}
- a) 5
- b) 14
- c) 5432
- d) 2345
Răspunsul corect este b). Apelurile sunt f(2345), f(234), f(23), f(2) și f(0). Funcția adună ultima cifră a fiecărui argument: , iar f(0) întoarce 0. Varianta c) ar fi un număr format din cifrele în ordine inversă, nu o sumă, iar varianta a) ar apărea dacă funcția ar returna numai prima cifră extrasă.
Cazul de bază contribuie la rezultat: dacă funcția din exemplu ar întoarce 1, nu 0, pentru , atunci f(2345) ar fi , nu 14.
Unde apare la examen
- Bacalaureatul, informatică, matematică-informatică: programa de examen are un capitol „Recursivitate” (prezentare generală, proceduri și funcții recursive), iar metoda backtracking poate fi implementată recursiv (programa de examen, anexa 2 la OMECTS 4800/2010). La sesiunea din iunie–iulie 2026, al doilea item al Subiectului I a cerut completarea autoapelului unui subprogram recursiv, astfel încât apelul dat să afișeze o valoare cerută (subiectele și baremele Bac 2026). Formatul descris este cel din 2026; potrivit proiectului de ordin pentru Bacalaureatul 2027, programa rămâne aceeași.
- Programa școlară: pe programa veche, recursivitatea este la clasa a XI-a, împreună cu subprogramele (programa de clasa a XI-a, OMECI 5099/2009); pe programa nouă, subprogramele recursive sunt la clasa a X-a, din 2027–2028, în Python (OMEC 4370/2026).
Greșeli frecvente
- Greșit: o funcție recursivă nu are nevoie de un caz de oprire.Corect: fără caz de bază apelurile nu se mai opresc.
- Greșit: apelurile recursive nu ocupă memorie suplimentară.Corect: fiecare apel adaugă un cadru pe stiva apelurilor.
- Greșit: cazul de bază este întotdeauna n = 1.Corect: depinde de problemă; la factorial este n = 0, cu 0! = 1.
- Greșit: cazul de bază este opțional, iar lipsa condiției de oprire nu strică.Corect: fără o oprire atinsă, funcția se autoapelează la nesfârșit, până se umple stiva și programul se oprește cu eroare.
- Greșit:
fact(n)în loc defact(n - 1), sau o funcție pe cifre care nu împarte la 10.Corect: autoapelul se apropie de oprire; altfel funcția nu se termină niciodată. - Greșit:
fact(n - 1); return n;.Corect: rezultatul autoapelului trebuie folosit, deci scriireturn n * fact(n - 1);. - Greșit: ce e scris după autoapel se execută în ordinea apelurilor.Corect: se execută la revenire, adică în ordinea inversă apelurilor (exemplul 3).
- Greșit: la numărarea apelurilor se omite primul apel.Corect: se numără și apelul inițial, cel din
main. - Greșit: Fibonacci recursiv naiv pentru valori mari, când există o soluție iterativă simplă.Corect: pentru n = 50 face miliarde de apeluri.
- Greșit:
factorial(4)întoarce 4 la prima intrare în funcție.Corect: așteaptă rezultatul apelului cu 3. - Greșit: toate apelurile folosesc aceeași variabilă locală
n.Corect: fiecare apel are propria instanță.
Testează-te pe această lecție
Lecția o citești aici, fără cont. Testele pe lecții și capitole sunt în cont, cu grile originale Didaxis și explicația fiecăreia: gratuit 10 grile pe zi, fără limită cu Premium, 49 de lei pe lună pentru fiecare copil.
Înainte și după
Întrebări frecvente
Ce este condiția de oprire la o funcție recursivă?
Ce este mai bine: recursiv sau iterativ?
Ce este recursivitatea indirectă?
Surse și referințe
Redactare proprie, conform programei:
- OMECI 5099/2009, anexa 5, Informatică, p. 4–5, clasa X intensiv; rocnee.eu
- OMECI 5099/2009, anexa 5, Informatică, p. 4–5, clasa XI; plan de încadrare: OMEC 7723/2024, anexa 2, tabelele 1–25, legislatie.just.ro; filiera tehnologică: OMEC 7724/2024, anexa 4, tabelul calificării, legislatie.just.ro
- UNSTPB, Regulament admitere licență 2026, Anexa II-1.2 p. Anx II-14 și Anexa XI-3 p. Anx XI-6–7 (PDF p. 64, 120–121); admitere.pub.ro
- OMECTS 4800/2010, anexa 2, Informatică, p. 3–4; edu.ro
Documente consultate
- UNSTPB, admitere 2026, anexa XI-3, p. Anx XI-6–7
- Programa școlară de informatică, clasa a XI-a (OMECI 5099/2009, anexa 5), CNCE; rocnee.eu
- Programa de bacalaureat la informatică, matematică-informatică (anexa 2 la OMECTS 4800/2010), Ministerul Educației, p. 4–5; edu.ro
- Programele școlare noi de informatică pentru clasele X–XII (OMEC 4370/2026, MO 591 bis/20.07.2026), CNCE; rocnee.eu
- Subiectele și baremele Bacalaureatului 2026; subiecte2026.edu.ro
Desene: Redactare proprie (desen original), conform Programa școlară de informatică, clasa a XI-a (OMECI 5099/2009, anexa 5), CNCE; rocnee.eu
