P. 1
Cap 3. Subprograme Recursive

Cap 3. Subprograme Recursive

|Views: 975|Likes:
Published by bblondd

More info:

Published by: bblondd on Jan 11, 2011
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

06/09/2013

pdf

text

original

3 Subprograme recursive

Recursivitatea este tehnica de programare în care un subprogram se autoapelează. Limbajul C face parte din clasa limbajelor de programare care admit scrierea de funcţii recursive. În continuare sunt prezentate câteva exemple simple de subprograme C prin intermediul cărora sunt calculate recursiv valorile k n! , C n , f ο g ο f , unde n , k ∈ N şi f, g funcţii, f , g : R → R. De asemenea, este ilustrată maniera în care sunt efectuate apelurile recursive şi tratarea condiţiilor terminale.

3.1 Calcul recursiv
Calculul valorii n! pentru n dat poate fi efectuat pe baza formulei recursive

Fie fact(n) funcţia C care calculează n!. Dacă n ≥ 1, evaluarea lui fact(n) rezultă prin multiplicarea cu n a valorii calculate de apelul fact(n-1), cu fact(0)=1. Cu alte cuvinte, apelul funcţiei fact(n) realizează calculul „imediat” dacă n=0, altfel presupune un nou apel al aceleiaşi funcţii pentru valoarea argumentului decrementată. Cazurile în care este posibilă evaluarea „imediată” se numesc condiţii terminale. În limbajul C, funcţia fact este
long fact(unsigned n) { if (!n) return 1; return n*fact(n-1); }

⎧1, n = 0 n! = ⎨ . ⎩n(n − 1)! , n > 0

Utilizarea formulei

k Cn =

n! k ! (n − k )!

pentru calculul combinărilor

( n , k ∈ N date) este ineficientă şi uneori imposibilă deoarece n!, pentru n ≥ 13 nu

