Sunteți pe pagina 1din 48

+

Algoritmi și tehnici de
programare

Cursul 5
+
Cuprins

 Iterativitate şi recursivitate

 Subprograme recursive

 Căutare secvențială și binară

 Divide et Impera

 Interclasare

 Sortare prin interclasare

 Eficiența algoritmilor

 Pointeri la funcții
+
Subprograme iterative

 Algoritmi iterativ – repetitiv

long factorial(int n)
{
int i;
long p;
p=1;
for(i=0; i<n; i++)
p=p*(i+1);
return p;
}
+
Subprograme recursive

 Recursivitatea este noţiune matematică care


implică definirea unui concept prin referirea la
acelaşi concept.

 Algoritm recursiv - algoritm care se autoapelează


+
Subprograme recursive

 Tipuri de recursivitate:
 directă (un subprogram se autoapelează);

 în cascadă (o anumită valoare este calculată în cascadă de mai


multe ori /vezi Fibonacci);

 indirectă (2 subprograme (A şi B) se apelează unul pe altul) in


acest caz este obligatoriu definirea prototipului subprogramului;
+
Subprograme recursive

 Trebuie să existe:
 Formulă de start (simpla sau compusa)
 Formulă recursivă
+
Subprograme recursive

 Orice algoritm iterativ poate fi transcris într-


un algoritm recursiv şi invers.

 Algoritmul ales depinde de competenţa/pregătirea


programatorului.

 Uzual, volumul de muncă pentru scrierea


algoritmului recursiv este mai mic decât în cazul
algoritmului iterativ.
+
Subprograme recursive

 Consideraţii generale
 Fiecare apel recursiv trebuie aplicat unei probleme mai simple decât în
pasul anterior
 Rezultă o metodă simplă de oprire a generării de noi apeluri

 Cum se transformă un subprogram iterativ în unul recursiv?


1. instrucţiunea iterativă dispare
2. condiţia de la instrucţiunea iterativă devine (eventual modificată)
condiţie de oprire a generării de noi autoapeluri
3. Repetarea este asigurată prin autoapel
+
Subprograme recursive

 Când alegem subprograme iterative/ recursive?

 Consum memorie
 Timp de execuţie
 Uşurinţă în proiectare / implementare
 Lungime cod sursă
+
Subprograme recursive
 Construcţia subprogramelor recursive

 Să asigure respectarea finititudinii algoritmului: ieşirea


după un număr finit de paşi

 Condiţie de oprire sau a generării de noi apeluri


 Aplicarea formulei de start dacă e îndeplinită condiţia
 Aplicarea formulei recursive în caz contrar
+
Subprograme recursive
 Necesarul de memorie
 La fiecare apel se alocă spaţiu în stivă pentru … x = factorial(5);

factorial( 5 )
factorial(4) * 5
factorial(3) * 4
factorial(2) * 3
factorial(1) * 2
factorial(0) * 1
1
1
2
6
24
120
+
Subprograme recursive

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

long factorial(int n)
{ long f;
if (n == 0) f= 1;
else
f=n * factorial(n-1);
return f;
}
+
Subprograme recursive
fib(n) = fib(n-1) + fib(n-2),

fib( 1 ) = fib( 0 ) = 1

fib( 3 ) fib: N N (Fibonacci)


fib( 2 ) 1 dacă n = 0 sau n = 1
fib( 1 ) fib(n) =
1 fib(n-2)+fib(n-1) dacă n >1
fib( 0 )
1
2
fib( 1 )
1
3
+
Subprograme recursive
 Fibonacci iterativ  Fibonacci recursiv
long fibo_it (int n) long fibo (int n)
{long f1=1,f2=1,fn=1; {
if ( n==0 || n==1 ) return 1;
int i; return fibo (n-2) + fibo (n-1);
if (n==0 || n==1) return 1; }
for (i=2;i<=n;i++) • Apel
{ void main ()
fn=f1+f2; {
f1=f2; int n;
f2=fn; printf("n=");
scanf ("%d", &n);
} printf ("%ld", fibo(n));
return fn; printf ("%ld", fibo_it(n));
} }
+
Subprograme recursive - exemple
Calculul combinărilor de n luate cîte k
C  1 C 1
 0 k
n k

n! k 1
Cnk 
k!n  k ! C C
k
n
k
n 1 C n 1

