Sunteți pe pagina 1din 14

CURS

Principiul
9
recursivităţii
Sumar

Notiuni generale privind recursivitatea


Aplicaţii rezolvate
Probleme propuse

Noţiuni generale privind recursivitatea


După cum se ştia, limbajul C/C++ permite împărţirea programului creat în părţi mai mici,
numite funcţii. Folosind funcţiile, programele create vor deveni mai uşor de înţeles, de programat şi
de testat. Mai mult de atât, se pot folosi funcţiile create pentru un program în cadrul altui program.
În cursul execuţiei unui program, o funcţie poate apela o altă funcţie, care la rândul ei poate apela o
altă funcţie, s.a.m.d. Într-o astfel de înlănţuire, fiecare funcţie execută o anumită operaţie. Există şi
cazul acceptat de limbaj, în care o funcţie se apelează pe ea însăşi.
O funcţie recursivă este o funcţie care se apelează pe sine însăşi pentru a efectua o anumită
operaţie. Procesul prin care o funcţie se apelează pe sine poartă numele de recursivitate.

Există două tipuri de funcţii (generic denumite subprograme) recursive: -


direct recursive;
- indirect recursive.
Un subprogram S, în corpul căruia apar apeluri la S (la el însuşi) se numeşte subprogram
direct recursiv, iar un subprogram P, pentru care există un subprogram Q, astfel încât P conţine
apeluri la Q iar Q conţine apeluri la P se numeşte subprogram indirect recursiv. Înainte de a intra
în detaliu asupra modului concret de execuţie al unui subprogram recursiv, trebuie amintit că în
majoritatea cazurilor, soluţia iterativă (prin repetiţii) de scriere este preferabilă celei recursive.
Subprogramele recursive sunt mult mai concise, însă mult mai greu de controlat şi depanat (riscul
unor greşeli de concepţie este mare). De asemenea, în cadrul subprogramelor recursive este nevoie
de un spaţiu mai mare de memorie disponibilă, iar timpul de execuţie este mai ridicat în cele mai
multe din cazuri.

1
De regulă se folosesc algoritmi recursivi în cazurile în care calculele aferente sunt descrise în formă
recursivă. Practic, recursivitatea este frecvent folosită în procedurile de prelucrare a structurilor de
date definite recursiv (liste, arbori, etc.).

Pentru a înţelege modul în care se execută un subprogram recursiv trebuie mai întâi cunoscut ce se
întâmplă la apelul unei funcţii oarecare (nerecursive). Implementarea apelurilor de funcţii se face
prin intermediul unei structuri de date internă numită stivă. Prin stivă se înţelege o zonă rezervată de
memorie, în care se poate efectua salvarea temporară a valorilor unei variabile. Extragerea din stivă
se face în ordinea inversă a introducerii datelor, fiecare introducere de informaţie în stivă mărind
dimensiunea stivei şi fiecare extragere reducându-i acesteia dimensiunea. Stiva se completează în
ordinea descrescătoare a adreselor de memorie.

În general, în momentul în care un program P apelează un subprogram S are loc o depunere în stivă
a unei adrese de revenire şi a contextului programului P. Mai precis, adresa de revenire serveşte
controlului execuţiei generale a programului, ea indicând instrucţiunea din programul P ce urmează
să fie executată la sfârşitul subprogramului S. Prin contextul unui subprogram înţelegem totalitatea
variabilelor sale locale. Pe lângă operaţiile de salvare în stivă, la apelul unui subprogram se alocă
spaţiu pentru variabilele locale asociate subprogramului.

Aplicaţii rezolvate
Aplicaţia 1.
Să se rezolve problema calculării lui n!.

a) Varianta iterativă:
#include<stdio.h>

long int factorial(int n)


{
long int f=1;
for(int i=1;i<=n;i++)
f*=i;
return f;
}
/* varianta prin folosirea unei instructiuni repetitive
conditionate: long int factorial (int n)
{
long int f=1; int i=1;
while(i<=n)
{
f*=i;
i++;
}
}
*/

2
void main()
{
int n;
printf("dati n: "); scanf("%d",&n);

if(!n)
printf(“0! = 1\n”);
else
printf("%d! = %ld\n",n,factorial(n));
}

b) Varianta recursivă:
Indicaţie:
1, 1 !
*( 1)!, , 1
==⎨
⎧ ⎩ − ∈≠
dacă n
n
n n dacă n

presupunem: n=3
atunci: 3!=3*2!=3*2*1!=3*2*1
cu alte cuvinte: factorial(3)=3*factorial(2)=3*2*factorial(1)=3*2*1

#include<stdio.h>

long int factorial(int n)


{
if (n==1) return 1;
else return n*factorial(n-1);
}

