Sunteți pe pagina 1din 27

Curs 9 Complexitatea algoritmilor

Recursivitate
Complexitate

Curs 8 Testarea programelor


Motenire, UML
Unit teste in Python
Depanarea aplicaiilor Python
Recursivitate
O noiune e recursiv dac e folosit n propria sa definiie.

O funcie recursiv: funcie care se auto-apeleaz.


Rezultatul este obinut apelnd acelai funcie dar cu ali parametrii

def factorial(n):
"""
compute the factorial
n is a positive integer
return n!
"""
if n== 0:
return 1
return factorial(n-1)*n

Recursivitate direct: P apeleaz P


Recursivitate indirect: P apeleaz Q, Q apeleaz P
Cum rezolvm probleme folosind recursivitatea:
Definim cazul de baz: soluia cea mai simpl.
Punctul n care problema devine trivial (unde se oprete apelul recursiv)
Pas inductiv: mprim problema ntr-o variant mai simpl al aceleai probleme
plus ceva pai simplii
ex. apel cu n-1, sau doua apeluri recursive cu n/2

def recursiveSum(l): def fibonacci(n):


""" """
Compute the sum of numbers compute the fibonacci number
l - list of number n - a positive integer
return int, the sum of numbers return the fibonacci number for a given n
""" """
#base case #base case
if l==[]: if n==0 or n==1:
return 0 return 1
#inductive step #inductive step
return l[0]+recursiveSum(l[1:]) return fibonacci(n-1)+fibonacci(n-2)

Obs recursiveSum(l[1:]):
l[1:] - creaz o copie a listei l
exerciiu: modificai funcia recursiveSum pentru a evita l[1:]
Recursivitate n Python:
la fiecare apel de metod se creeaz o noua tabel de simboluri (un nou
namespace). Aceast tabel conine valorile pentru parametrii i pentru variabilele
locale
tabela de simboluri este salvat pe stack, cnd apelul se termin tabela se elimin
din stiv

def isPalindrome(str):
"""
verify if a string is a palindrome
str - string
return True if the string is a palindrome False otherwise
"""
dict = locals()
print (id(dict),dict)

if len(str)==0 or len(str)==1:
return True

return str[0]==str[-1] and isPalindrome(str[1:-1])


Recursivitate
Avantaje:
claritate
cod mai simplu

Dezavantaje:
consum de memorie mai mare
pentru fiecare recursie se creeaz o nou tabel de simboluri
Analiza complexitii
Analiza complexiti studiul eficienei algoritmilor.

Eficiena algoritmilor n raport cu:


timpul de execuie necesar pentru rularea programului
spaiu necesar de memorie

Timp de execuie, depinde de:


algoritmul folosit
datele de intrare
hardware-ul folosit
sistemul de operare (apar diferene de la o rulare la alta).
Exemplu timp de execuie
def fibonacci(n): def fibonacci2(n):
""" """
compute the fibonacci number compute the fibonacci number
n - a positive integer n - a positive integer
return the fibonacci number for a given n return the fibonacci number for a given n
""" """
#base case sum1 = 1
if n==0 or n==1: sum2 = 1
return 1 rez = 0
#inductive step for i in range(2, n+1):
return fibonacci(n-1)+fibonacci(n-2) rez = sum1+sum2
sum1 = sum2
sum2 = rez
return rez
def measureFibo(nr):
sw = StopWatch()
print "fibonacci2(", nr, ") =", fibonacci2(nr)
print "fibonacci2 take " +str(sw.stop())+" seconds"

sw = StopWatch()
print "fibonacci(", nr, ") =", fibonacci(nr)
print "fibonacci take " +str(sw.stop())+" seconds"

measureFibo(32)
fibonacci2( 32 ) = 3524578
fibonacci2 take 0.0 seconds
fibonacci( 32 ) = 3524578
fibonacci take 1.7610001564 seconds
Eficiena algoritmilor
Eficiena algoritmilor poate fi definit ca fiind cantitatea de resurse utilizate de algoritm (timp,
memorie).
Msurarea eficienei:
analiz matematic a algoritmului - analiz asimptotic
Descrie eficiena sub forma unei funcii matematice.
Estimeaz timpul de execuie pentru toate intrrile posibile.
o analiz empiric a algoritmului
determinarea timpului exact de execuie pentru date specifice
nu putem prezice timpul pentru toate datele de intrare.
Timpul de execuie pentru un algoritm este studiat n relaie cu dimensiunea datelor de intrare.
Estimm timpul de execuie n funcie de dimensiunea datelor.
Realizm o analiz asymptotic. Determinm ordinul de mrime pentru resursa utilizat (timp,
memorie), ne intereseaz n special pentru cazurile n care datele de intrare sunt mari
Complexitate
caz favorabil - datele de intrare care conduc la timp de execuie minim
BC ( A) min E ( I )
best-case complexity (BC): ID

