Sunteți pe pagina 1din 6

Algoritmic i programare Laborator 10

Recursivitate
Recursivitatea este una dintre noiunile fundamentale ale informaticii. Def: Recursivitatea este un mecanism general de elaborare al programelor. Ea const n posibilitatea unui subprogram de a se autoapela. Recursivitatea a aprut din necesitatea de a transcrie direct formule matematice recursive. n timp acest mecanism a fost extins i pentru ali algoritmi. Mecanismul recursivitii Care este mecanismul prin care programele se autoapeleaz? S ne amintim cum memoreaz programele parametrii transmii: pentru memorarea parametrilor, subprogramele folosesc o zon de memorie numit stiv. Memorarea parametrilor transmii se face n ordinea n care acetia figureaz n antet: de la stnga la dreapta. Pentru parametrii transmii prin valoare, se transmite valoarea, iar pentru cei transmii prin referin se transmite adresa. n cadrul subprogramului, parametrii transmii i memorai n stiv sunt variabile. Numele lor este cel din lista parametrilor formali. Subprogramul lucreaz cu datele aflate pe un anumit nivel al stivei pentru variabilele transmise prin valoare, dar i cu variabilele funciei main(), dac acestea sunt transmise prin referin. Exist posibilitatea ca subprogramul s lucreze direct cu variabilele globale fr ca acestea s fie transmise prin referin. Dup cum tim variabilele globale pot fi accesate din orice subprogram, dac sunt declarate la nceputul codului surs. n cazul unui numr foarte mare de autoapelri, exist posibilitatea ca segmentul de stiv s se ocupe total, caz n care programul se va termina cu eroare. Recursivitatea presupune mai mult memorie. O gndire recursiv exprim concentrat o anumit stare, care se repet la infinit. Aceast gndire se aplic n elaborarea algoritmilor recursivi cu o modificare esenial: adugarea condiiei de terminare. n absena acestei condiii nu se poate vorbi despre un algoritm deoarece acetia sunt finii. Pai de elaborare: Ce se ntmpl la un nivel se ntmpl la orice nivel. Subprogramul care se autoapeleaz trebuie s conin instruciunile corespunztoare unui nivel. Un algoritm recursiv se elaboreaz folosind acest tip de gndire , nu o gndire precum cea folosit pn acum, cnd am elaborat algoritmi iterativi. Pentru orice algoritm recursiv exist unul iterativ care rezolv aceeai problem.

Problema 1. Un exemplu simplu de algoritm recursiv este calculul lui n!, aa cum putem vedea n cele ce urmeaz. // calzulez n! fara recursivitate #include<iostream> using namespace std; unsigned long fact(int n) { int i; unsigned long p=1; if(n==0) return 1; else { for(i=1;i<=n;i++) p*=i; return p; } } void main() { int n; cout<<"Introduceti n= ";cin>>n; cout<<n<<"! este "<<fact(n)<<endl; } // calzulez n! prin recursivitate #include<iostream> using namespace std; int factorial(int n) { if(n == 0) return 1; else return n*factorial(n-1); } void main() { int n; cout<<"Introduceti n= ";cin>>n; cout<<n<<"! este "<<factorial(n)<<endl; } Problema 2. Folosind o metod recursiv, s se determine primii n termeni ai irului lui Fibonacci, numrul n fiind introdus de la tastatur. #include<iostream> using namespace std; int Fibonacci(int n) { if(n<2) return n; else return Fibonacci(n-1)+Fibonacci(n-2); } void main()

int i,n; cout<<"Introduceti n= ";cin>>n; for(i=1;i<=n;i++) cout<<"Termenul "<<i<<" din sir este : "<<Fibonacci(i)<<endl; cout<<endl;

Problema 3. Folosind o metod recursiv, s se determine cel mai mare divizor comun al numerelor a i b, introduse de la tastatur. #include<iostream> using namespace std; int cmmdc(int m, int n) { if(!n) return m; return cmmdc(n,m%n); } void main() { int a,b; cout<<"Introduceti a = ";cin>>a; cout<<"Introduceti b = ";cin>>b; cout<<"("<<a<<","<<b<<")="<<cmmdc(a,b)<<endl; } Problema 4. S se citeasc un numr ntreg n de la tastatur i, folosind o metod recursiv, s se afieze descresctor ntregii de la n la 1. #include<iostream> using namespace std; void afiseaza(int p) { if(p==0) return; cout<<p<<" "; afiseaza(p-1); return; } void main() { int n; cout<<"Introduceti n = ";cin>>n; afiseaza(n); cout<<"BUMMM !!!!"<<endl; } Problema 5. S se citeasc un numr ntreg n de la tastatur i, folosind o metod recursiv, s se afieze cresctor ntregii de la 1 la n. #include<iostream> using namespace std; void afiseaza(int p) { if (p==0) return; afiseaza(p-1); cout<<p<<" "; return;

} void main() { int n; cout<<"Introduceti n = ";cin>>n; afiseaza(n); cout<<"BUMMM !!!!"<<endl; } Problema 6. Folosind o metod recursiv, s se stabileasc dac un numr ntreg citit de la tastatur este numr prim sau nu. #include<iostream> using namespace std; bool isPrime(int p, int i) { if(i==p) return 1; if(p%i==0) return 0; return isPrime(p,i+1); } void main() { int n; cout<<"Introduceti n = ";cin>>n; if(isPrime(n,2)) cout<<n<<" este prim !"<<endl; else cout<<n<<" nu este prim !"<<endl; } *********************************