void main()
{
int n;
printf("dati n: "); scanf("%d",&n);
if(!n)
printf(“0! = 1\n”);
else
printf("%d! = %ld\n",n,factorial(n));
}
Aplicaţia 2.
Să se scrie un program C, pentru rezolvarea cmmdc-ului dintre două numere întregi fără semn
(pentru determinarea cmmdc-ului vom folosi algritmul lui Euclid prin scăderi).

a) Varianta iterativă:
Exemplu:
a=9 b=6
a>b
a=9-6=3 b=6

a=3 b=6
b>a

3
a=3 b=6-3=3

a=3 b=3 => a=b => cmmdc(a,b)=3

#include<stdio.h>

unsigned int cmmdc(unsigned int a, unsigned int b)


{
while(a!=b)
{
if(a>b)
a=a-b; //o alta forma de scriere: a-=b;
else
b-=a;
}
return a; //sau return b; ptr ca ele sunt egale
}

void main()
{
unsigned int x,y;

printf("dati x: ");
scanf("%u",&x);
printf("dati y: ");
scanf("%u",&y);

if(!x || !y) //daca x=0 sau y=0


printf(”cmmdc(%u,%u) = 1\n”,x,y);
else
printf(”cmmdc(%u,%u) = %u\n”,x,y,cmmdc(x,y)); }

b) Varianta recursivă:
Indicaţie:
,
a ab
= ⎪ = −> ⎨⎪
⎧ ⎩−<
( , ) ( , ),
cmmdc a b cmmdc a b b a b
( , ),
cmmdc a b a a b

#include<stdio.h>

unsigned int cmmdc(unsigned int a, unsigned int b)


{
if(a==b) return a; //sau return b; ptr. ca sunt identice
else
if(a>b) return cmmdc(a-b,b);
else return cmmdc(a,b-a);
}

void main()
{
unsigned int x,y;

printf("dati x: ");
scanf("%u",&x);
printf("dati y: ");
scanf("%u",&y);

4
if(!x || !y) //daca x=0 sau y=0
printf(”cmmdc(%u,%u) = 1\n”,x,y);
else
printf(”cmmdc(%u,%u) = %u\n”,x,y,cmmdc(x,y));
}

Aplicaţia 3.
Se va extinde problema 2 pentru determinarea recursivă a cmmdc-ului a n numere întregi fără semn
date de la tastatură.
Indicaţie:
Vom face raportare la funcţia de calcul a cmmdc-ului a două întregi, indiferent de varianta de
scriere aleasă.

#include<stdio.h>

unsigned int cmmdc_2(unsigned int a, unsigned int b)


{
if(a==b) return a; //sau return b; ptr ca sunt identice
if(a>b) return cmmdc_2(a-b,b);
else return cmmdc_2(a,b-a);
}

unsigned int cmmdc_n(unsigned int x[], int n) //x - sirul de elemente, n -


dimensiunea acestuia
{
if (n==2) return cmmdc_2(x[0],x[1]);
else return cmmdc_2(cmmdc_n(x,n-1),x[n-1]);
}

void main()
{
unsigned int x[20];

//numarul de elemente
int n;
printf("dati n: "); scanf("%d",&n);

//citirea elementelor din sir


for(int i=0;i<n;i++)
{
printf("elementul %d= ",i+1);
scanf("%u",&x[i]);
}

if (n==1)
printf("\nCmmdc-ul numerelor: %u",x[0]);
else
printf("\nCmmdc-ul numerelor: %u",cmmdc_n(x,n));
}

Aplicaţia 4.
Se consideră urmăroarele declaraţii şi convenţii:
typedef int vector[20];

5
x – este un vector (şir de elemente)
n – este lungimea sa (n>=1)
Se cere să se scrie funcţii recursive pentru a determina, pentru un vector x de lungime n,
urmatoarele:
a. citirea componentelor şirului
b. afişarea elementelor din şir
c. suma componentelor
d. produsul componentelor
e. numarul componentelor negative
f. produsul componentelor pozitive
g. media aritmetica a elementelor

VARIANTA 1:

#include<stdio.h>

/* un tip propriu definit pentru memorarea sirurilor de elemente intregi, cu o


dimensiune maxima de 20 de componente */
typedef int vector[20];
/*--------------------------------------------------*/
//functia de citire
void citire(vector x,int n) //n este dimensiunea reala a sirului
{
//citim ultimul element din sir
printf("\telementul %d: ",n);
scanf("%d",&x[n-1]);

if(n>=2)
citire(x,n-1); //apelul recursiv al functiei
}

/*--------------------------------------------------*/
//functia de afisare
void afisare(vector x,int n) //n este dimensiunea reala (nr. de elem. din sir) {
//afisam ultimul element
printf("%d ",x[n-1]);

if(n>=2)
afisare(x,n-1); //apelul recursiv al functiei
}