caz defavorabil date de intrare unde avem cel mai mare timp de execuie.
WC ( A) max E ( I )
worst-case complexity (WC): ID

caz mediu - timp de execuie.


AC ( A) P( I ) E ( I )
average complexity (AC): ID

A - algoritm; E(I) numr de operaii; P(I) probabilitatea de a avea I ca i date de intrare


D mulimea tuturor datelor de intrare posibile pentru un n fixat

Obs. Dimensiunea datelor (n) este fixat (un numr mare) caz favorabil/caz defavorabil se refer la un
anumit aranjament al datelor de intrare care produc timp minim/maxim
Complexitate timp de execuie

numrm pai (operaii elementare) efectuai (de exemplu numrul de instruciuni, numr de
comparaii, numr de adunri).
numrul de pai nu este un numr fixat, este o funcie, notat T(n), este in funcie de dimensiunea
datelor (n), nu rezult timpul exact de execuie
Se surprinde doar esenialul: cum crete timpul de execuie n funcie de dimensiunea datelor.
Ne ofer ordinea de mrime pentru timpul de execuie (dac n , then 3 n n ).
2 2

putem ignora constante mici dac n aceste constante nu afecteaz ordinea de mrime.

Ex : T (n) 13 n 42 n 2 n log 2 n 3 n
3 2

Fiindc 0 log 2 n n, n 1 i n n, n 1 , putem conclude c temenul n domin aceast expresie


3

cand n este mare


3
Ca urmare, timpul de execuie a algoritmului crete cu ordinul lui n , ceea se scrie sub forma
T (n) O(n3 ) i se citete T(n) este de ordinul n3
n continuare, vom nota prin f o funcie f : N i prin T funcia care d complexitatea timp
de excuie a unui algoritm, T : N N .

Definiia 1 (Notaia O , Big-oh). Spunem c T (n) O( f (n)) dac exist c i n0 constante pozitive

(care nu depind de n) astfel nct 0 T (n) c f (n), n n0 .

Cu alte cuvinte, notaia O d marginea superioar


T ( n)
lim
Definiia alternativ: Spunem c T (n) O( f (n)) dac
n f ( n)
este 0 sau o constant, dar nu .

Observaii.
T ( n)
lim 13
Dac T (n) 13 n 42 n 2 n log 2 n 3 n , atunci
3 2
1. n n3 . Deci, putem spune c
T ( n) O ( n 3 ) .

2. Notaia O este bun pentru a da o limit superioar unei funcii. Observm, totui, c dac
T (n) O(n3 ) , atunci este i O(n 4 ) , O(n5 ) , etc atta timp ct limita este 0. Din aceast cauz avem

nevoie de o notaie pentru limita inferioar a complexitii. Aceast notaie este .


Definiia 2 (Notaia , Big-omega). Spunem c T (n) ( f (n)) dac exist c i n0 constante

pozitive (care nu depind de n) astfel nct 0 c f (n) T (n), n n0 .

notaia d marginea inferioar

T ( n)
lim
Definiia alternativ: Spunem c T (n) ( f (n)) dac
n f (n) este o constant sau , dar nu 0.
Definiia 3 (Notaia , Big-theta). Spunem c T (n) ( f (n)) dac T (n) O( f (n)) i dac
T (n) ( f (n)) , altfel spus dac exist c1, c2 i n0 constante pozitive (care nu depind de n) astfel nct

c1 f (n) T (n) c 2 f (n), n n0 .

notaia mrginete o funcie pn la factori constani


T ( n)
lim
Definiia alternativ Spunem c T (n) ( f (n)) dac
n f ( n)
este o constant nenul (dar nu 0 sau
).

Observaii.
1. Timpul de execuie al unui algoritm este ( f (n)) dac i numai dac timpul su de execuie n
cazul cel mai defavorabil este O( f (n)) i timpul su de execuie n cazul cel mai favorabil este
( f (n)) .

