RECURSIVITATE

 

Recursivitatea este un mecanism general de elaborare a algoritmilor. O funcţie se numeşte recursivă dacă ea se autoapelează, fie direct (în definiţia ei se face apel la ea însăşi), fie indirect (funcţia X apelează funcţia Y, care apelează funcţia X). Recursivitatea a apărut din necesitati practice, date de transcrierea directă a formulelor matematice recursive. Apoi, acest mecanism a fost extins, fiind utilizat în elaborarea multor algoritmi.

De exemplu, definiţia recursivă a lui n! (factorialul lui n) este :

factorial

Functia recursivă se va scrie astfel:

factorial

Se observă ca funcţia factorial se autoapelează. Autoapelul se realizează prin instrucţiunea return factorial(n-1)*n.
Recursivitatea utilizează segmentul de stivă pentru a memora datele programului apelant, câtă vreme calculatorul rulează programul apelat. De fiecare dată când o funcţie se autoapelează, se creează un nou nivel în segmentul de stivă.

Funcţionarea poate fi urmărită în figura de mai jos:

principiuRecursivitate

 Fiecare apel de funcţie lucrează cu datele aflate pe nivelul corespunzator acelui apel. La ieşirea din apelul unei funcţii, nivelul respectiv se eliberează şi datele aflate acolo se pierd. Există posibilitatea ca subprogramul să lucreze direct cu variabilele globale, dar în acest caz, subprogramul işi pierde independenţa.

Observaţii :

-       în cazul unui număr mare de autoapelări, există posibilitatea ca segmentul de stivă sa se ocupe total, caz în care programul se va termina cu eroarea STACK OVERFLOW. Aceasta se întamplă mai ales atunci când condiţia de terminare este pusă greşit şi subprogramul se apelează la nesfârşit.
-      pentru orice algoritm recursiv există unul iterativ, care rezolvă aceeaşi problemă.
-     mecanismul recursivităţii înlocuieste instrucţiunile repetitive.
-     datorită faptului că la fiecare autoapel se ocupă o zonă de memorie, recursivitatea este eficientă numai dacă numărul de autoapelări nu este prea mare, pentru a nu se ajunge la umplerea zonei de memorie alocată.
-        recursivitatea oferă avantajul unor soluţii mai clare pentru probleme şi a unei lungimi mai mici a programului. Ea prezintă  însă dezavantajul unui timp mai mare de execuţie şi a unui spaţiu de memorie alocată mai mare. Este de preferat ca, atunci când programul recursiv poate fi transformat cu uşurinţă într-unul iterativ, să se facă apel la cel din urmă.

Este clar că ordinea executării instructiunilor va depinde de locul în care se întâlneşte apelul recursiv. Să studiem trei cazuri:

 

                       
Pentru structura generică:

                        tip_func_rec sub (listă de parametri){
                                    {setul “a“de instrucţiuni;}
                                    if(condiţie) sub (parametri); // apelul recursiv
                        }

se va executa setul a de instrucţiuni, în ordine “directă” (prima dată cele întâlnite la primul apel al funcţiei recursive sub, ultima dată celor întâlnite la ultimul apel al funcţiei recursive sub).

                  De exemplu, pentru programul de mai jos:

recurs_s1

şi n=3, m=7, se vor obţine rezultatele:

rezultatRecurs_s1

O variantă on-line poate fi experimentată la recurs_s1 sau la recurs_s1

si asa mai departe

Up Home Structura Site

 

Pentru structura generică:

                        tip_func_rec sub (listă de parametri){
                                    if(condiţie) sub (parametri); // apelul recursiv
                                    {setul “b“de instrucţiuni;}
                        }

se va executa setul b de instrucţiuni, în ordine “inversă” (prima dată cele întâlnite la ultimulul apel al funcţiei recursive sub, ultima dată celor întâlnite la primul apel al funcţiei recursive sub).

De exemplu, pentru programul de mai jos:

recurs_s2

şi n=3, m=7, se vor obţine rezultatele:

rezultatRecurs_s2

O variantă on-line poate fi experimentată la recurs_s2 sau la recurs_s2

Up Home Structura Site

 

                        fie structura generică:

                        tip_func_rec sub (listă de parametri){
                                    {setul “a“de instrucţiuni;}
                                    if(condiţie) sub (parametri); // apelul recursiv
                                    {setul “b“de instrucţiuni;}
                        }

De exemplu, pentru programul de mai jos:

recurs_s3

şi n=3, m=7, se vor obţine rezultatele:

rezultRecurs_s3

O variantă on-line poate fi experimentată la recurs_s3 sau la recurs_s3

Up Home Structura Site

 

Un exemplu practic pentru această din urmă situaţie se prezintă în programul următor, care afişează în ordine inversă valorile citite de la tastatură (introducerea datelor se consideră încheiată când se introduce valoarea zero)

recurs_s4

şi n=3, 4,5,6, 7, se vor obţine rezultatele:

rezultRecurs_s3

rezultRecurs_s4

O variantă on-line poate fi experimentată la recurs_s4 sau la recurs_s4

Up Home Structura Site

 

 

EXEMPLE DE PROBLEME REZOLVATE RECURSIV

 

A. Calculul factorialului unui număr, implementat recursiv

factorialRecursiv

rezulFactRecurs

O variantă on-line poate fi experimentată la factorialRecursiv sau la factorialRecursiv

Up Home Structura Site

 

 

B. Calculul cmmdc prin algoritmul lui Euclid implementat recursiv

 

B.1. Algoritmul lui Euclid cu scăderi succesive

euclidDifRec

rezEuclidDifRec

O variantă on-line poate fi experimentată la euclidDifRec sau la euclidDifRec

Up Home Structura Site

 

B.2. Algoritmul lui Euclid cu împărţiri succesive

euclidDifRec

rezEuclidDifRec

O variantă on-line poate fi experimentată la euclidImpRec sau la euclidImpRec

 

Up Home Structura Site

 

C. Şirul lui Fibonacci (varianta recursivă)

 

fibonacci1

 

rezFibonacci1

O variantă on-line poate fi experimentată la fibonacci1 sau la fibonacci

Up Home Structura Site

 

D. Trecerea unui număr din baza 10 în baza b (varianta recursivă)

schimbareBaza

rezSchimbareBaza

O variantă on-line poate fi experimentată la schimbareBaza sau la schimbareBaza

Up Home Structura Site

 

E. Umplerea cu 1 a unei zone închise de valori de 0, delimitate de valori de 1

 

umplereSus

umplereJos

 

umplereInainte

umplereDupa

Exemplul este rulat cu ajutorul compilatorului de C++ “Neutron”, pe un emulator al sistemului de operare DOS

Up Home Structura Site

 

 

F. Ieşirea din labirint

 

labirintInclude

labirintAfisare

labirintIes

labirintMain

canv31

canv32

Exemplul este rulat cu ajutorul compilatorului de C++ “Neutron”, pe un emulator al sistemului de operare DOS


mai jos, problema este rezolvata in CodeBlocks

Labirint canvas

(desenat apoi in canvas, cu JavaScript)

Up Home Structura Site