Sunteți pe pagina 1din 37

Tehnici de programare

1.Recursivitate

1 14/05/2012

Structura semastrului II: -14 saptamani -1 saptamana vacanta de pasti (dupa) -4 saptamani sesiunea iunie-iulie

Structura disciplinei:
- 2 ore curs/saptamana -2 ore/saptamana laborator/subgrupa- obligatoriu

-1 ora proiect/grupa saptamanal


OBS. Lucrarile de laborator se recupereaza la anul in sem II

Calculul notei la examen NF=0.1*TC+0.1*Test+ 0.7*NEX+1of TC,Test,NEX>=5 Se poate da ex. partial


2 14/05/2012

Bibliografie
1.Waite M., Prata St., Martin D. ,C Primer Plus, Howard W. Sans & C0, 1986 2.Jamsa K., Klander L.- Totul despre C si C++. Manual fundamental de programare in C si C++. Teora 2002 3.Schildt H. C manual complet. Teora 1998 4. Schildt H. C++ manual complet. Teora 1998 5.Kernighan B., Ritchie D. The C Programming Language, Printice Hall,1988 6.Cristea V., Kalisz E., Giumale C., Panoiu F.- Limbajul C standard, Teora 1992 7.Marian Gh., si alti- Modele de grile pentru examenele de diploma si absolvire, Editura Universitaria 2004 ISBN 973-8043-503-3 8.Patrascoiu O., Marian Gh., Mitroi N. Elemente de grafuri si combinarorica. Metode, algoritmi si programe, Editura ALL 1993 ISBN 973-571-017-X 9. Patrascoiu O., Mitroi N. , Marian Gh.- Limbajul C, Teorie si programe, Editura Microcomputer Service, Craiova, 1994. 10.Petrovici V., Goicea F. Programare in limbajul C, Ed. Tehnica 1988 11.Marian Gh., Badica C., Lascu M.,Iordache S.-Limbajul C-Aplicatii, Editura Spirit Romanesc, Craiova 1997 12.Mocanu M., Marian Gh., Badica I., C., Badica P., C. - 333 probleme de 3 programare, Editura TEORA, Bucuresti, 1993. 14/05/2012

Introduction
Defintii Notiunea de recursivitate din programare este derivata in mod natural din notiunea de recusivitate din matematica, unde o notiune este definita recursiv daca in cadrul definitiei sale apare insasi notiunea ce se defineste. Exemple: -factorial

1 n!=

daca n=0

n *(n-1)!

daca n>0

4 14/05/2012

-sirul fibonacci f(0)=0, f(1)=1, f(i)=f(i-1)+f(i-2) pentru i>1.


-cel

mai mic divizor comun

a
cmmdca,b)=

daca a=b

cmmdc(b,a mod b)daca a<> b Etc.


5 14/05/2012

In programare, instrumentul necesar si suficient pentru a exprima programe recursive este subprogramul pentru ca subprogramul da un nume unei actiuni care poate fi invocata prin numele respectiv. Recursivitatea poate fi directa atunci cand in corpul subprogramului apare apelul acelui subprogram, sau indirecta daca apelul apare in corpul altui subprogram care se apleleaza din primul subprogram. Autoapelul provoaca o noua activare a aceluiasi subprogram, ceea ce presupune executia instructiunilor subprogramului, incepand cu prima instructiune a acestuia si pana la autoapel, cand se activeaza din nou subprogramul s.a.m.d. Ca atare, partea de inceput a subprogramului se poate executa de o infinitate de ori. Pentru a se evita o asemenea situatie trebuie ca autoapelul subprogramului sa fie conditionat de indeplinirea unei anumite conditii. Daca conditia de autoapel (C) este indeplinita, atunci subprogramul este reactivat (reapelat), in caz contrar sirul de activari (apeluluri) ale subprogramului se intrerupe si se executa secventele ramase de executat din subprogram, activate in ordine inversa activarii.