long comb(unsigned n, unsigned k)


{
if (k>n) return 0;
else
if ((k==0) || (k==n))
return 1;
else
return comb(n-1,k)+comb(n-1,k-1);
}
+
Subprograme recursive - exemple
 Suma elementelor unui vector
 S(n) = x[n-1] + S(n-1), S(0) = 0

long suma(long v[], int n)


{
if( n == 1)
return v[0];
else
return v[n-1] + suma(v, n-1);
}
 Apel:
long s, *v;
int n;
…………………………………………………
if (n>0)
{
s = suma(v,n);
printf("suma=%ld",s);
}
else printf("nu avem elemente\n");
+
Subprograme recursive - exemple
 Maximul dintr-un vector

int max(int a[], int n)


{
int x1;
if (n==1) return a[0];
else
{
x1=max(a,n-1);
if (x1>a[n-1]) return x1;
else return a[n-1];
}
}

 Apel:
int m, n,*v;
…………………………………………………
if (n>0)
{
m = max(v, n);
printf("max=%d", m);
}
else printf("nu avem elemente\n");
+
Subprograme recursive - teme

 Numărul de elemente negative dintr-un vector

 Produs scalar între doi vectori

 Algoritmul lui Euclid

 Calculul cmmdc

 Problema turnurilor din Hanoi

 A la puterea p, unde A este masiv cu doua dimensiuni

 Metoda tangentei
+
Căutare secvențială

 Presupunem că dorim să determinăm dacă un


element aparține sau nu unui vector de elemente.

 Dacănu se știe nimic despre elementele vectorului


avem soluția căutării secvențiale, până găsim
elementul căutat sau până testăm toate elementele.
+
Căutare secvențială

#include<stdio.h> void main()


{
int *x,n,a,i,g;
int cautare(int x[],int n, int a)
…………………………………….
{
printf("valoarea cautata=");
int gasit=0,i; scanf("%d",&a);
for(i=0;i<n;i++) g=cautare(x,n,a);
if(a==x[i]) gasit=1; if(g==1)
return gasit; printf("valoarea %d se gaseste in sir",a);
} else
printf("valoarea %d nu se gaseste in sir",a);
}
+
Căutare secvențială

Cautarea secvenţială

bool cautare(int x[],int n, int a)


{
bool gasit=false,
int i=0;
while ( ( v[i]!=x ) && ( i<n ) )
i++;
if(i<n) gasit=true;
return gasit;
}
+
Căutare binară

 Dacă elementele vectorului sunt ordonate crescător,


putem să ne dăm seama dacă elementul nu există în
vector fără a fi nevoie să parcurgem toate elementele
vectorului.

 Unuldintre algoritmii folosiţi în acest caz este algoritmul


de căutare binară.

 Acest algoritm are la bază principiul înjumătăţirii repetate


a intervalului sau domeniului în care se caută elementul,
prin împărţirea vectorului în doi subvectori.
+
Căutare binară

 Notăm cu p primul indice al vectorului şi cu u ultimul


indice al vectorului, iar m este indicele elementului din
mijloc al vectorului m=(p+u)/2.

 Secompară valoarea căutată k cu valoarea elementului


din mijloc v[m] .

 Dacăcele două valori sunt egale înseamnă că s-a găsit


elementul.

 Dacănu sunt egale vectorul o să fi împărţit în doi


subvectori.
+
Căutare binară
int cautare(int v[], int n, int k)
{
int u, p, gasit=-1, m;
p = 0;
u = n - 1;
while(p <= u)
{ m = (p + u) / 2;
if (k == v[m])
{ gasit=m;
p = u + 1;
}
else
if (k < v[m]) u = m - 1;
else p = m + 1;
}
return gasit;
}
+
Căutare binară - recursiv

int cauta(int v[], int p, int u, int k)


{ if (p > u) return -1;
else
{ int m = (p + u) / 2;
if(k < v[m])
return cauta(v, p, m-1, k);
else
if(k > v[m])
return cauta(v, m+1, u, k);
else
return m;
}
}
+
Metoda Divide et Impera

 Metoda Divide et Impera (Imparte si Stapaneste) este o


