Documente Academic
Documente Profesional
Documente Cultură
Principiul
9
recursivităţii
Sumar
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>
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>
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
#include<stdio.h>
void main()
{
unsigned int x,y;
printf("dati x: ");
scanf("%u",&x);
printf("dati y: ");
scanf("%u",&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>
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>
void main()
{
unsigned int x[20];
//numarul de elemente
int n;
printf("dati n: "); scanf("%d",&n);
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>
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)
//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));
Dacă se doreşte, aplicaţia de mai sus poate fi scrisă şi asemeni construcţiei de mai jos.
VARIANTA 2:
#include<stdio.h>
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)
//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>
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
#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