6 14/05/2012

C=adevarat apel subprogram (2) S (1) (3) apel S (4)

C=adevarat (5) (6)

C=adevarat (7) apel S (8) (9)

Revenire in Programul apelant


(17)

daca C atunci S (16) (15)

apel S daca C atunci S (14) Subprogram S (13)

daca C atunci S

daca C atunci S C=fals (11)


(10) Subprogram S

(12)

Subprogram S

Subprogram S

7 14/05/2012

In figura 1.2 se prezinta schematic executia unui subprogram indirect recursiv S, subprogram ce este reapelat prin intermediul subprogramului S1.

Figura 1.2.
C=adevarat (2) S1 revenire in programul apelant (17) Subprogram S Subprogram S1 Subprogram S Subprogram S1
8 14/05/2012

apel subprogram S (1)

(3)

(4) daca C atunci S

(5)
apel S S1

(6)

(7) apel S1

(8) C=fals daca C atunci S (9) (10)

apel S1

(16)

(15)

(14)

(13)

(12)

(11)

Apelul unui subprogram recursiv, ca si al unuia nerecursiv presupune alocarea de memorie pe stiva a variabilelor locale, a parametrilor si a adresei de revenire. Ca atare pentru a nu se ajunge in situatia depasirii stivei se recomanda utilizarea unei solutii recursive numai daca numarul autoapelului (adancimea recursivitatii) nu este mare. In cazul utilizarii recursivitatii se mai recomanda utilizarea variabilelor globale pentru a se evita crearea unor dubluri de variabile pe stiva. Avantajul recursivitatii este acela ca: - reduce lungimea textului sursa; - conduce la solutii clare in comparatie cu solutiile iterative. Recursivitatea se recomanda: - in problemele ale caror solutii pot fi definite in termeni recursivi; - in cazul problemelor complexe rezolvate prin backtracking. Intotdeauna o problema recursiva are si o rezolvare iterativa si invers. Utilizarea tehnicilor recursive nu conduce intotdeauna la solutii optime. Ca atare, se recomanda o analiza a eficientei solutiei recursive si a celei nerecursive si alegerea solutiei celei mai avantajoase

9 14/05/2012

Exemplul 1: Sa se calculeze n! intr-o varianta iterativa si alta recursiva. Se stie ca : 1 n!= n.(n-1)! daca n>0 Pseudocodul pentru varianta iterativa si cea recursiva: functia FACT1(i) este: {iterativ} f:=1; pentru i=1,n,1 repeta f=f*i FACT1:=f sfarsit functia FACT2(i) este: {recursiv} daca i>0 atunci FACT2=i*FACT2(i-1) altfel | FACT2=1 Sfarsit citeste n pentru k=1,n,1 repeta f1=FACT1(k); f2:=FACT2(k); scrie f1,f2 stop daca n=0

10 14/05/2012

Programul C: #include <stdio.h> /* functia recursiva*/ int factr(int i) { if (i>0) return i*factr(i-1) else return 1; } /* functia iterativa*/ int facti(int i{ int p,k; p=1; for (k=1; k<i; k++) p=p*k; return p; } void main(){ printf( Factr( %d)=%d\n Facti(%d)=%d\n, 5,factr(5),5,facti(5) }

11 14/05/2012

Exemplul 2: Sa se genereze un sir de cifre reprezentand scrierea rasturnata a unui numar intreg si pozitiv intr-o varianta iterativa si una recursiva. Algoritmul de calcul se bazeaza pe aflarea cifrelor numarului prin impartiri la 10. procedura INV1(n)este: {iterativ} repeta scrie n mod 10 n=n div 10; pana n=0 sfarsit procedura INV2(n)este: {recursiv} scrie n mod 10 daca (n div 10)<>0 atunci INV2(n div 10) sfarsit Programul principal: n=12345 INV1(n); INV2(n); stop
12 14/05/2012

