Sunteți pe pagina 1din 9

1.

Recursivitate.......................................................................................................................2
Definiie..................................................................................................................................2
Exemple..................................................................................................................................2
Caracteristicile funciilor recursive........................................................................................2
Calculul timpului de execuie.................................................................................................3
Generarea de submulimi.......................................................................................................3
2. Funcii cu numr variabil de argumente.............................................................................5
3. Probleme propuse...............................................................................................................9

1. Recursivitate
Putem considera noiunea de algoritm ca o exprimare specializat a unor definiii matematice,
care ne ajut la construirea programelor n diverse limbaje de programare. n matematic,
multe definii utilizeaz, pentru construirea unor concepte, o metod special numit
recursivitate.

Definiie
Se spune despre un concept c este recursiv dac el este definit prin el nsui. n acest fel pot
fi definite numerele naturale, iruri de numere sau unele funcii:

Exemple

numrul 1 este numr natural; succesorul unui numr natural este tot un numr
natural (se presupune c este definit noiunea de succesor);

irul numerelor triunghiulare (termenul de rang n se obine adunnd pe n la


termenul anterior)
tri1 = 1;
trin = n + trin-1, pentru n > 1

funcia lui Ackermann:


ack(m,n) = ack(m-1, ack(m, n-1));
ack(0,n) = n+1;
ack(m,0) = ack(m-1,1);

Aceast funcie are o adncime mare din punctul de vedere al recursivitii. Este util n
compararea eficienei compilatoarelor limbajelor de programare. De asemenea valoarea ei
crete foarte rapid.

Caracteristicile funciilor recursive