/*--------------------------------------------------*/
//adunarea componentelor unui sir
int suma(vector x,int n) //n in acest caz il consideram ca fiind indice
ultimului element din sir
{
if(n==-1) return 0; //situatia in care nu mai sunt elemente in sir, pozitia
n=-1 nefiind in sir
else return x[n]+suma(x,n-1);
}

6
/*--------------------------------------------------*/
//produsul componentelor
int produs(vector x,int n)
{
if(n==-1) return 1;
else return x[n]*produs(x,n-1);
}

/*--------------------------------------------------*/
//numarul de componente negative
int numar_negative(vector x,int n)
{
//ne pozitionam pe primul element din sir si verificam dc acesta este negativ
if(n==0) return (x[n]<0); //expresia conditionata va returna 1 in caz de adv.
si 0 in caz de fals
else return (x[n]<0)+numar_negative(x,n-1);
}

/*--------------------------------------------------*/
//produsul componentelor pozitive
int produs_pozitive(vector x,int n)
{
if(n==0) return (x[n]>0?x[n]:1); /* am folosit operatorul de conditionare,
care, daca expresia evaluata ca fi adv. se va lua in calcul x[n], altfel,
valoarea 1 */
else return (x[n]>0?x[n]:1)*produs_pozitive(x,n-1);
}
/*--------------------------------------------------*/
//media aritmetica a componentelor sirului
float media(vector x, int m, int n) //cu m am notat indicele elementelor, iar cu
n dimensiunea reala a sirului
{
return (float)x[m]/n + ((m!=0)?media(x,m-1,n):0);
/* - am folosit expresia (float) pentru o conversie explicita a rezultatului
spre un tip real
- prin x[m]/n intelegem un element (in prima faza, acesta fiind ultimul
element din sir) impartit la numarul total de componente */
}

/*--------------------------------------------------*/
//functia principala in rulare
void main()
{
vector x; //sirul de elemente
int n; //dimensiunea sa (numarul de componente citite)

//realizarea operatiei de citire a sirului


printf("Dati numarul de elemente: ");
scanf("%d",&n);
printf("Introduceti elementele sirului:\n");
citire(x,n);

//realizarea operatiei de afisare a sirului


printf("Sirul de elemente este: ");
afisare(x,n);

//sumarea elementelor
printf("\nSuma elementelor: %d",suma(x,n-1)); /* am apelat cu n-1, ptr ca am
spus mai sus ca acest parametru reprezinta indicele ultimului element din sir */

//produsul elementelor
printf("\nProdusul elementelor: %d",produs(x,n-1));

7
//numarul elementelor negative din sir
printf("\nNumarul elementelor negative: %d",numar_negative(x,n-1));

//produsul componentelor pozitive


printf("\nProdusul elementelor pozitive: %d",produs_pozitive(x,n-1));

//media componentelor din sir


printf("\nMedia componentelor din sir: %.2f",media(x,n-1,n)); /* primul
parametru - sirul, al doilea parametru - indicele ultimului element din sir, al
treilea parametru - dimensiunea reala a sirului (numarul de elemente citite) */ }

Dacă se doreşte, aplicaţia de mai sus poate fi scrisă şi asemeni construcţiei de mai jos.

VARIANTA 2:

#include<stdio.h>

/* un tip propriu definit pentru memorarea sirurilor de elemente intregi, cu o


dimensiune maxima de 20 de componente */
typedef int vector[20];
/*--------------------------------------------------*/
//functia de citire
void citire(vector x,int i,int j)
{
printf("\telementul %d: ",j-i+1);
scanf("%d",&x[j-i]);

if(i>=2)
citire(x,i-1,j); //apelul recursiv al functiei
}

/*--------------------------------------------------*/
//functia de afisare
void afisare(vector x,int i,int j)
{
//afisam ultimul element
printf("%d ",x[j-i]);

if(i>=2)
afisare(x,i-1,j); //apelul recursiv al functiei
}

/*--------------------------------------------------*/
//adunarea componentelor unui sir
int suma(vector x,int n)
{
if(n==1) return x[0];
else return x[n-1]+suma(x,n-1);
}

/*--------------------------------------------------*/
//produsul componentelor
int produs(vector x,int n)
{
if(n==1) return x[0];
else return x[n-1]*produs(x,n-1);
}

8
/*--------------------------------------------------*/
//functia principala in rulare
void main()
{
vector x; //sirul de elemente
int n; //dimensiunea sa (numarul de componente citite)

//realizarea operatiei de citire a sirului


printf("Dati numarul de elemente: ");
scanf("%d",&n);
printf("Introduceti elementele sirului:\n");
citire(x,n,n);

//realizarea operatiei de afisare a sirului


printf("Sirul de elemente este: ");
afisare(x,n,n);

//sumarea elementelor
printf("\nSuma elementelor: %d",suma(x,n));
//produsul elementelor
printf("\nProdusul elementelor: %d",produs(x,n));
}