Programul in C: #include<stdio.h> const n=1234; /*functia iterativa*/ void invi(int n){ do{ printf("%d",n%10); n=n/10; }while(n>0); } /*functia recursiva*/ void invr(int n) { printf("%d", n%10); if(n/10!=0) invr(n/10); } void main() { invr(n); printf("\n"); invi(n); }

13 14/05/2012

Exemplul 3. Sa se determine cel mai mare divizor comun a doua numere folosind o varianta recursiva si una iterativa pentru algoritmul lui Euclid. In varianta iterativa se va folosi algoritmul lui Euclid in varianta clasicia iar in varianta recursiva se va folosi definitia: a daca a=b cmmdc(a,b)= cmmdc(b,a mod b) daca a<> b functia CMMDCR(a,b) este: daca b=0 atunci CMMDC=a altfel CMMDCR=CMMDCR(b,a mod b); sfarsit functia CMMDCI(a,b) este: daca a<b atunci r=a; a=b; b=r; r= a mod b; cat timp r<>0 executa a=b; b=r; r= a mod b; 14 CMMDCI:=a; 14/05/2012 sfarsit

Programul principal: scrie dati numerele:' citeste a,b c:=CMMDCR(a,b); scrie'CMMDCR=',c c:=CMMDCI(a,b); scrie 'CMMDCI=',c stop

15 14/05/2012

Programul in C: #include <stdio.h> #include <conio.h> /* functia recursiva*/ int cmmdcr(int a,int b){ if (b==0) return a; else return cmmdcr(b,a%b); } /* functia iterativa*/ int cmmdci(int a, int b){ int r; r=a%b; while (r!=0){ a=b; b=r; r=a%b; } return b; } void main(){ clrscr(); printf(" Cmmdcr( %d,%d)=%d\n Cmmdci(%d,%d)=%d",72,48, Cmmdcr(72,48),72,48,cmmdci(72,48)); getchar(); }

16 14/05/2012

. Sa se calculeze primele n numere din sirul lui Fibonacci folosind o varianta recursiva si una iterativa. Sirul numerelor lui Fibonacci 0, 1, 1, 3, 5, 8, 13, 21, ... sunt definite recursiv astfel: f(0)=0, f(1)=1, f(i)=f(i-1)+f(i-2) pentru i>1. functia fiboi (n) este: // varianta iterativa i=1 a=0 b=1 daca(n<=1) atunci fiboi=n altfel cat timp (i<=n) repeta c=a+b a=b b=c i=i+1 fiboi=c stop functia fibor (n) este: // varianta recursiva daca(n<=1) atunci fibor=n altfel fibor=fibor(n-1)+fibor(n-2) stop citeste n pentru i=1,n,1 repeta scrie fiboi(i), fibor(i) stop

17 14/05/2012

Programul in C: #include <stdio.h> int fiboi(int n); int fibor(int n); void main(){ int n,i; printf("dati n"); scanf("%d",&n); printf(" i fiboi(i) fibor(i)\n\n"); for (i=0;i<=n;i++) printf(" %d %d %d\n",i,fiboi(i),fibor(i)); getch(); } int fiboi(int n){ int i=1,a=0,b=1,c; if(n<=1) return n; else{ while(i<n){ c=a+b; a=b; b=c; i++; } return c; } } int fibor(int n){ if(n<=1) return n; else return fibor(n-1)+fibor(n-2); }

18 14/05/2012

Observatie. De obicei se asociaza o multime de entitati locale unui subprogram, adica variabile, constante, tipuri care sunt definite local in acel subprogram si care nu au nici o semnificatie in afara ei. In acest caz de fiecare data cand un asemenea subprogram este activat recursiv, se creaza un nou set de variabile locale. Desi noile variabile au acelasi nume cu cele existente inainte de activarea subprogramului, valorile lor sunt distincte si orice conflict de nume este evitat de regulile domeniului de valabilitate al identificatorilor: Identificatorii se refera intotdeauna la ultimul set de variabile creat. Aceleasi reguli se refera si la parametrii de tip valoare ai subprogramului, care sunt inglobati prin definitie in subprogram. Este indicat pe cat posbil a nu se folosi variabile locale in subprograme recursive pentru a nu se depasi stiva procesorului.

