Tematică științifică · 2.7
Subprograme recursive
Recursivitatea directă și indirectă, mecanismul apelului recursiv pe segmentul de stivă, evoluția parametrilor și a variabilelor locale la funcție și la procedură, adâncimea recursivității, avantajele și dezavantajele, cu planul de idei cerut la examen.
Recursivitatea este un mecanism general de elaborare a algoritmilor.
Recursivitate directă și indirectă
Forma generală cuprinde întotdeauna o condiție de oprire și o ramură pe care se face autoapelul.
În C++
tip_returnat nume_functie(lista de parametri) {
if (conditie_de_oprire)
instructiune;
else
nume_functie(lista de parametri);
}
În Python
def nume_functie(lista de parametri):
if conditie_de_oprire:
instructiune
else:
nume_functie(lista de parametri)
În C++
Al doilea subprogram trebuie declarat înaintea primului, printr-un prototip: la momentul în care compilatorul citește corpul primului, celălalt încă nu există pentru el.
bool impar(int n); // prototipul, fara corp
bool par(int n) {
if (n == 0) return true;
else return impar(n - 1);
}
bool impar(int n) {
if (n == 0) return false;
else return par(n - 1);
}
Fără prototipul din primul rând, programul nu se compilează.
În Python
Nu se declară nimic înainte. Numele se caută în momentul apelului, nu în momentul citirii
programului, deci par se poate scrie prima chiar dacă folosește impar.
def par(n):
if n == 0:
return True
else:
return impar(n - 1)
def impar(n):
if n == 0:
return False
else:
return par(n - 1)
Trebuie doar ca amândouă să fie definite înainte de primul apel, nu înainte una de alta.
Mecanismul recursivității
Pe segmentul de stivă se păstrează, la fiecare apel:
- variabilele locale ale subprogramului;
- parametrii formali, adică valorile sau referințele transmise la apel;
- adresa de revenire, adică locul din program unde execuția se întoarce după ce subprogramul se încheie.
Ce se întâmplă la fiecare apel
| Pasul | Ce se execută |
|---|---|
| Pas 1. La primul apel | Se depun pe primul nivel al segmentului de stivă parametrii transmiși, în ordine, și se rezervă spațiu pentru variabilele locale. |
| Pas 2. La autoapel | Se depun pe al doilea nivel, din nou, parametrii transmiși, și se rezervă spațiu nou pentru variabilele locale. |
| Pas 3. La revenire | Vârful stivei coboară și este adus la valoarea dinaintea activării. |
De câte ori se autoapelează subprogramul, se creează un nivel nou. Așa se explică de ce toate activările lui se află deodată în stivă.
Fiecare autoapel primește o zonă nouă de memorie, deci și o definire nouă a variabilelor locale: variabila locală este unică pentru un apel. Dacă subprogramul se apelează recursiv de n ori, există n imagini ale aceleiași variabile, câte una pentru fiecare apel, fiecare cu conținutul ei.
La calculul lui 5! sunt 6 apeluri: pentru 5, 4, 3, 2, 1 și 0.
Subprogram-funcție: factorialul
Un subprogram-funcție recursiv întoarce o valoare prin return. Factorialul se calculează după
formula:
factorial(x) = 1, dacă x = 0
factorial(x) = x · factorial(x − 1), dacă x ≥ 1
În C++
long long factorial(int x) {
if (x == 0)
return 1; // conditia de oprire
else
return x * factorial(x - 1); // autoapelul
}
int main() {
int n;
cin >> n;
cout << "Factorial " << factorial(n);
return 0;
}
În Python
def factorial(x):
if x == 0:
return 1 # conditia de oprire
else:
return x * factorial(x - 1) # autoapelul
n = int(input())
print('Factorial', factorial(n))
Nu se pune problema depășirii domeniului: int nu are limită de mărime, iar factorial(50) se calculează
exact, cu toate cele 65 de cifre.
Evoluția parametrului la coborâre și la revenire
La coborâre se rețin pe fiecare nivel valorile 5, 4, 3, 2, 1, 0. Înmulțirile se fac abia la revenire, de la baza stivei spre vârf:
| Nivelul | Ce se calculează | Rezultatul |
|---|---|---|
| x = 0 | condiția de oprire | 1 |
| x = 1 | 1 · factorial(0) | 1 |
| x = 2 | 2 · factorial(1) | 2 |
| x = 3 | 3 · factorial(2) | 6 |
| x = 4 | 4 · factorial(3) | 24 |
| x = 5 | 5 · factorial(4) | 120 |
Desfășurat, calculul arată așa:
factorial(5) = 5 · factorial(4)
= 5 · 4 · factorial(3)
= 5 · 4 · 3 · factorial(2)
= 5 · 4 · 3 · 2 · factorial(1)
= 5 · 4 · 3 · 2 · 1 · factorial(0)
= 5 · 4 · 3 · 2 · 1 · 1 = 120
Niciun rezultat nu se obține la coborâre. Toate se obțin la revenire, iar valoarea este întoarsă
prin return din apel în apel, până la programul principal.
Subprogram-procedură: afișarea cifrelor
Un subprogram-procedură recursiv nu întoarce nimic; el afișează. Aici nu mai contează valoarea întoarsă
prin return, ci unde este scrisă afișarea față de autoapel.
În C++
void cifre(int n) {
if (n > 9)
cifre(n / 10);
cout << n % 10 << " ";
}
int main() {
cifre(357);
return 0;
}
În Python
def cifre(n):
if n > 9:
cifre(n // 10)
print(n % 10, end=' ')
cifre(357)
Împărțirea se scrie //, nu /. Cu /, parametrii autoapelurilor ar fi numerele reale 35.7 și
3.57. Programul s-ar încheia, dar ar afișa valori reale în locul cifrelor:
3.5700000000000003 5.700000000000003 7. Nu există void: un subprogram care nu are return întoarce None.
Evoluția parametrului
Fiecare apel are copia lui de n. La coborâre se rețin, prin împărțiri succesive la 10, valorile
357, 35, 3. La revenire, fiecare nivel afișează n % 10 din copia lui:
| Nivelul | n | Ce face |
|---|---|---|
| 1 | 357 | 357 > 9, deci se autoapelează cu 35 |
| 2 | 35 | 35 > 9, deci se autoapelează cu 3 |
| 3 | 3 | 3 > 9 este fals, deci afișează 3 |
| revine la 2 | 35 | afișează 5 |
| revine la 1 | 357 | afișează 7 |
Se afișează 3 5 7, adică cifrele în ordinea firească, deși împărțirile le-au dat de la ultima
spre prima. Ordinea se inversează fiindcă afișarea este scrisă după autoapel. Mutată înaintea lui, s-ar
afișa 7 5 3.
Materialul acesta se citește pe educamp.ro și nu se tipărește.
S-a încheiat minutul de citit liber.
Ce cuprinde subcapitolul
- Recursivitate directă și indirectă
- Mecanismul recursivității
- Subprogram-funcție: factorialul
- Subprogram-procedură: afișarea cifrelor
- Funcție sau procedură
- Alte exemple
- Avantaje și dezavantaje
- Apariții la examen
Continuă lectura ca și cursant
Cel puțin un subcapitol din fiecare capitol este disponibil gratuit și integral. Pentru a citi toate celelalte subcapitole ale disciplinei, te înscrii la cursul de pregătire.
Te înscrii, alegi în cont când începe cursul și plătești prima lună prin transfer bancar. Pentru a vedea mai întâi cum este prezentată materia, poți reveni la primul subcapitol al capitolului.
Acces la suportul de curs
25 RON / lună, fără ședințe
Ce cuprinde:
- Acces la toate lecțiile disponibile
- Suport de curs pentru tematica științifică și metodică
- Exemple, explicații și exerciții
- Acces la materialele noi adăugate pe platformă
- Posibilitatea de a adresa întrebări
Poți alege și variante cu ședințe online, în grup sau individuale. Le vezi pe pagina cursului.
Ai întrebări sau o problemă? Scrie-mi pe WhatsApp, la 0745 874 576.
Surse (3)
- Cerchez, E., Șerban, M., „Programarea în limbajul C/C++ pentru liceu”, Editura Polirom, Iași, 2005
- „Informatică. Manual pentru clasa a XI-a”, Editura Didactică și Pedagogică, București
- „Tutorialul de Python”, documentația oficială a limbajului Python, https://docs.python.org/ro/3/tutorial/index.html