double factrec(unsigned n)
{
/* Exista o anumita versiune a problemei,
suficient de simpla, pentru care rezultatul
este imediat, nemaifiind necesara autoapelarea. */
if (n == 1)
return 1;

/* Se autoapeleaza;
Atunci cand se apeleaza pe sine, o fac
pentru a rezolva o problema mai mica; */
else
return n * factrec(n-1);

Calculul timpului de execuie


Performana programelor depinde de doi factori eseniali: spaiul de memorie ocupat de
program i timpul de execuie. O variant simpl de calcul a timpului de execuie este
prezentat n cele ce urmeaz.
#include <time.h>
#include <stdio.h>
int main()
{
long i, j;
double timp_total;
/* Tipul clock_t reprezinta un numar de
cicluri de ceas al procesorului */
clock_t t1, t2;
/* Functia clock() returneaza numarul de
cicluri de ceas scurse de la un moment
arbitrar fix. */
t1 = clock ();
/* Aici se efectueaza operatiile pentru care
dorim sa masuram timpul de executie.
Pentru acest exemplu nu facem decat sa
parcurgem doua bucle for, pentru a consuma
ceva timp. */
for (i=0; i<25000; i++)
for (j=0; j<25000; j++)
;
/* Citim din nou numarul de cicluri de ceas ale
procesorului, fata de acelasi moment arbitrar
fix. */
t2 = clock ();
/* Valoarea returnata de macroul CLOCKS_PER_SEC
este frecventa de ceas a procesorului.
In functie de aceasta valoare si de diferenta
celor doua valori pastrate in t1 si t2 calculam
timpul total de executie in secunde. */
timp_total = (double)(t2 - t1) / (double)CLOCKS_PER_SEC;
printf("timpul de executie = %.3lf secunde", timp_total);
}

Generarea de submulimi
Pentru mulimea: {a, b, c } se pot genera urmtoarele mulimi:
Permutri :
{{a, b, c}, {a, c, b}, {b, a, c}, {b, c, a}, {c, a, b}, {c, b, a}}
Aranjamente de 2 elemente cu repetiie :
{{a, a}, {a, b}, {a, c}, {b, a}, {b, b}, {b, c}, {c, a}, {c, b}, {c, c}}

Combinri de 2 elemente cu repetiie :


{{a, a}, {a, b}, {a, c}, {b, b}, {b, c}, {c, c}}
Pentru determinarea acestor mulimi se pot folosi cu succes funcii recursive.
/* Generarea aranjamentelor de n elemente luate
cate m, cu repetitie. */
#include <stdio.h>
int n, m;
char caractere[20];
int indici[20];
/* Functia de tiparire a solutiei in care se face
asocierea dintre pozitia generata si caracter. */
void tipareste()
{
int i;
/* Tiparirea indicilor se realizeaza "de la m la 1"
deoarece si generarea lor a nceput de pe pozitia m. */
for(i=m-1; i>=0; i--)
printf("%c", caractere[indici[i]]);
printf("\n");
}
void aranj_r(int k)
{
int j;
/* Cand submultimea de prelucrat este vida,
se tipareste solutia gasita pana in acest moment. */
if (k == -1)
{
tipareste();
}
else
{

/* Variabila j substituie caracterele pentru


o mai usoara iterare. Pe pozitia data de
indicele k punem pe rand fiecare caracter. */
for(j=0; j<n; j++)
{
indici[k] = j;

/* Generarea submultimii se reduce la generarea


unei alte submultimi avand un element mai putin
decat in etapa curenta. */
aranj_r(k-1);
}

}
int main()
{
int i;
char buffer[21];
/* Citire tablou de n caractere, unice si a m<=n. */

printf("caracterele? (unice, toate pe o linie, fara spatii) ");


gets(buffer);
n = strlen(buffer);
for (i=0; i<n; i++)
caractere[i] = buffer[i];
printf("am citit %d caractere: ", n);
for (i=0; i<n; i++)
printf("%c", caractere[i]);
printf("\n");
do
{
printf("m? "); scanf("%d", &m);
} while ((m<=0) || (m>n));
/* Apelul functiei recursive. Pornim de la ultima
pozitie (indice m-1). Apelurile recursive ne
vor duce spre indicii mai mici. */
aranj_r(m-1);
}

2. Funcii cu numr variabil de argumente


n mod normal, cnd scriem o funcie C trebuie s specificm exact numrul i tipul
parametrilor pe care i poate primi funcia.
S ne reamintim exemplul de la lucrarea cu transmiterea parametrilor din linie de comand, i
anume programul care afia pe ecran literele TP construite din stelue i spaii:
*******
*
*
*
*
*
*
*
*
*
*

*****
*
*
*
*
*
*
*
*
*****
*
*
*
*
*

Dac vrem s scriem un program C care s afieze literele TP n mod similar cu fiierul BAT
de la laboratorul respectiv, o variant este s scriem o funcie linie() care primete ca i
parametru o secven de numere i afieaz pe ecran, pe o singur linie, o alternan de stelue
i spaii, n funcie de numerele specificate. Dac analizm literele TP pe care vrem s le
tiprim, vedem c avem trei situaii posibile: cnd trimitem 3 numere ca i parametri, cnd
trimitem 7 numere ca i parametri sau cnd trimitem 5 numere ca i parametri.
O modalitate de implementare este s scriem trei funcii linie, cu 3, cu 5 i respectiv cu 7
parametri. Programul ar arta n felul urmtor.
#include <stdio.h>
void stelute(int n)
{

int i;
for (i=0; i<n; i++)
printf("*");
}
void spatii(int n)
{
int i;

for (i=0; i<n; i++)


printf(" ");

void linie3(int n1, int n2, int n3)


{
stelute(n1);
spatii(n2);
stelute(n3);
printf("\n");
}
void linie5(int n1, int n2, int n3, int n4, int n5)
{
stelute(n1);
spatii(n2);
stelute(n3);
spatii(n4);
stelute(n5);
}

printf("\n");

void linie7(int n1, int n2, int n3, int n4, int n5, int n6, int n7)
{
stelute(n1);
spatii(n2);
stelute(n3);
spatii(n4);
stelute(n5);
spatii(n6);
stelute(n7);
printf("\n");
}
int main(int argc, char* argv[])
{
linie3(7, 3, 5);
linie7(0, 3, 1, 6, 1, 4, 1);
linie7(0, 3, 1, 6, 1, 5, 1);
linie7(0, 3, 1, 6, 1, 5, 1);
linie7(0, 3, 1, 6, 1, 4, 1);
linie5(0, 3, 1, 6, 5);
linie5(0, 3, 1, 6, 1);
linie5(0, 3, 1, 6, 1);
linie5(0, 3, 1, 6, 1);
linie5(0, 3, 1, 6, 1);
linie5(0, 3, 1, 6, 1);
linie5(0, 3, 1, 6, 1);

return 0;
}

n urma rulrii lui, pe ecran apare:


*******
*
*
*
*
*
*
*
*
*
*
*

*****
*
*
*
*
*
*
*
*
*****
*
*
*
*
*
*

Dac urmrim sursa programului, vom realiza c ea nu este foarte bine structurat. Ce se
ntmpl dac vrem s transmitem 4 parametri la funcia linie? Sau 6, sau 8? Ar trebui s
scriem noi variante ale funciei care s accepte numrul dorit de parametri.
Limbajul C ne permite s declarm funcii cu numr variabil de parametri.
O funcie cu numr variabil de parametri se declar asemntor cu orice funcie, cu diferena
c pe post de ultimul parametru al funciei apar caracterele '...' (trei puncte).
Vom da explicaii despre modul de folosire a funciilor cu numr variabil de parametri direct
pe un cod care funcioneaz:
#include <stdio.h>
#include <stdarg.h>
void stelute(int n)
{
int i;
for (i=0; i<n; i++)
printf("*");
}
void spatii(int n)
{
int i;
for (i=0; i<n; i++)
printf(" ");
}
/* Functia are numar variabil de parametri.
Putem avea si parametri ficsi, important este
ca dupa ultimul parametru fix sa apara caracterele
'...'. In acest caz concret avem un singur parametru
fix, care va spune de fiecare data cati
parametri variabili urmeaza. Parametri variabili vor
fi numerele in functie de care se afiseaza

stelute sau spatii. */


void linie(int nr_param, ...)
{
/* Pentru a avea acces la parametri functiei,
avem nevoie de o variabila de tipul "va_list".
E necesar sa includem <stdarg.h> pentru asta.
Practic aceasta variabila este un pointer care
va indica rand pe rand catre parametri variabili
ai functiei. */
va_list ap;
int i, n;
/* Primul pas este sa initializam variabila de mai sus
folosind macro-ul "va_start". Varibila se initializeaza
relativ la ultimul parametru fix. In cazul nostru
avem un singur parametru fix, deci nu avem de ales. */
va_start(ap, nr_param);
/* Parametri ficsi pot fi accesati direct, asa cum suntem
obisnuiti. Noi folosim singurul parametru fix pentru
a parcurge intr-o bucla parametri variabili. */
for (i=0; i<nr_param; i++)
{
/* Pentru a afla valoarea unui parametru variabil, folosim
macro-ul "va_arg". La fiecare apel al acestui macro se
returneaza un parametru variabil (cel spre care indica
pointer-ul "ap" definit mai sus). Dupa apel, in mod automat
variabila "ap" este setata sa indice spre urmatorul
parametru variabil. */
n = va_arg(ap, int);
/* In functie de pozitiile pare sau impare afisam stelute
sau spatii. */
if (i % 2 == 0)
stelute(n);
else
spatii(n);
}
/* Dupa ce am parcurs toti parametri variabili, apelam macro-ul
"va_end". Acesta face operatii necesare pentru curatarea
memoriei. */
va_end(ap);
printf("\n");
}
int main()
{
linie(3,
linie(7,
linie(7,
linie(7,
linie(7,
linie(5,
linie(5,
linie(5,
linie(5,
linie(5,
linie(5,

7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,

5);
1, 6,
1, 6,
1, 6,
1, 6,
1, 6,
1, 6,
1, 6,
1, 6,
1, 6,
1, 6,

1, 4,
1, 5,
1, 5,
1, 4,
5);
1);
1);
1);
1);
1);

1);
1);
1);
1);

linie(5, 0, 3, 1, 6, 1);
}

return 0;

Efectul rulrii acestui program este acelai ca i la programul precedent. Observm c


programul n aceast form este mult mai flexibil. Acum putem apela funcia linie n orice
mod dorim, cu orici parametri, fr s fie nevoie s mai scriem noi variante ale ei.
n schimb trebuie s fim mult mai ateni la apelurile funciei, s trimitem corect numrul de
parametri. Compilatorul nu mai poate detecta situaii n care spre exemplu primul parametru
are valoare 5, dar dup el urmeaz doar 4 parametri variabili. Asemenea erori vor iei la
iveal doar la execuia programului.

3. Probleme propuse
1. Implementati funcia lui Ackermann recursiv.
2. Implementati funcia factorial n variant recursiv i iterativ. Comparai timpii de
execuie pentru 10000 de repetri ale calculului funciei factorial n varianta iterativ i
cea recursiv.
3. Generai combinrile cu repetiie pentru o mulime de caractere citite dintr-un fiier text.
Mulimea soluiilor se va scrie ntr-un fiier text. Datele de intrare vor fi validate pentru a
se asigura unicitatea caracterelor. n cazul n care datele de intrare sunt eronate se va
genera o soluie pentru caracterele unice.
4. Scriei o funcie cu numr variabil de argumente care primete ca prim parametru un
numr N, iar ca urmtorii N parametri primete N numere reale. Funcia va returna suma
celor N numere reale. Verificai funcia prin cteva apeluri ale ei cu diverse seturi de
parametri.