19 14/05/2012

Exemplul 5. Sa se afiseze in ordine inversa un sir de caractere de lungime arbitrara, terminat prin spatiu. Algoritmul recursiv in pseudocod:: procedura INVSIR() este: citeste ch daca ch<> atunci INVSIR(); scrie ch sfarsit Programul principal: scrie sirul este INVSIR() stop Programul in C: #include <stdio.h> #include<conio.h> void invsir(){ char ch; if((ch=getchar())!=' ') invsir(); putchar(ch); } void main(){ printf("Dati sirul terminat cu spatiu liber:\n"); invsir(); }

20 14/05/2012

Pentru fiecare apel al procedurii recursive INVSIR se creaza o copie a variabilei locale "ch". Recursivitatea poate fi transformata in majoritatea cazurilor intr-o iteratie. In general, forma nerecursiva a unui program este mai eficienta decit cea recursiva in ceea ce priveste timpul de executie si memoria ocupata. In alegerea caii recursive sau iterative de a rezolva o problema, programatorul trebuie sa-si stabileasca bine prioritatile in alcatuirea programului, analizand complexitatea acestuia, naturaletea exprimarii, usurinta proiectarii si intretinerii programului, eficienta in executie. Daca problema de rezolvat este de complexitate redusa si programul trebuie sa aiba eficienta maxima, iar transformarea recursivitatii in iteratii se face usor fara a dauna asupra naturaletii algoritmului, este de preferat varianta iterativa (ca in exemplele de mai sus). In general, clasa de functii definite recursiv: G(x) , daca n=0 Fn(x)= H * Fn-1(x) , daca n>0 se poate exprima iterativ, solutia recursiva nefiind absolut necesara. Pentru a nu se crea la fiecare apel recursiv multe copii de variabile locale, se indica, atunci cand acest lucru este posibil, folosirea variabilelor declarate global. 21
14/05/2012

Astfel, in programul urmator care determina recursiv maximul unui sir, variabilele k, j, s-au declarat globale si nu locale. Exemplul 6. Sa se determine maximul unui sir de numere intregi folosind o solutie recursiva. functia maxr(i) este: daca i<n atunci daca a[i]>a[j] atunci k=i altfel k=j altfel k=n maxr=k sfarsit Programul principal: citeste n citeste a(i),i=1,n scrie "Rangul elementului maxim=,maxr(1) scrie Valoarea sa este, a[maxr(1)] 22 stop 14/05/2012

Programul in C:

#include <stdio.h> #define NMAX=6; int a[6]={1,4,2,5,9,15}; int i,j,k,n=6; int maxr(int i){ if (i<n-1){ j=maxr(i+1); if (a[i]>a[j]) k=i; else k=j; } else k=n-1; return k;
} void main(){ printf("Rangul elementului maxim=%d\n Valoarea sa este %d\n", maxr(2)+1,a[maxr(2)]); 23 } 14/05/2012

