Teorie
Recursivitate în C++ la atestatul de informatică — cazul de bază, pasul recursiv, funcții pe cifre și pe tablouri
Cum scrii o funcție recursivă cerută la atestat: cazul de bază și pasul recursiv, elementul neutru, recursivitatea pe cifre, algoritmul lui Euclid și recursivitatea pe tablouri — cu urmărirea apelurilor și problemele în care apar.
6
minute de citit
9
secțiuni
6
probleme rezolvate legate
Actualizat pe .
Pe această pagină
- Scheletul: cazul de bază și pasul recursiv
- Tehnica 1 — Recursivitate care acumulează: elementul neutru
- Tehnica 2 — Recursivitate care coboară: răspunsul e în cazul de bază
- Tehnica 3 — Doi parametri: algoritmul lui Euclid
- Tehnica 4 — Recursivitate pe un tablou: n scade cu 1
- Când recursivitatea e o idee proastă
- Greșeli care se repetă
- Unde exersezi mai departe
- Toate problemele din această categorie
La atestat, recursivitatea nu apare ca subiect separat, ci ca o condiție pusă unui subprogram:
„folosind o funcție recursivă, calculați suma cifrelor”, „p_cifra (implementat recursiv)
returnează prima cifră”. Rezultatul ar ieși la fel de bine cu un while — dar enunțul cere
recursivitate, iar varianta iterativă nu primește punctajul subpunctului, chiar dacă dă
răspunsul corect.
Vestea bună: toate cele șase probleme din sesiunile Dolj 2026 și Bucureşti 2020 folosesc același schelet de trei rânduri. Pagina îl explică o dată, apoi arată cele patru variante în care apare.
Scheletul: cazul de bază și pasul recursiv
Orice funcție recursivă are două părți:
- cazul de bază — situația în care răspunsul se știe direct, fără alt apel;
- pasul recursiv — funcția se apelează pe o variantă mai mică a aceleiași probleme și își construiește rezultatul din răspunsul primit.
int sumaCifrelor(int n) {
if (n == 0) return 0; // cazul de baza: nu mai sunt cifre
return n % 10 + sumaCifrelor(n / 10); // ultima cifra + suma celorlalte
}Ca să verifici o funcție recursivă, urmărește apelurile pe hârtie. Ele coboară până la cazul de bază, iar rezultatele se combină la întoarcere:
sumaCifrelor(3815) = 5 + sumaCifrelor(381)
sumaCifrelor(381) = 1 + sumaCifrelor(38)
sumaCifrelor(38) = 8 + sumaCifrelor(3)
sumaCifrelor(3) = 3 + sumaCifrelor(0)
sumaCifrelor(0) = 0 <- cazul de baza
-> 0 + 3 + 8 + 1 + 5 = 17
Două reguli fără excepție:
- Cazul de bază se scrie primul. Fără el, funcția se apelează la nesfârșit, iar programul se oprește cu stack overflow.
- Fiecare apel trebuie să se apropie de cazul de bază.
n / 10scurtează numărul cu o cifră, deci ajunge sigur la0. Cun / 10.0(împărțire reală), nu mai ajunge niciodată.
Exersează pe subiecte reale
Tehnica 1
Recursivitate care acumulează: elementul neutru
Când funcția adună, numără sau înmulțește ceva, cazul de bază întoarce elementul neutru al operației — valoarea care nu schimbă rezultatul:
| Operația | Cazul de bază întoarce | Exemplu |
|---|---|---|
| sumă | 0 |
suma cifrelor |
| numărare | 0 |
câte cifre pare are numărul |
| produs | 1 |
produsul cifrelor, produsul diagonalei |
int produsCifrelor(int n) {
if (n == 0) return 1; // 1, nu 0: la inmultire, 0 "inghite" tot
return n % 10 * produsCifrelor(n / 10);
}Cu return 0, produsul iese 0 pentru orice număr — greșeala cea mai depunctată la aceste
probleme. (Dacă numărul chiar conține cifra 0, de exemplu 305, produsul este 0; nu e o
eroare.)
Numărarea se scrie la fel, adunând rezultatul unei condiții — în C++, o comparație valorează 1
dacă e adevărată și 0 dacă e falsă:
int contorizare(int y) { // cate cifre pare are y
if (y == 0) return 0;
return (y % 10 % 2 == 0) + contorizare(y / 10);
}Exersează pe subiecte reale
Tehnica 2
Recursivitate care coboară: răspunsul e în cazul de bază
Nu orice funcție recursivă combină ceva la întoarcere. Ca să afli prima cifră a unui număr, tai ultima cifră până rămâne una singură — iar aceea e chiar răspunsul:
int p_cifra(int y) {
if (y < 10) return y; // o singura cifra: ea e prima
return p_cifra(y / 10); // nimic de adaugat, doar coboram
}p_cifra(48903) → p_cifra(4890) → p_cifra(489) → p_cifra(48) → p_cifra(4) = 4.
Aici cazul de bază nu e y == 0: coborând până la 0, funcția ar întoarce mereu 0. Întrebarea
de pus e „când știu răspunsul direct?” — la sumă, când nu mai sunt cifre (0); la prima cifră,
când a rămas una (y < 10).
Exersează pe subiecte reale
Tehnica 3
Doi parametri: algoritmul lui Euclid
C.m.m.d.c.-ul are o definiție recursivă aproape gata scrisă: c.m.m.d.c.(x, y) = c.m.m.d.c.(y, x % y),
iar c.m.m.d.c.-ul unui număr cu 0 e numărul însuși.
int cmmdc(int x, int y) {
if (y == 0) return x;
return cmmdc(y, x % y);
}cmmdc(48, 36) -> cmmdc(36, 12) -> cmmdc(12, 0) = 12
Ordinea parametrilor nu contează: cmmdc(36, 48) face întâi cmmdc(48, 36) și continuă la fel.
Contează însă ca testul din cazul de bază să fie pe parametrul care se micșorează — y, care
devine x % y. Cu if (x == 0) return y; recursivitatea nu se mai termină.
Varianta cu scăderi (cmmdc(x - y, y)) e corectă matematic, dar pentru 1000000 și 1 face un
milion de apeluri. Ideea e explicată pe larg la algoritmul lui Euclid.
Exersează pe subiecte reale
Tehnica 4
Recursivitate pe un tablou: n scade cu 1
Același schelet merge și pe tablouri: în loc să tai o cifră, tai ultimul element. Produsul
elementelor de pe diagonala principală a unei matrici n × n e a[n][n] înmulțit cu produsul
diagonalei matricei de dimensiune n - 1:
double produs(double a[21][21], int n) {
if (n == 0) return 1; // produsul vid
return a[n][n] * produs(a, n - 1);
}Pentru diagonala 3, 2, 1: produs(a, 3) = 1 · produs(a, 2) = 1 · 2 · produs(a, 1) = 1 · 2 · 3 · 1 = 6.
Parametrul care scade e dimensiunea, nu tabloul — tabloul se transmite neschimbat la fiecare
apel. Cazul de bază n == 0 presupune indexarea de la 1; cu indexare de la 0, elementul curent e
a[n-1][n-1].
Exersează pe subiecte reale
Când recursivitatea e o idee proastă
Recursivitatea e corectă oriunde, dar nu e mereu rezonabilă. Șirul lui Fibonacci scris recursiv
(fib(n - 1) + fib(n - 2)) recalculează aceiași termeni de foarte multe ori și devine inutilizabil
deja pe la n = 45 — vezi șirurile recurente.
Regula practică la atestat: scrie recursiv doar ce enunțul cere explicit recursiv; restul, cu
bucle.
Greșeli care se repetă
- Lipsa cazului de bază sau scris după apelul recursiv — stack overflow
return 0la produs — rezultatul e0pentru orice număr; produsul începe de la1- Cazul de bază greșit ales —
y == 0la „prima cifră” întoarce mereu0 - Testul pe parametrul care nu scade — la Euclid,
x == 0în loc dey == 0 - Împărțire reală (
n / 10.0) — numărul nu mai ajunge la0 - Rezolvare iterativă când enunțul cere recursiv — rezultat corect, punctaj pierdut
- Scăderi repetate în loc de
%— corect, dar prea lent pe numere mari
Mai departe
Unde exersezi mai departe
Cel mai bun exercițiu e urmărirea apelurilor pe hârtie: ia oricare dintre funcțiile de mai sus, alege un număr și scrie tabelul de apeluri înainte să rulezi programul. Dacă tabelul tău și programul dau același rezultat, ai înțeles mecanismul.
Pe pbinfo.ro găsești probleme de recursivitate cu evaluator automat, pe cifre, pe tablouri și pe șiruri.
Toate problemele din această categorie
Fiecare are rezolvare completă în C++, cu urmărirea apelurilor.
6Arată toate problemele rezolvateAscunde lista6 programare
- Cel mai mare divizor comun a două numere, folosind o funcție recursivă
- Matrice inferior triunghiulară și determinantul ei (inftr și produs recursiv)
- Numere cu prima cifră pătrat perfect, ordonate descrescător (p_cifra recursiv și sortare)
- Numărul prim cu cele mai multe cifre pare dintr-o matrice (prim și contorizare recursiv)
- Produsul cifrelor unui număr, folosind o funcție recursivă
- Suma cifrelor unui număr, folosind o funcție recursivă
Alte pagini de teorie