dacă este îndeplinită o condiţie terminală.k-1). În continuare execuţia determină fact(0)=1.k) şi comb(n-1. eliberându-se spaţiul ocupat. De exemplu. unsigned k) { if (k>n) return 0. fact(1). Apelurile recursive ale unui subprogram S1 pot fi şi indirecte.1.0)=1.k) revine la însumarea rezultatelor obţinute prin apelurile comb(n-1. în cazul apelului fact(3). Schema unui apel recursiv poate fi descrisă astfel: se verifică dacă este îndeplinită cel puţin una din condiţiile terminale. fact(2)=2*fact(1)=2. k-1). În cazul subprogramelor recursive. De . k Soluţia recursivă a evaluării C n este: long comb(unsigned n. Conform relaţiei de recurenţă.b. atunci calculul este încheiat şi controlul este returnat unităţii apelante. Dacă evaluările comb(n-1. a adresei de revenire şi a variabilelor locale. unde n . Fiecare apel determină introducerea în stivă a valorilor parametrilor formali. până când este îndeplinită una din condiţiile terminale comb(n. unde (○) reprezintă adresa de revenire în punctul de unde a fost efectuat apelul fact(3). rezultă că apelul comb(n.k) va determina o secvenţă de apeluri ale aceleiaşi funcţii pentru valori ale argumentelor din ce în ce mai mici. La momentul execuţiei.1.k)=1. comb(k.a şi 3. if ((k==0)||(k=n)) return1. unde comb(n. Pe baza k k k −1 k relaţiei de recurenţă C n = C n −1 + C n −1 . C n . în sensul că este efectuat un apel al unui alt subprogram S2 şi S2 iniţiază un apel al lui S1.k)+comb(n-1.0)=1. n ≥ 0. valoarea C n poate fi calculată astfel. fact(0). secvenţa de apeluri recursive este: fact(2).k) funcţia care calculează C n . în caz contrar este iniţiat calculul pentru noile valori ale parametrilor. mecanismul funcţionează astfel: este generat un număr de apeluri succesive cu ocuparea spaţiului din stivă necesar efectuării apelurilor până la îndeplinirea unei condiţii terminale. fact(3)=3*fact(2)=6. aceste informaţii sunt extrase cu eliminare din stivă.k) şi comb(n-1. Fie k comb(n. k ∈ N realizează apeluri recursive directe. atunci evaluarea corespunzătoare apelului comb(n. iar operaţia de inserare în stivă poate produce depăşirea spaţiul de memorie rezervat. calcul care presupune unul sau mai multe apeluri recursive. apelurile sunt executate în ordinea inversă celei în care au fost generate. fact(1)=1*fact(0)=1. Mecanismul prin care este efectuat apelul unui subprogram se bazează pe utilizarea stivei memoriei calculatorului. chiar dacă numărul C n este relativ mic şi poate fi reprezentat prin intermediul unui tip întreg. dacă n ≥ k ≥ 1. return comb(n-1. Evoluţia determinată de apelul fact(3) în stivă este ilustrată în figurile 3. k-1) sunt realizate în acelaşi mod. } k Funcţiile C recursive pentru calculul n! .Subprograme recursive k poate fi reprezentat în calculator ca dată de un tip întreg.

Pentru funcţiile f.Programarea calculatoarelor exemplu.1. calculul valorilor funcţiei h=f◦g◦f . unde f.g:R→R sunt funcţii date poate fi descris astfel. g definite prin: 3 Fact=3*Fact(2) 2 Fact=2*Fact(1) 3 (o) Fact=3*Fact(2) (o) 1 Fact=1*Fact(0) 2 Fact=2*Fact(1) 0 Fact=1 1 Fact=1*Fact(0) 2 Fact=2*Fact(1) 3 Fact=3*Fact(2) 3 (o) Fact=3*Fact(2) (o) Figura 3.a Evoluţia în stivă până la verificarea condiţiei terminale n=0 .

if (x<8) return pow(x. return pow(x. x ≤ 1 f (x ) = ⎨ x 4 + 2 .Subprograme recursive 1 Fact=1 2 Fact=2*Fact(1) 2 Fact=2 3 Fact=3*Fact(2) 3 Fact=3*Fact(2) (o) 3 Fact=6 (o) Figura 3. 5 ≤ x < 8 . x > 8 ⎩ ⎩ funcţiile C pentru calculul h=f◦g◦f pot fi descrise astfel. x < 5 ⎧ 2 ⎪ ⎪5 x − 3 x + 2 .4)+2.3)-x+5.3)+1. return 3. float f(float x) { if (x<5) return 2*pow(x.b Eliberarea stivei după execuţia determinată de condiţia terminală ⎧2 x 3 + 1. } float h(float x) { return f(g(f(x))).1. } . g (x ) = ⎨ 3 ⎪x − x + 5. } float g(float x) { if (x<=1) return 5*x*x-3*x+2. x > 1 ⎪3 .

n parametri naturali daţi. conform definiţiei următoare.Programarea calculatoarelor 3. } . printf( “%s\n“.2 Aplicaţii cu subprograme recursive 1. long cmmdc(long a. a > b ⎨ ⎪( a . unsigned n) { if (!m) return n+1.b − a ). n ) = ⎨a (m − 1. an terminată cu simbolul # şi afişează anan-1…a1. long b) { if (a==b) return a.“#“)) { Scrie. m = 0 ⎪ a (m . if (strcmp(cuvant. } 3. a = b (a . Funcţia Ackermann este definită pentru argumentele m. a2.b ). scanf(“%s“. Pentru rezolvarea problemei se utilizează funcţia recursivă Scrie.&cuvant).. ⎧a . void Scrie() { char cuvant[100].n numere naturale prin ⎧n + 1. return Ackermann(m-1. n = 0 ⎪a (m − 1.cuvant). } } 2.b ) = ⎪( a − b . n − 1)). altfel ⎩ Funcţia C Ackermann calculează valoarea funcţiei a pentru m. b > a ⎩ Funcţia C cmmdc(a.1). if (!n) return Ackermann(m-1.Ackermann(m.b) este.b-a). a (m .n-1)).b).1). …. Să se scrie o funcţie C care citeşte o secvenţă oarecare de cuvinte a1. if (a>b) return cmmdc(a-b.. Calculul valorii funcţiei Ackermann. long Ackermann(unsigned m. Problema calculului celui mai mare divizor comun dintre două numere naturale a şi b poate fi rezolvată recursiv. return cmmdc(a.

c. Problema poate fi enunţată astfel: se presupune că există trei tije a. Soluţia recursivă este prezentată în funcţia Hanoi. b. Exemplu Presupunând că discurile sunt numerotate în ordinea crescătoare a diametrelor cu etichetele 1. 2. pentru rezolvarea ei putem raţiona în modul următor.b).h> void Hanoi(unsigned n.h> #include <conio.unsigned a. Notând cu P(n.Subprograme recursive 4.c) problema transferului celor n discuri de pe tija a pe tija c. Problema turnurilor din Hanoi ilustrează foarte bine avantajele recursivităţii. • tija b poate fi folosită pentru deplasări intermediare. o soluţie a problemei pentru n=3 poate fi descrisă astfel.unsigned c) { . unsigned b.a. atunci discul de diametru maxim care se află încă pe tija a este deplasat pe tija c şi în continuare se rezolvă problema P(n-1.a. Se cere ca cele n discuri de pe tija a să fie deplasate pe tija c astfel încât să fie îndeplinite condiţiile: • la fiecare mutare este deplasat unul dintre discurile aflate pe poziţia superioară pe una din tije. Tija a 1 2 3 2 3 3 3 Tija b Tija c Mutarea efectuată a⇒c a⇒b c⇒b a⇒c b⇒a b⇒c a⇒c 1 2 1 2 1 2 2 1 3 3 2 3 1 2 3 1 1 #include <stdio. pe tija a fiind plasate n discuri de diametre diferite în ordinea descrescătoare a acestora. 3.b. Dacă s-a rezolvat problema P(n-1. • oricare din discuri poate fi aşezat numai pe un disc de diametru mai mare.c).

printf("n="). scanf("%i".c. void main() { clrscr(). printf("Elementele vectorului\n"). if(v[mij]==k) ⎡n⎤ . astfel încât v[poz]=k.a.h> #include <conio.3). #include <stdio. Căutarea în vectori sortaţi (căutarea binară) Fie v este un vector de numere reale sortat crescător şi k este un număr real dat.b.b. if(c==-1) printf("Cheia nu a fost gasita"). for(unsigned i=0.int.b).a.&k).1.…. Se generează astfel subvectori din ce în ce mai mici până când valoarea k este găsită sau până când nu mai poate fi generat un nou subvector. float v[100].scanf("%u".c.k). scanf("%f".2.int ls. Rezolvarea ei porneşte cu luarea în considerare a întregului vector v şi determinarea poziţiei mijlocului m = ⎢ ⎥ .&v[i]).c).&n). } int cauta_binar(float *v. Problema este de a identifica (dacă există) o valoare poz.c. atunci poz:=m.v[n-1]. Hanoi(n. Dacă v[m]=k. else printf("Cheia pe pozitia:%i".i++) scanf("%f".n-1.a. Hanoi(n-1.i<n.int li.float).b).int.Programarea calculatoarelor if(n>0) { Hanoi(n-1. float k. int c=cauta_binar(v. } } void main() { unsigned n. altfel cu cel format din componentele v[m+1].a). int mij=(li+ls)/2.&n).0. printf("Cheia de cautare:").h> int cauta_binar(float *. } 5.float k) { if(li>ls) return -1. printf("Transfer disc de pe tija %u pe tija %u\n".getch(). printf("Dimensiunea vectorului:"). int n. clrscr(). atunci se procedează în acelaşi mod cu vectorul format din primele m componente din v. getch(). Dacă ⎣2⎦ v[m]>k.

Funcţia insera realizează inserarea valorii x în vectorul v în poziţia „corectă”. H2 se poate obţine prin interconectarea a patru cópii ale lui H1 rotite cu unghiuri drepte şi prin interconectarea punctelor izolate prin segmente de aceeaşi lungime. instanţe. Compunerea constă în repetarea primitivelor considerate şi a rezultatelor obţinute prin rotirea lor într-un sens sau celălalt.n-1).&m... a2. Pot fi realizate desene prin compunerea într-o manieră recursivă a unor figuri geometrice primitive. Fiecare problemă intermediară P(k).mij-1. clone) de primitive din H0 unite prin segmente de lungime h. insera(v.Subprograme recursive return mij. Funcţia recursivă inssort realizează sortarea vectorului cu n componente prin inserţie..v[n-1]). } void inssort(float *v.ls.int m=n-1. atunci soluţia problemei P(n) rezultă din soluţia problemei P(n-1) prin inserarea lui an în soluţia problemei P(n-1).a. } } 7. atunci: H1 rezultă din patru exemple (cópii. realizări. Curbele rezultate se numesc curbele Hilbert Hi.(i<*n)&&(x>v[i]). return cauta_binar(v.m.li. i ≥ 0.an şi P(n-1) este problema sortării primelor n-1 componente. Generalizând. for(int j=*n.mij+1.float x) { for(int i=0. . k = 2 . } 6.j--)v[j]=v[j-1].int n) { if(n) { inssort(v. De asemenea.…. o curbă Hn rezultă din patru cópii ale unei curbe Hn-1. void insera(float *v. H2 rezultă din 16 exemple din H0 unite prin 15 segmente de lungime h/2 ş.i++). punctele izolate fiind unite prin segmente de lungime hn=h/2n. dacă mulţimea de primitive H0 constă dintr-un punct şi pentru compunere este considerat un segment de lungime h. if(v[mij]>k) return cauta_binar(v. v[i]=x.int *n. Sortarea crescătoare prin inserare Pentru sortarea crescătoare a unei secvenţe de numere reale se poate raţiona astfel: dacă P(n) este problema sortării crescătoare a secvenţei a1..k).(*n)++. Astfel.k). n este rezolvată aplicând aceeaşi metodă P(1) fiind o problemă „gata rezolvată” (condiţie terminală).j>=i+1.d.

x. C(int). h.y=y0."D:\BC\BGI").h> <stdlib. do{ i++. B(int).&gm. const h0=480. D(int). B. x=x0. setcolor(4).x0. x0+=h/2. } . initgraph(&gd. void main() { clrscr().y0+=h/2.gm. D şi se reprezintă prin săgeţi rutinele care desenează segmentele care le interconectează.y0. #include #include #include #include <stdio. h=h0.Programarea calculatoarelor H1 H2 H3 Dacă cele patru părţi ale unei curbe Hilbert Hk sunt notate A.y0=x0=h/2. C.moveto(x. setbkcolor(0).h/=2.y. A(i). atunci rezultă următoarele scheme recursive.h> <graphics. A: D ← A↓ A→ B B: C↑B→B↓ A A: D ← A↓ A→ B C: B→C ↑C ←D D: A↓ D← D↑C Prin executarea următoarei surse C sunt obţinute curbele Hilbert H4.h> const n=5.h> <conio. int int int int void void void void i=0.y). gd=DETECT. A(int).

C(i-1). closegraph(). } void A(int i) { if (i>0) { D(i-1).x-=h. } } .lineto(x. } } void D(int i) { if (i>0) { A(i-1).lineto(x.y).lineto(x.y+=h.y+=h.x+=h.y).lineto(x.x+=h. A(i-1). A(i-1). } } void C(int i) { if (i>0) { B(i-1).y+=h.y). getch().lineto(x.x+=h.lineto(x. D(i-1). D(i-1).Subprograme recursive while(i<n). B(i-1).y).lineto(x. B(i-1). } } void B(int i) { if (i>0) { C(i-1).y).y-=h.lineto(x.y-=h. C(i-1).y). B(i-1).lineto(x.y). A(i-1).y).y).lineto(x.y-=h.x-=h.x-=h.lineto(x. D(i-1).y). C(i-1).y).y).lineto(x.

rezultă curbele Sierpinski Sn. toate unghiurile determinate de segmentele care unesc punctele sunt de măsură 900. Dacă se consideră ca valori pentru măsurile unghiurilor determinate de aceste segmente 450.Programarea calculatoarelor Curba Hilbert obţinută este 8. Curba Sierpinski S2 este. n ≥ 1. Recursia pentru obţinerea curbelor Sierpinski poate fi descrisă astfel. S: A A: A B C B⇒D D A . 900. În cazul curbelor Hilbert. 1350.

x+=h. getch().h> const n=4.lineto(x.y)."d:\bc\bgi"). void D(int).y-=h. lineto(x.lineto(x.x-=h. const h0=412. lineto(x. x0-=h.y0+=h.x+=2*h.x-=h.y). x0=2*h. void C(int). B(i-1). void main() { clrscr().y0. x=x0. do{ i++. setbkcolor(15). setcolor(8).y. initgraph(&gd. void A(int). h=h0/4.h> <graphics.y). } void A(int i) { if (i>0) { A(i-1).h> <conio.x0.&gm. C(i).y=y0. A(i).y).lineto(x.y+=h.gm. #include #include #include #include <stdio. Următorul program desenează curbele Sierpinski S4.h/=2. moveto(x. int x.} while(i!=n).x+=h. int h. D(i).y).x+=h. int gd=DETECT. B(i). void B(int).lineto(x. y0=3*h.Subprograme recursive B: B C: C D: D C⇓ A D⇐B A⇑ C B C D unde săgeţile duble indică segmente de lungime 2h.y). closegraph(). .y-=h. int i=0.y+=h.h> <stdlib.y).y-=h.

y). } } void D(int i) { if (i>0) { D(i-1).x-=h.x+=h. lineto(x.x+=h. C(i-1). D(i-1).x-=h. lineto(x. . lineto(x.y+=h. lineto(x. } } Rezultatul execuţiei programului este prezentat în următoarea figură.y+=h.y).y).C(i-1).x+=h.y-=h.y).y+=2*h. B(i-1). } } void B(int i) { if (i>0) { B(i-1). } } void C(int i) { if (i>0) { C(i-1).y). y-=2*h. lineto(x.y).x-=h.x-=2*h.y-=h. lineto(x.x-=h. lineto(x.Programarea calculatoarelor D(i-1). A(i-1).y). lineto(x.y). A(i-1).y-=h.y). B(i-1). A(i-1). lineto(x. D(i-1).y).y+=h. lineto(x. C(i-1).y+=h.

Subprograme recursive .

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->