Exemplul 7. O functie recursiva este adecvata in problema partitiilor unui numar natural n. Prin partitiile unui numar natural n, se intelege totalitatea posibilitatilor de exprimare ale unui numar natural n ca o suma de alte numere naturale, fiecare dintre ele nedepasind o valoare data m. 1, daca m=1 sau n=1 P(n,m)= 1+P(n,n-1) daca n<=m P(n,m-1)+P(n-m,m) daca n>m De exemplu partitiile numarului 5 din numere ce nu depasesc 3 se vor calcula astfel: de partitii, valoarea functiei este data de urmatoarea regula: Atasand o functie numarului P(5,2)=P(5,1)+P(3,2) P(5,1)=1 P(5,3)=P(5,2)+P(2,3) P(3,2)=P(3,1)+P(1,2) =>P(5,3)=1+1+1+1+1=5 P(3,1)=1 P(1,2)=1 P(2,3)=1+P(2,1) P(2,1)=1 Intr-adevar, exista 5 posibilitati de a exprima numarul 5 ca sume de numere naturale ce nu depasesc valoarea 3: 1+1+1+1+1 , 1+1+1+2 , 1+1+3 , 1+2+2, 2+3. In timp ce proiectarea unui algoritm nerecursiv pentru calculul acestei functii este destul de greoaie, necesitand crearea unei stive pentru memorarea 24 argumentelor si valorilor functiei, varianta recursiva este imediata. 14/05/2012

Algoritmul in pseudocod:
functia PART(n,m) este daca (m=1) or (n=1) atunci PART=1 altfel daca n<=m atunci PART=1+PART(n,n-1) altfel PART=PART(n,m-1)+PART(n-m,m); sfarsit citeste n,m scrie (PART(n,m)); stop Programul in C: #include <stdio.h> #include<conio.h> int partitii(int n, int m){ if((m==1) || (n==1)) return 1; else if (n<=m) return (1+partitii(n,n-1)); else return ( partitii(n,m-1)+partitii(n-m,m)); } void main(){ int n,m; printf("\nDati n si m\n"); scanf("%d",&n); scanf("%d",&m); printf("%d\n",partitii(n,m)); }

25 14/05/2012

2. O alt funcie recursiv cunoscut este funcia lui Ackermann definit astfel: A(0,n)=n+1 A(m,0)=A(m-1,1) A(m,n)=A(m-1,A(m,n-1)) Pentru cteva valori particulare ale argumentelor, valorile acestei funcii sunt cunoscute: A(1,n)=n+2; A(2,n)=2*(n+3)-3; A(3,n)=2n+3-3. Datorit gradului mare de recursivitate a acestei funcii, ea este frecvent folosit ca un criteriu de evaluare a performanelor compilatoarelor, indicnd eficiena mecanismului de apel al procedurilor recursive. Recursivitate indirect n cazul recursivitii indirecte, exist cel puin un apel al unei proceduri care nu a fost nc declarat. Cum n Pascal, declararea procedurilor trebuie s precead folosirea lor, aceast situaie se rezolv printr-o declarare fictiv ce precede declararea propriu-zis i care are forma: antet procedura;FORWARD; Parametrii formali specificai n declaraia fictiv a procedurii sau funciei, nu se vor mai specifica la declararea ei propriu-zis. 26
14/05/2012

Schema general pentru recursivitatea indirecta este: procedure Q (lista de parametrii formali);FORWARD; procedure P (lista de parametrii formali); ............................................. begin ................. Q (lista de parametrii actuali) .................................. end; {P} procedure Q ...................... begin .................. P(lista parametrii actuali) ................................. end; {Q} In Pascal.
27 14/05/2012

In C problema recursivitatii indirecte se rezolva prin predefinirea functiilor la inceputul programului si definirea lor propriu-zisa dupa definirea functiei main(). Schema general pentru recursivitatea indirecta este: procedure Q (lista de parametrii formali); procedure P (lista de parametrii formali); ............................................. begin ................. Q (lista de parametrii actuali) .................................. end; {P} procedure Q ...................... begin .................. P(lista parametrii actuali) ................................. end; {Q}
28 14/05/2012

Exemplul 1: S se simuleze operaiile unui calculator de birou care calculeaz expresii i livreaz rezultatul. Exemplul ilustreaz maniera de programare TOPDOWN (prin rafinare succesiv). Sintaxa expresiilor care se calculeaz, se exprim n felul urmtor:

29 14/05/2012

30 14/05/2012