metodă de programare care se aplică problemelor care pot fi
descompuse în subprobleme independente, similare problemei
inițiale, cu complexitate mai mică și care pot fi rezolvate foarte
ușor.

 Procesul se reia până când (în urma descompunerilor


repetate) se ajunge la probleme care admit rezolvare
imediată.
+
Metoda Divide et Impera

 Divide et Impera este o tehnică speciala prin care se pot


rezolva o serie de probleme.

 Principiul care stă la baza acestei tehnici este


următorul:
 Descompunerea problemei Prob curente in subprobleme
independente SubProb.
 In cazul in care subproblemele SubProb admit o rezolvare
imediata se compun solutiile si se rezolva problema Prob
 Altfel se descompun in mod similar si subproblemele
SubProb
+
Metoda Divide et Impera

Probleme clasice
 Cautarea binară
 Maximul dintr-un vector
 Turnurile din Hanoi
 Sortarea prin interclasare
 Sortarea rapidă
+
Metoda Divide et Impera
 Cautarea binară
int cauta(int v[], int p, int u, int k)
{
if (p > u)
return -1;
else
{ int m = (p + u) / 2;
if(k < v[m])
return cauta(v, p, m-1, k);
else
if(k > v[m])
return cauta(v, m+1, u, k);
else
return m;
}
}

 Apel:
int poz, n, val, a[10];
poz = cauta(a,0,n-1,val);
+
Metoda Divide et Impera
 Maximul dintr-un vector
int max1(int a[100], int s, int d)
{
int x1, x2;
if (s==d) return a[s];
else
{
x1 = max1(a,s,(s+d)/2);
x2 = max1(a,(s+d)/2+1,d);
if (x1>x2)
return x1;
else
return x2;
}
}
 Apel:
int m, a[100], n;
m = max1(a,0,n-1);
+
Metoda Divide et Impera

max=max(x[0],…,x[4])=max(max3,max4)

max3=max(x[0],..,x[2])=max(max1,max2) max4=max(x[3],x[4])

max2=max(x[0],x[1]) max1=max(x[2])
+
Interclasare

 Fiind dati doi vectori ordonaţi se obţine un al treilea


vector ordonat care va conţine elementele din cei doi
vectori.
+
Interclasare -

void interclasare(int x[],int p, int m, int u) if (i<=m)


{int i,j,k,l;int z[10]; for(l=i;l<=m;l++)
{ z[k]=x[l];
i=p; j=m+1; k=0; k++;
while((i<=m)&&(j<=u)) }
{ if (x[i]<x[j]) if (j<=u)
for (l=j;l<=u;l++)
{z[k]=x[i]; i++; k++; { z[k]=x[l];
} k++;
}
else
int t=p;
{z[k]=x[j]; j++; k++; for (k=0;k<(u-p)+1;k++)
} x[t++]=z[k];
}
}
+
Sortare prin interclasare -
Merge sort
 Schema problemei:
 generalizare: sortează(a[p],…,a[u])

 conditie: p < u

 Soluţie:

 divide-et-impera
 divide: m = [(p + u)/2]

 subprobleme:sortează(a[p..m]), sortează(a[m+1..u])

 asamblare: interclaseaza subsecvenţele sortate a[p..m] şi a[m+1..u]


+
Sortare prin interclasare -
Merge sort
 Algoritm:
 se împarte vectorul în secvenţe din ce in ce mai mici, astfel încât
fiecare secvenţă sa fie ordonată la un moment dat şi interclasată
cu o altă secvenţă din vector corespunzătoare.

 Se urmăresc cei trei pași:


 se descompune problema în două sau mai multe subprobleme
 se rezolvă subproblemele
 se combină soluţiile problemelor în care a fost descompusă și se
obţine soluţia problemei iniţiale

 Timpul este de ordinul O(n * logn)


+
Sortare prin interclasare -
Merge sort

 Aceasta metodă de ordonare are la bază interclasarea a


doi vectori.

 În cazul sortării prin interclasare, vectorii care se


interclasează sunt două secvenţe ordonate din acelaşi
vector.
+
Sortare prin interclasare -
Merge sort
34 25 41 6 9 84 78

34 25 41 6 9 84 78

34 25 41 6 9 84 78

34 25 41 6 9 84 78

25 34 41 6 9 78 84

25 34 41 6 9 78 84