2. Notaia O( f (n)) este de cele mai multe ori folosit n locul notaiei ( f (n)) .
T ( n)
lim 13
Dac T (n) 13 n 42 n 2 n log 2 n 3 n , atunci n n . Deci, T (n) (n ) . Acest lucru
3 2 3 3
3.
poate fi dedus i din faptul c T (n) O(n ) i T (n) (n ) .
3 3
Sume

for i in range(0, n):


#some instructions

presupunnd c ceea ce este n corpul structurii repetitive (*) se execut n f(i) pai timpul de
execuie al ntregii structuri repetitive poate fi estimat astfel
n
T (n) f (i )
i 1

Se poate observa c, n cazul n care se folosesc bucle imbricate, vor rezulta sume imbricate. n
continuare, vom prezenta cteva dintre sumele uzuale:
Calculul se efectueaz astfel:
se simplific sumele eliminm constantele, separm termenii in sume individuale
facem calculul pentru sumele simplificate.
Exemple cu sume
Analizai complexitatea ca timp de execuie pentru urmtoarele funcii
def f1(n):
s = 0 () = 1 = () ()
for i in range(1,n+1): (=1)
s=s+i Complexitate (Overall complexity) ()
return s
Cazurile Favorabil/Mediu/Defavorabil sunt identice
def f2(n):
i = 0 () = 1 = () ()
while i<=n: (=1)
#atomic operation
i = i + 1 Overall complexity ()
Cazurile Favorabil/Mediu/Defavorabil sunt identice
def f3(l): Caz favorabil:
"""
l list of numbers primul element e numr par: () = 1 (1)
return True if the list contains Caz defavorabil:
an even nr
""" Nu avem numere pare n list: () = ()
poz = 0 Caz mediu:
while poz<len(l) and l[poz]%2 !=0:
poz = poz+1
While poate fi executat 1,2,..n ori (acelai probabilitate).
return poz<len(l) Numrul de pai = numrul mediu de iteraii

() = (1 + 2+. . . +) = ( + 1)2 () ()

Complexitate ()
Exemple cu sume
def f4(n): (2n2) 2n (2n2)
for i in range(1,2*n-2): () = 1= (2n 1)
for j in range(i+2,2*n): (=1) (=+2) (=1)
#some computation
pass (2n2) (2n2) (2n2)
() = 2n 1
(=1) (=1) (=1)

(2n2)
() = 2n 1 (2n 2) (2n 1)2 (2n 2)
(=1)

() = 2n2 3n + 1 (2 ) Overall complexity (2 )
def f5():
for i in range(1,2*n-2):
Caz favorabil: While se execut odat () = (2n2)
(=1) 1=
j = i+1 2n 2 ()
cond = True Caz defavorabil: While executat 2n ( + 1)ori
while j<2*n and cond: (2n2)
#elementary operation () = (2n 1) =. . . = 2n2 3n + 1 (2 )
if someCond: (=1)
cond = False
Caz mediu:Pentru un i fixat While poate fi executat 1,2..2n-i-
1 ori numr mediu de pai: = (1 + 2+. . . +2n 1)2n
1 =. . . = (2n )2
(2n2) (2n2)
() = = (2n )2 =. . . (2 )
(=1) (=1)

Overall complexity (2 )
Formule cu sume:

1 n
i 1 suma constant.

n
n(n 1)
i 2
i 1 suma liniar (progresia aritmetic)
n
n(n 1)(2n 1)
i
i 1
2

6
suma ptratic

n
1
i ln(n) O(1)
i 1 suma armonic
n
c n 1 1
ci c 1
, c 1
i 1 progresia geometric (crete exponenial)
Complexiti uzuale

T (n) O(1) - timp constant. It is a great complexity. This means that the
algorithm takes only constant time.

T (n) O(log 2 log 2 n) - timp foarte rapid (aproape la fel de rapid ca un timp constant)

T (n) O(log 2 n) - complexitate logaritmic:


timp foarte bun (este ceea ce cutm, n general, pentru orice algoritm);

log 2 1000 10, log 2 1.000.000 20 ;

complexitate cutare binar, nlimea unei arbore binar echilibrat

T (n) O((log2 n) k ) - unde k este factor constant; se numete complexitate polilogaritmic


(este destul de bun);
Complexiti uzuale

T ( n) O ( n) - complexitate liniar;

T (n) O(n log 2 n) - este o complexitate faimoas, ntlnit mai ales la sortri
(MergeSort, QuickSort);