Daca se scrie cate o functie pentru expresie, termen, factor se obserca ca functia pentru expresie va apela functia terrmen, functia termen va apela functia factor iar functia factor va apela functia expresie- recursivitate indirecta. Exemple de astfel de expresii de calculat sunt: 130/(20+3*15), 83.5+3*7, 169+2/3; O expresie poate conine termeni aditivi i factori, iar un factor la rndul lui poate fi o expresie i astfel expresiile se definesc recursiv. O prim formulare a algoritmului n pseudocod poate fi: start citete(caracterurmator) ct timp caracterurmator <> ',' si caracterurmator<>'; repeta 1*citete o expresie, calc rezultatul i citete caracterul urmtor n variabila carurm *scrie rezultatul stop.
31 14/05/2012

Rafinnd 1 se obine: procedura CITEXPR (exprcar, valexpr) este 2*citete un termen, calculeaz valoarea lui n valexpr i citete urmtorul caracter n exprcar ct timp (exprcar='+') sau (exprcar='') execut opaditiv<-exprcar citeste exprcar 2* citete un termen, calculeaz valoarea lui n valtermurm i citete urmtorul caracter n exprcar dac opaditiv='+' atunci valexpr <- valexpr+valtermurm altfel valexpr <- valexpr-valtermurm sfrit

32 14/05/2012

Rafinnd 2 se obine: procedura CITERM (termcar, valterm) este 3* citete un factor, calculeaz valoarea lui n valterm i citete urmtorul caracter n termcar ct timp (termcar='*') sau (termcar='/') execut opmul termcar citete termcar 3* citeste un factor,calculeaza valoarea lui n valfacturm i citeste urmatorul caracter n termcar dac opmul='*' atunci valterm<-valterm*valfacturm altfel valterm <- valterm/valfacturm sfrit

33 14/05/2012

Rafinnd 3 se obine: procedura CITNUM (numcar, valnum) este valnum <- 0 ct timp * numcar este cifra execut valnum <- 10*valnum+cifra citete numcar dac numcar='.' atunci citete numcar scara < - 0 ct timp *numcar este cifra execut valnum <- 10*valnum+cifra citete numcar scara scara+1 valnum ,- valnum/10scara sfrit

34 14/05/2012

Trebuie luate n considerare i erorile ce pot apare la introducerea expresiilor. Pot fi depistate 4 tipuri de erori: 1) lipsete paranteza dreapt dup o expresie precedat de o parantez stnga; 2) lipsete virgula dup o expresie; 3) s-a detectat un caracter ilegal ntr-un factor; 4) s-a detectat o mprire la zero; Aceste 4 tipuri de erori se trateaz astfel: 1) Erorile vor fi semnalate prin indicarea poziiei caracterului ce a provocat eroarea. n acest scop, caracterele tastate se numeroteaz i deci pentru citirea unui caracter se va folosi o procedur care numr caracterele citite. Dup semnalarea unei erori se ignor toate caracterele citite. 2) Citirea unui caracter se poate face cu o procedur care s ignore eventualele blancuri ce pot separa caracterele. Astfel, pentru claritate pot fi introduse blancuri n introducerea expresiilor: 5+4* (21+27129) 3) Se permite terminarea unei expresii la sfritul unui rnd. Astfel, dac dup o expresie se detecteaz caracterul eoln, el se modific n virgul. 5+3*8, 21+83*(7+139), 34+91..... 5+3*8 35 14/05/2012 21+83*(7+138)

4)Dac la sfritul tuturor expresiilor se omite ';' i se ntlnete EOF(CTRL+Z), atunci caracterul se transform n ';'. Cu aceste observaii, pentru a citi un caracter se folosete o procedur al crui pseudocod este: procedura CITCAR (ch, pozcar)este repet dac EOF atunci ch ';'; altfel dac EOLN atunci pozcar 0 ch <- ',' * salt la urmtoarea linie altfel pozcar <- pozcar+1 citete ch pn ch <> ' '
36 14/05/2012

37 14/05/2012

S-ar putea să vă placă și