Metoda Backtracking
Se aplic problemelor n care soluia poate fi reprezentat sub forma unui vector x=(x1, x2, x3, xk, xn) din S, unde S este mulimea soluiilor problemei i S=S1 S2 Sn i Si sunt mulimi finite avnd s elemente i xi aparine lui Si . Pentru fiecare problem se dau relaii ntre componentele vectorului x, care sunt numite condiii interne; soluiile posibile care satisfac condiiile interne se numesc soluii rezultat. Metoda de generare a tuturor soluiilor posibile i apoi de determinare a soluiilor rezultat prin verificarea ndeplinirii condiiilor interne necesit foarte mult timp. Metoda backtracking evit aceast generare i este mai eficient. Elementele vectorului x, primesc pe rnd valori n ordinea cresctoare a indicilor, x[k] va primi o valoare numai dac au fost atribuite valori elementelor x[1]...x[k-1]. La atribuirea valorii lui x[k] se verific ndeplinirea unor condiii de continuare referitoare la x[1]...x[k-1]. Dac aceste condiii nu sunt ndeplinite la pasul k, acest lucru nseamn c orice valori am atribui lui x[k], x[k+1],...,x[n] nu se va ajunge la o soluie rezultat.

Metoda backtracking construiete un vector soluie n mod progresiv ncepnd cu prima component a vectorului i mergnd spre ultima, cu eventuale reveniri asupra atribuirilor anterioare. Metoda se aplica astfel: Se alege prima valoare din S1 i i se atribuie lui x1 . Se presupun generate elementele x[1],,x[k-1], cu valori din S1..Sk-1 . Pentru generarea lui x[k] se alege primul element din Sk disponibil i pentru valoarea aleas se testeaz ndeplinirea condiiilor de continuare. Pot aprea urmtoarele situaii : 1. x[k] ndeplinete condiiile de continuare. Dac s-a ajuns la soluia final (k=n) atunci se afieaz soluia obinut. Dac nu s-a ajuns la soluia final se trece la generarea elementului urmtor x[k+1] 2. x[k] nu ndeplinete condiiile de continuare. Se ncearc urmtoarea valoare disponibil din Sk. Dac nu se gsete nici o valoare n Sk care s ndeplineasc condiiile de continuare, se revine la elementul x[k-1] i se reia algoritmul pentru o nou valoare a acestuia. Algoritmul se ncheie cnd au fost luate n considerare toate elementele lui S1 . Problemele rezolvate prin aceat metod necesit timp mare de execuie, de aceea este indicat s se foloseasc metoda numai dac nu avem alt algoritm de rezolvare. Dac mulimile S1,S2,Sn au acelai numr k de elemente, timpul necesar de execuie al algoritmului este k la puterea n. Dac mulimile S1, S2,...,Sn nu au acelai numr de elemente, atunci se noteaz cu m minimul cardinalelor mulimilor S1,S2,Sn i cu M maximul. Timpul de execuie este situat n intervalul [mn , Mn]. Metoda backtracking are complexitatea exponeial, n cele mai multe cazuri fiind ineficient. Ea ins nu poate fi nlocuit cu alte variante de rezolvare mai rapide n situaia n care se cere determinarea tuturor soluiilor unei probleme. Exemplu - Problema 7. Pe o tabl de ah de dimensiune n x n, n>3, trebuie aezate n dame astfel nct s nu se atace ntre ele. S se afieze toate posibilitile de a realiza acest lucru. Aceast problem se poate rezolva usor cu metoda backtracking. Ideile principale ale algoritmului sunt: 1. Se caut un loc pe o coloan de pe linia i, se gsete primul loc n care se poate pune o dam i se trece la linia i+1 i coloana 1 de pe aceasta. 2. Dac pe coloana i nu s-a gsit un loc unde se poate pune o dam (este atacat de altele) atunci se rentoarce pe linia i-1 i se caut un alt loc de pe aceasta care ndeplinete condiiile i apoi se trece din nou la linia i i coloana 1. #include<iostream> #include<math.h> using namespace std; int n,sol,st[25],v[25]; void initializari() { int i; cout<<"Introduceti n = ";cin>>n; for(i=1;i<=25;i++) st[i]=0;

sol=0; } void tipar(int p) { int i,j; sol++; cout<<"Solutia numarul "<<sol<<" este : "<<endl; for(i=1;i<=p;i++) { for(j=1;j<=p;j++) if(j==st[i]) cout<<" * "; else cout<<" o "; cout<<endl; } cout<<"==================="<<endl; } int valid(int p) { int i,ok; ok=1; for(i=1;i<p;i++) if(st[p]==st[i]||abs(st[p]-st[i])==abs(p-i)) ok=0; return ok; } void bktr(int p) { int pval; for(pval=1;pval<=n;pval++) { st[p]=pval; if (valid(p)) if (p==n) tipar(p); else bktr(p+1); } } void main() { initializari(); bktr(1); }