T ( n) O ( n 2 ) - este complexitate ptratic (cuadratic);


dac n este de ordinul milioanelor, nu este prea bun;

T ( n) O ( n k ) - unde k este constant; este complexitatea polinomial


(este practic dora daca k nu este prea mare);

T (n) O(2n ), O(n3 ), O(n!) - complexitate exponenial (algoritmii cu astfel de complexiti sunt
practici doar pentru valori mici ale lui n: n 10, n 20 ).
Recurene
O recuren este o formul matematic definit recursiv.
Ex. numrul de noduri (notat N(h)) dintr-un arbore ternar complet de nlime h ar putea fi descris sub
forma urmtoarei formule de recuren:
N (0) 1

N (h) 3 N (h 1) 1, h 1

Explicaia ar fi urmtoarea:
Numrul de noduri dintr-un arbore ternar complet de nlime 0 este 1.
Numrul de noduri dintr-un arbore ternar complet de nlime h se obine ca fiind de 3 ori
numrul de noduri din subarborele de nlime h-1, la care se mai adaug un nod (rdcina
arborelui).
Dac ar fi s rezolvm recurena, am obine c numrul de noduri din arborele ternar complet de
h
N (h) 3h N (0) (1 31 32 ... 3h 1 ) 3i
nlime h este i 0
Exemple
def recursiveSum(l): 1 = 0
"""Compute the sum of numbers Recurrence: () = {( 1) + 1
l - list of number
return the sum of numbers """ () = ( 1) + 1
#base case ( 1) =( 2) + 1
if l==[]: ( 2) =( 3) + 1=>() = + 1 ()
return 0
...= ...
#inductive step
return l[0]+recursiveSum(l[1:]) (1) = (0) + 1

def hanoi(n, x, y, z): 1 = 1


""" Recurrence: () = {2( 1) + 1
n -number of disk on the x
stick () = 2( 1) + 1
x - source stick ( 1) =2( 2) + 1 =
y - destination stick ( 2) = 2( 3) + 1 =>
z - intermediate stick
...= ...
"""
if n==1: (1) = (0) + 1
print ("disk 1 from",x,"to",y) () = 2( 1) + 1
return 2( 1) = 22 ( 2) + 2
hanoi(n-1, x, z, y)
print ("disk ",n,"from",x,"to",y) 22 ( 2) = 23 ( 3) + 22
hanoi(n-1, z, y, x) ...= ...
2(2) (2) = 2(1) (1) + 2(2)

() = 2(1) + 1 + 2 + 22 + 23 +. . . +2(2)

() = 2 1 (2 )
Complexitatea spaiu de memorare

Complexitatea unui algoritm din punct de vedere al spaiului de memorare estimeaz cantitatea
de memorie necesar algoritmului pentru stocarea datelor de intrare, a rezultatelor finale i a
rezultatelor intermediare. Se estimeaz, ca i timpul de execuie al unui algoritm, n notaiile , , .

Toate observaiile referitoare la notaia asimptotic a complexitii ca timp de execuie sunt valabile i
pentru complexitatea ca spaiu de memorare.
Exemplu
Analizai complexitatea ca spaiu de memorare pentru urmtoarele funcii

def iterativeSum(l): Avem nevoie de spaiu pentru numerele din list


"""
Compute the sum of numbers
l - list of number () = ()
return int, the sum of numbers
"""
rez = 0
for nr in l:
rez = rez+nr
return rez
def recursiveSum(l): 0 = 1
""" Recuren:() = {
( 1) + 1
Compute the sum of numbers
l - list of number
return int, the sum of numbers
"""
#base case
if l==[]:
return 0
#inductive step
return l[0]+recursiveSum(l[1:])
Analza complexitii (timp/spaiu) pentru o funcie
1 Dac exist caz favorabil/defavorabil:
descrie Caz favorabil
calculeaz complexitatea pentru Best Case
descrie Worst Case
calculeaz complexitatea pentru Worst case
calculeaz complexitatea medie
calculeaz complexitatea general
2 Dac Favorabil = Defavorabil = Mediu (nu avem cazuri favorabile/defavorabile)
calculeaz complexitatea

Calculeaz complexitatea:
dac avem recuren
calculeaz folosind egaliti
altfel
calculeaz folosind sume
Curs 9 Complexitatea algoritmilor
Recursivitate
Complexitate

Curs 10 Cutri sortri


Cutri
Sortri

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