6 9 25 34 41 78 84
+
Sortare prin interclasare -
Merge sort
#include<stdio.h> if (i<=m)
void interclasare(int x[10],int p, int m, int u) for(l=i;l<=m;l++)
{ z[k]=x[l];
{int i,j,k,l;int z[10]; k++;
i=p; j=m+1; k=0; }
if (j<=u)
while((i<=m)&&(j<=u))
for (l=j;l<=u;l++)
{ if (x[i]<x[j]) { z[k]=x[l];
{z[k]=x[i]; i++; k++; k++;
}
}
int t=p;
else for (k=0;k<(u-p)+1;k++)
{z[k]=x[j]; j++; k++; x[t++]=z[k];
}
}
}
+
Sortare prin interclasare -
Merge sort
void divimp(int x[10],int p,int u) void main()
{ {
int n,m,i,j,k;
if (p<u)
int x[10];
{ printf("n=");
int m=(p+u)/2; scanf("%d", &n);
divimp(x,p,m); for(i=0; i<n;i++)
{printf("x[%d]=",i+1);
divimp(x,m+1,u); scanf("%d",&x[i]);
interclasare(x,p,m,u); }
} divimp(x,0,n-1);
printf("\nvect rez. este \n");
}
for(i=0;i<n;i++)
printf("x[%d]=%d\n",i,x[i]);
}
+
In ce constă analiza eficienței
algoritmilor?
 Resurse de calcul:

• Spațiu de memorie = spațiul necesar stocării datelor prelucrate de


către algoritm
• Timp de execuție = timp necesar execuției prelucrărilor din cadrul
algoritmului

 Dacă un algoritm utilizează mai puține resurse de calcul decât un


alt algoritm atunci este considerat mai eficient
+
In ce constă analiza eficienței
algoritmilor?
 Există două tipuri de eficiență

• Eficiența în raport cu spațiul de memorie = se referă la spațiul de


memorie necesar algoritmului
• Eficiența în raport cu timpul de execuție = se referă la timpul necesar
execuției prelucrărilor din algoritm
+
In ce constă analiza eficienței
algoritmilor?
 Pentru estimarea timpului de execuție este necesar să se
stabilească :

• Un model de calcul

• O unitate de măsură a timpului de execuție


+
In ce constă analiza eficienței
algoritmilor?
 Unitate de măsură = timpul necesar execuției unei
prelucrări elementare (prelucrare de bază)
 Operații elementare (de bază):
• Asignare
• Operații aritmetice
• Comparații
• Operații logice
 Timp de execuție al algoritmului = numărul de operații
elementare executate
+
In ce constă analiza eficienței
algoritmilor?
 Timpul de execuție al unei prelucrari elementare nu
depinde de valorile operanzilor (timpul de execuție
pentru a calcula 1+2 nu diferă de timpul de execuție
pentru 124+120)

 Timpul necesar accesării datelor nu depinde de locația


datelor în memorie (nu este diferență între timpul
necesar prelucrării primului element al unui tablou și
cel al prelucrării ultimului element)
+
In ce constă analiza eficienței
algoritmilor?
 Dim. problema: n Sum(n)
 Algoritm: Sum(n)
 1: S←0
 2: i ← 0
 3: WHILE i<n DO
 4: i ← i+1
 5: S ←S+i

Timp de execuție:  6:ENDWHILE


T(n)=(c3+c4+c5)n+(c1+c2+c3)  7:RETURN S
T(n)= a*n +b
+
In ce constă analiza eficienței
algoritmilor?

 Dacăun algoritm necesită a*n+b, o să simplificăm această


formulă la n si spunem ca algoritmul are complexitatea O(n)

 Dacă un algoritm necesită a*n3+b*n2+c*n+d, o să simplificăm


această formulă la n3 si spunem ca algoritmul are
complexitatea O(n3)
+
In ce constă analiza eficienței
algoritmilor?

 Majoritatea algoritmilor întâlniți în practică se încadrează în


una dintre clasele
+
Bibliografie

 I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, Programarea calculatoarelor. Ştiinţa învăţării
unui limbaj de programare, Teorie şi aplicaţii, Ed. ASE,
2003

 I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, M. Mircea, Programarea calculatoarelor.
Algoritmi în programare, Ed. ASE Bucureşti, 2007

 C. Uscatu, M. Popa, L. Bătăgan, C. Silvestru, Programarea


Calculatoarelor. Aplicații, Ed. ASE, 2012