Aplicaţia 5.
Să se scrie o functie recursivă pentru determinarea sumei cifrelor unui număr natural.

Indicaţie: Se izolează ultima cifra, iar lui n i se atribuie câtul întreg dintre vechea valoare şi 10.
0, 0 ( ) pentru n S n
%10 ( /10), n S n altfel
==⎨
⎧ ⎩+

#include<stdio.h>

int S(int n)
{
if (!n) return 0;
else return n%10 + S(n/10);
}

void main()
{
int n;
printf("n="); scanf("%d",&n);
printf("Suma este: %d",S(n));
}

Aplicaţia 6.
Să se scrie o funcţie recursivă pentru a transforma un număr natural n, din baza 10 în baza k
(1<k<=10).
Indicaţie: Numărul se împarte la k, se reţine restul, câtul se împarte la k, se reţine restul... până când
câtul este mai mic decât împărtitorul. Rezultatul se obţine prin scrierea în ordine inversă a resturilor
obţinute. Tipărirea restului se face după autoapel.

9
#include<stdio.h>
void transform(int n,int b)
{
int rest=n%b;
if (n>=b) transform(n/b,b);
printf("%d",rest);
}

void main()
{
int n,b;
printf("n="); scanf("%d",&n);
printf("baza="); scanf("%d",&b);
transform(n,b);
}
Aplicaţia 7.
+
Să se determine functia recursivă a lui Ackerman definită pe ⋅ , astfel: ⎧

= ⎪ =− = ⎨⎪
⎩−−

1, 0
nm
( , ) ( 1,1) , 0
Ack m n Ack m n
( 1, ( , 1)) ,
Ack m Ack m n altfel

#include<stdio.h>

int Ack(int m, int n)


{
if (!m) return n+1;
else
if (!n) return Ack(m-1,1);
else return Ack(m-1,Ack(m,n-1));
}

void main()
{
int m,n;
printf("m="); scanf("%d",&m);
printf("n="); scanf("%d",&n);
printf("\nRezultatul functiei este: %d",Ack(m,n));
}

Aplicaţia 8.
Se citeste x din . Se cere subprogramul pentru calculul funcţiei Manna-Pnuelli:
1, 12 ( )
( ( 2)) , 12

−≥=⎨
⎧ ⎩+<
xx
Fx
FFx x

#include<stdio.h>

int F(int x)
{
if (x>=12) return x-1;
return F(F(x+2));
}

10
void main()
{
int x;
printf("x="); scanf("%d",&x);
printf("Valoarea functiei este: %d",F(x));
}

Aplicaţia 9.
Se consideră şirul lui Fibonacci definit astfel:
= ⎪ = = ⎨⎪
⎧ ⎩+
0, 0
n
Un
1, 1
n

U U altfel − −
,
12
nn

Se citeste n, un număr natural. Să se calculeze Un, în variantă recursivă.

#include<stdio.h>

int U (int n)
{
if (!n) return 0;
else if (n==1) return 1;
else return U(n-1)+U(n-2);
}

void main()
{
int n;
printf("n="); scanf("%d",&n);
printf("Valoarea sirului in n este: %d",U(n));
}

/*
In aceasta situatie, este corect a se folosi un program care calculeaza
U(n) iterative, deoarece in modul descris mai sus, functia se executa astfel:
Ptr. calculul lui U(n) este necesar sa se cunoasca U(n-1) si U(n-2). Procedeul
continua pâna este calculat U(n-1), apoi se reia calculul ptr. U(n-2). Acest lucru
este foarte ineficient pentru valori mari ale lui n (ex. n=100). Se calculeaza
U(100) ca suma intre U(99) si U(98). Pentru calculul lui U(99) se caculeaza U(98)
si U(97), s.a.m.d.
Dupa ce calculam U(99) reluam calculele ptr. U(98). O astfel de recursie
se executa printr-un dublu apel al functiei din care unul este apel in apel.
Asemenea recursivitate se numeste recursivitate in cascada. Propunem folosirea
iterativa in rezolvarea acestui caz. Vezi exemplul urmator.
*/

- varianta iterativă:

#include<stdio.h>
void main()
{
int n,U0=0,U1=1,U2;

printf("n="); scanf("%d",&n);

if(!n) printf("%d",U0);
else

11
if (n==1) printf("%d",U1);
else
{
for (int i=2;i<=n;i++)

{
U2=U0+U1;
U0=U1;
U1=U2;
}
printf("%d",U2);
}
}

/*
ptr. n=3

i=2: U2=U0+U1
U0=U1
U1=U2

i=3: U2=U1+U2
*/

Probleme propuse
Să se refacă toate exemplele anterioare şi unde e cazul să se aducă îmbunătaţiri.
12

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