Sunteți pe pagina 1din 247

Fundamentele Programrii

Limbajul de programare
PYTHON
Ce este programarea
Hardware / software
Hardware - computere(desktop,laptop, etc) i alte dispozitive (mobile, atm,
etc)
Software - programe sau sisteme ce ruleaz pe hardware
Limbaj de programare Notaii i reguli pentru scrierea de programe
(sintax i semantic)
Python: Limbaj de programare de nivel nalt (high level programming
language).
Interpretor Python: un program care permite rularea/interpretarea
programelor scrise in limbajul Python.
Biblioteci Python: Funcii, module, tipuri de date disponibile n Python,
scrise de ali programatori

Program 1 - Hello world

print (Hello world)


Ce fac computerele
Stocheaz date
Memoria intern
Memoria extern (hard, stick, CD, etc)
Opereaz
procesor
Comunic
Prin tastatur, mouse, ecran
Conexiuni de tip reea

Informaii i date
Date - o colecie de simboluri stocate ntr-un computer (Ex. 123 decimal
sau irul de caractere abc) sunt stochate folosind reprezentarea binara
Informaii - interpretarea unor date (Ex. 123, abc)
Procesarea datelor i informaiilor
Dispozitivele de intrare transform informaiile n date (ex. 123
citit de la tastatur)
Datele sunt stocate n memorie (ex. 1111011 pentru numrul
123)
Dispozitivele de ieire produc informaii din date
Operaii de baz ale procesoarelor
n reprezentare binar
Operaii (and, or, not; add, etc)
Elemente de baz ale unui program Python
Program 2 - Adding two integers

# Reads two integers and prints the sum of them


a = input("Enter the first number: ")
b = input("Enter the second number: ")
c = int(a) + int(b)
print("The sum of ", a, " + ", b, " is ", c)

Elemente lexicale
Un program Python este alctuit din mai multe linii de cod
Comentarii
ncep cu # i in pn la sfritul liniei
ncep cu i in mai multe rnduri, pn la un nou

Identificatori:secvene de caractere (litere, cifre, _) care ncep cu o liter


sau cu _

Literali: notaii pentru valorile constante sau pentru tipuri definite de


utilizator
Modelul de date
Toate datele ntr-un program Python obiecte
Un obiect are :
o identitate adresa lui n memorie
un tip care determin operaiile posibile precum i valorile pe
care le poate lua obiectul
o valoare.

Odat creat, identitatea i tipul obiectului nu mai pot fi


modificate.
Valoarea unor obiecte se poate modifica
Obiecte mutabile - se poate modifica
Obiecte ne-mutabile nu se poate modifica
Tipuri de date standard
Tipul de date definete domeniul de valori posibile i operaiile permise asupra
valorilor din domeniu.

Numerice Numerele sunt inmutabile odat create valoare nu se mai poate


schimba (operaiile creaz noi obiecte).

int (numere ntregi):


numerele ntregi (pozitive i negative), dimensiune limitat doar de
memoria disponibil
Operaii: +, -, *, /, //, **, % comparare:==,!=,<, > operaii pe bii: |, ^,
&, <<, >>, ~
Literali: 1, -3

bool (boolean):
Valorile True i False.
Operaii: and, or, not
Literali: False, True; 0, 1

float (numere reale):


numerele reale (dubl precizie)
Operations: +, -, *, / comparare:==,!=,<, >
Literals: 3.14
Tipuri de date standard

Secvene:
Mulimi finite i ordonate, indexate prin numere ne-negative.
Dac a este o secven atunci:
len(a) returnez numrul de elemente;
a[0], a[1], , a[len(a)-1] elementele lui a.
Examples: [1, a]
Stringuri:
este o secven inmutabil;
caractere Unicode .
Literali: abc, abc

Liste
secven mutabil
ex: [] sau [1, a, [1, 3]]
Liste
operaii:
creare [7, 9]
accesare valori,lungime (index, len), modificare valori (listele sunt mutabile),
verificare daca un element este in lista (2 in [1, 2, 'a'])
stergere inserare valori (append,insert,pop) del a[3]
slicing, liste eterogene
listele se pot folosi in for
lista ca stiv(append, pop)
folositi instructiunea help(list) pentru mai multe detalii despre operaii posibile

# create # slicing
a = [1, 2, 'a'] print a[:2]
print (a) b = a[:]
x, y, z = a print (b)
print(x, y, z) b[1] = 5
print (b)
# indices: 0, 1, ..., len(a) 1 a[3:] = [7, 9]
print a[0] print(a)
print ('last element = ', a[len(a)-1]) a[:0] = [-1]
print(a)
# lists are mutable a[0:2] = [-10, 10]
a[1] = 3 print(a)
print a
# lists as stacks # nesting
stack = [1, 2, 3] c = [1, b, 9]
stack.append(4) print (c)
print stack
print stack.pop()
print stack
#generate lists using range #list in a for loop
l1 = range(10) l = range(0,10)
print l1 for i in l:
l2 = range(0,10) print i
print l2
l3 = range(0,10,2)
print l3
l4 = range(9,0,-1)
print l4
Tuple
Sunt secvene inmutabile. Conine elemente, indexat de la 0
Operations:
Crearea - packing (23, 32, 3)
eterogen
poate fi folosit in for
unpacking

# Tuples are immutable sequences # tuple with one item


# A tuple consists of a number of singleton = (12,)
values separated by commas print (singleton)
print (len(singleton))
# tuple packing
t = 12, 21, 'ab' #tuple in a for
print(t[0]) t = 1,2,3
for el in t:
# empty tuple (0 items) print el
empty = ()

# sequence unpacking # Tuples may be nested


x, y, z = t u = t, (23, 32)
print (x, y, z) print(u)
Dicionar
Un dicionar este o multime de perechi (cheie, valoare).
Cheile trebnuie sa fie imutabile.
Operations:
creare {} sau {'num': 1, 'denom': 2}
accesare valoare pe baza unei chei
adaugare/modificare pereche (cheie, valoare)
tergere pereche (cheie, valoare)
verificare dac cheia exist

#create a dictionary #set a value for a key


a = {'num': 1, 'denom': 2} a['num'] = 3
print(a) print(a)
print(a['num'])
#get a value for a key
print(a['num'])

#delete a key value pair #check for a key


del a['num'] if 'denom' in a:
print (a) print('denom = ', a['denom'])
if 'num' in a:
print('num = ', a['num'])
Variables

Variabl: Variabl in Python:


nume nume
valoare valoare
tip tip
domeniu domeniu
operaii operaii
locaie de memorie locaie de memorie

Introducerea unei variabile ntr-un program asignare

Expressi
O combinaie de valori, constante, variabile, operatori i funcii care sunt
interpretate conform regulilor de preceden, calculate i care produc o alt
valoare
Exemple:
numeric : 1 + 2
boolean: 1 < 2
string : 1 + 2

Funcii utile:
help(instructiune) - ajutor
id(x) identitatea obiectului
dir()
locals() / globals() - nume definite (variabile, funcii, module, etc)
Instruciuni

Operaiile de baz ale unui program. Un program este o secven de


instruciuni

Atribuire/Legare
Instructiunea =.
Atribuirea este folosit pentru a lega un nume de o variabil
Poate fi folosit i pentru a modifica un element dintr-o secvena
mutabil.
Legare de nume:
x = 1 #x is a variable (of type int)
Re-legare name:
x = x + 2 #a new value is assigned to
x
Modificare secven:
y = [1, 2] #mutable sequence
y[0] = -1 #the first item is bound to
-1

Blocuri
Parte a unui program care este executat ca o unitate
Secven de instruciuni
Se realizeaz prin identarea liniilor (toate instruciunile identate
la acelai nivel aparin aceluiai bloc
Instruciuni - If, While
def gcd(a, b):
"""
Return the greatest common divisor of two positive integers.
"""
if a == 0:
return b
if b == 0:
return a

while a != b:
if a > b:
a = a - b
else:
b = b - a
return a

print gcd(7,15)
Instruciuni For
#use a list literal
for i in [2,-6,"a",5]:
print (i)

#using a variable
x = [1,2,4,5]
for i in x:
print (i)

#using range
for i in range(10):
print (i)

for i in range(2,100,7):
print (i)

#using a string
s = "abcde"
for c in s:
print (c)
Cum se scriu programe

Roluri n ingineria software


Programator/Dezvoltator
Folosete calculatorul pentru a scrie/dezvolta aplicaii
Client (stakeholders):
Cel interesat/afectat de rezultatele unui proiect.
Utilizatori
Folosesc/ruleaz programul.

Un proces de dezvoltare software este o abordare sistematic pentru


construirea, instalarea, ntreinerea produselor software. Indic:
Paii care trebuie efectuai.
Ordinea lor

Folosim la fundamentele programrii: un proces de dezvoltare


incremental bazat pe funcionaliti (simple feature-driven
development process)
Enun (problem statement)
Enunul este o descriere scurt a problemei de rezolvat.

Calculator - Problem statement


Profesorul (client) are nevoie de un program care ajut elevii (users) sa
invee despre numere raionale.
Programul ar trebui sa permite elevilor s efectueze operaii aritmetice
cu numere raionale

Cerine (requirements)
Cerinele definesc n detaliu de ce este nevoie n program din perspectiva
clientului. Definete:
Ce dorete clientul
Ce trebuie inclus n sistemul informatic pentru a satisface
nevoile clientuli.
Reguli de elaborare a cerinelor:
Cerinele exprimate corect asigur dezvoltarea sistemului
conform ateptrilor clienilor. (Nu se rezolv probleme ce
nu s-au cerut)
Descriu lista de funcionaliti care trebuie oferite de sistem.
Funcionalitile trebuie s clarifice orice ambiguiti din enun.
Funcionalitate

O funcie a sistemului dorit de client


descrie datele rezultatele i partea sistemul care este afectat
este de dimensiuni mici, poate fi implementat ntr-un timp relativ scurt
se poate estima
exprimat n forma aciune rezultat obiect
Aciunea o funcie pe care aplicaia trebuie s o furnizeze
Rezultatul este obinut n urma execuiei funciei
Obiect o entitate n care aplicaia implementeaz funcia

Calculator List de Funcionaliti


F1. Adun un numr raional n calculator.
F2. Sterge calculator.
F3. Undo reface ultima operaie (utilizatorul poate repeta
aceast operaie).
Proces de dezvoltare incremental bazat pe funcionaliti
Se creaz lista de funcionalitai pe baza enunului
Se planific iteraiile (o interaie conine una/mai multe
funcionaliti)
Pentru fiecare funcionalitate din iteraie
Se face modelare scenarii de rulare
Se creaz o lista de tascuri (activiti)
Se implementeaz i testeaz fiecare
activitate

Iteraie: O perioad de timp n cadrul creia se realizeaz o versiune


stabil i executabil a unui produs, mpreun cu documentaia suport

La terminarea iteraiei avem un program funcional care face ceva util


clientului

Examplu: plan de iteraii


Iteratio Planned features
n
I1 F1. Adun un numr raional n calculator.
I2 F2. Sterge calculator.
I3 F3. Undo reface ultima operaie (utilizatorul poate repeta
aceast operaie).
Modelare - Iteration modeling
La fiecare nceput de iteraie trebuie analizat funcionalitatea care urmeaza a fi
implementat.
Acest proces trebuie sa sigure nelegerea funcionalitii si sa rezulte un set de pai
mai mici (work item/task), activitai care conduc la realizarea funcionalitii
Fiecare activitate se poate implementa/testa independent

Iteraia 1 - Adun un numr raional n calculator.

Pentru programe mai simple putem folosi scenarii de rulare (tabelar) pentru a
nelege problema i modul n care funcionalitatea se manifest n program. Un
scenariu descrie interaciunea ntre utilizator i aplicaie.

Scenariu pentru funcionalitatea de adaugare numar raional


Utilizator Program Descriere

a 0 Tiprete totalul curent

b 1/2 Adun un numr raional

c 1/2 Tiprete totalul curent

d 2/3 Adun un numr raional

e 5/6 Tiprete totalul curent

f 1/6 Adun un numr raional

g 1 Tiprete totalul curent

h -6/6 Adun un numr raional

i 0 Tiprete totalul curent


List de activiti
Recomandri:
Definii o activitate pentru fiecare operaie care nu este
implementata deja (de aplicaie sa de limbajul Python), ex. T1,
T2.
Definii o activitate pentru implementarea interaciunii program-
utilizator (User Interface), ex. T4.
Definii o activitate pentru a implementa operaiile necesare
pentru interaciune utilizator cu UI, ex. T3.
Determinai dependenele ntre activiti (ex. T4 --> T3 --> T2
-->T1, unde --> semnific faptul ca o activitate depinde de o
alt activitate).
Facei un mic plan de lucru (T1,T2,T3,T4)

T1 Determinare cel mai mare divizor comun (punctele g, I din scenariu)


T2 Sum dou numere raionale (c, e, g, i)
T3 Implementare calculator: init, add, and total
T4 Implementare interfa utilizator
Activitate 1. Determinare cel mai mare divizor comun
Cazuri de testare
Un test case conine un set de intrri i rezultatele ateptate pentru fiecare intrare.
Date: a, b Rezultate: gcd (a, b): c, unde c este cel mai mare divizor comun

23 1

24 2

64 2

02 2

20 2

24 9 3

-2 0 ValueError

0 -2 ValueError
Programare procedural
Paradigm de programare
stil fundamental de scriere a programelor, set de convenii ce dirijeaz
modul n care gndim programele.

Programare imperativ
Calcule descrise prin instruciuni care modific starea programului. Orientat
pe aciuni i efectele sale

Programare procedural
Programul este format din mai multe proceduri (funcii, subrutine)
Ce este o funcie
O funcie este un bloc de instruciuni de sine stttor care are:
un nume,
poate avea o list de parametrii (formali),
poate returna o valoare
are un corp format din instructiuni
are o documentaie (specificaie) care include:
o scurt descriere
tipul i descriere parametrilor
condiii impuse paramterilor de intrare (precondiii)
tipul i descrierea valorii returnate
condiii impuse rezultatului, condiii care sunt
satisfcute n urma executrii (post-condiii).
Excepii ce pot s apar

def max(a, b):


"""
Compute the maximum of 2 numbers
a, b - numbers
Return a number - the maximum of two integers.
Raise TypeError if parameters are not integers.
"""
if a>b:
return a
return b
def isPrime(a):
"""
Verify if a number is prime
a an integer value (a>1)
return True if the number is prime, False otherwise
"""
Funcii
Toate funciile noastre trebuie s:
foloseasc nume sugestive (pentru numele funciei, numele variabilelor)
s ofer specificaii
s includ comentarii
s fie testat

O funcie ca i n exemplu de mai joi, este corect sintactic (funcioneaz n Python) dar
la laborator/examen nu consideram astfel de funcii:

def f(k):
l = 2
while l<k and k % l>0:
l=l+1
return l>=k

Varianta acceptat este:

def isPrime(nr):
"""
Verify if a number is prime
nr - integer number, nr>1
return True if nr is prime, False otherwise
"""
div = 2 #search for divider starting from 2
while div<nr and nr % div>0:
div=div+1
#if the first divider is the number itself than the number is prime
return div>=nr;
Definiia unei funcii n Python

Folosind instruciunea def se pot definii funcii n python.


Interpretorul executa instruciunea def, acesta are ca rezultat introducerea
numelui funciei (similar cu definirea de variabile)
Corpul funciei nu este executat, este doar asociat cu numele funciei

def max(a, b):


"""
Compute the maximum of 2 numbers
a, b - numbers
Return a number - the maximum of two integers.
Raise TypeError if parameters are not integers.
"""
if a>b:
return a
return b
Apel de funcii
Un bloc de instruciuni n Python este un set de instruciuni care este
executat ca o unitate. Blocurile sunt delimitate folosind identarea.
Corpul unei funcii este un bloc de instruciuni i este executat n momentul
n care funcia este apelat.

max(2,5)

La apelul unei funcii se creaz un nou cadru de execuie, care :


informaii administrative (pentru depanare)
determin unde i cum se continu execuia programului (dupa ce
execuia funciei se termin)
definiete dou spaii de nume: locals i globals care afecteaz
execuia funciei.
Spaii de nume (namespace)

este o mapare ntre nume (identificatori) i obiecte


are funcionaliti similare cu un dicionar (in general este
implementat folisind tipul dicionar)
sunt create automat de Python
un spaiu de nume poate fi referit de mai multe cadre de execuie

Adugarea unui nume n spaiu de nume: legare ex: x = 2


Modificarea unei mapri din spaiu de nume: re-legare

n Python avem mai multe spaiile de nume, ele sunt create n momente
diferite i au ciclu de viaa diferit.
General/implicit creat la pornirea interpretorului, conine denumiri
predefinite (built-in)
global creat la incrcarea unui modul, conine nume globale
globals() - putem inspecta spaiu de nume global
local creat la apelul unei funcii, conine nume locale funciei
locals() - putem inspecta spaiu de nume local
Transmiterea parametrilor
Parametru formal este un identificator pentru date de intrare. Fiecare apel
trebuie s ofere o valoare pentru parametru formal (pentru fiecare
parametru obligatoriu)
Parametru actual valoare oferit pentru parametrul formal la apelul
funciei.

Parametrii sunt transmii prin referin. Parametru formal


(identificatorul) este legat la valoarea (obiectul) parametrului actual.
Parametrii sunt introdui n spaiu de nume local

def change_or_not_immutable(a):
print ('Locals ', locals())
print ('Before assignment: a = ', a, ' id = ', id(a))
a = 0
print ('After assignment: a = ', a, ' id = ', id(a))

g1 = 1 #global immutable int


print ('Globals ', globals())
print ('Before call: g1 = ', g1, ' id = ', id(g1))
change_or_not_immutable(g1)
print ('After call: g1 = ', g1, ' id = ', id(g1))

def change_or_not_mutable(a):
print ('Locals ', locals())
print ('Before assignment: a = ', a, ' id = ', id(a))
a[1] = 1
a = [0]
print ('After assignment: a = ', a, ' id = ', id(a))

g2 = [0, 1] #global mutable list


print ('Globals ', globals())
print ('Before call: g2 = ', g2, ' id = ', id(g2))
change_or_not_mutable(g2)
print ('After call: g2 = ', g2, ' id = ', id(g2))
Vizibilitatea variabilelor
Domeniul de vizibilitate (scope) Definete vizibilitatea unui nume ntr-un
bloc.
Variabilele definite ntr-o funcie au domenul de vizibilitate local
(funcia) se poate accesa doar n interiorul funciei
Variabilele definite ntr-un modul au vizibilitate global (global pe
modul)
Orice nume (variabile, funcii) poate fi folosit doar dupa ce a fost legat
(prima atribuire)
Parametrii formali au domeniu de vizibilitate funcia (aparin spaiului
de nume local)

global_var = 100

def f():
local_var = 300
print local_var
print global_var
Domeniu de vizibilitate
Reguli de accesare a variabilelor (sau orice nume) ntr-o funcie:
cand se folosete un nume de variabil ntr-o funcie se caut n
urmtoarele ordine n spaiile de nume:
spaiu local
spaiu local funciei exterioare (doar dac avem funcie declarat
n interorul altei funcii)
spaiu global (nume definite n modul)
spaiul built-in
operatorul = schimba/creaz variabile n spaiu de nume local
Putem folosi declaraia global pentru a referi/importa o variabil
din spaiu de nume global n cel local
nonlocal este folosit pentru a referi variabile din funcia exterioar
(doar dac avem funcii n funcii

a = 100 a = 100
def f(): def f():
a = 300 global a
print (a) a = 300
print (a)
f()
print (a) f()
print (a)

globals() locals() - funcii built-in prin care putem inspecta spaiile de nume

a = 300
def f():
a = 500
print (a)
print locals()
print globals()

f()
print (a)
Cum scriem funcii Cazuri de testare
nainte s implementm funcia scriem cazuri de testare pentru:
a specifica funcia (ce face, pre/post condiii, excepii)
ca o metod de a analiza problema
s ne punem n perspectiva celui care folosete funcia
pentru a avea o modalitate sa testam dup ce implementm

Un caz de testare specific datele de intrare i rezultatele care le ateptam


de la funcie

Cazurile de testare:
se pot face n format tabelar, tabel cu date/rezultate.
executabile: funcii de test folosind assert
biblioteci/module pentru testare automat

Instruciunea assert permite inserarea de aseriuni (expressi care ar


trebui sa fie adevrate) n scopul depanrii/verificrii aplicaiilor.

assert expresie

Folisim assert pentru a crea teste automate


Funcii de test - Calculator
1 Funcionalitate 1. Add a number to calculator.
2 Scenariu de rulare pentru adugare numr
3 Activiti (Workitems/Tasks)
T1 Calculeaz cel mai mare divizor comun

T2 Sum dou numere raionale

T3 Implementare calculator: init, add, and total

T4 Implementare interfa utilizator

T1 Calculeaz cel mai mare divizor comun


Cazuri de testare Format tabelar Funcie de test
Input: (params a,b) Output: gdc(a,b)
def test_gcd():
23 1 assert gcd(2, 3) == 1
24 2 assert gcd(2, 4) == 2
assert gcd(6, 4) == 2
64 2
assert gcd(0, 2) == 2
02 2 assert gcd(2, 0) == 2
20 2 assert gcd(24, 9) == 3

24 9 3

Implementare gdc
def gcd(a, b):
"""
Compute the greatest common divisor of two positive integers
a, b integers a,b >=0
Return the greatest common divisor of two positive integers.
"""
if a == 0:
return b
if b == 0:
return a
while a != b:
if a > b:
a = a - b
else:
b = b - a
return a
Cum se scriu funcii
Dezvoltare dirijat de teste (test-driven development - TDD)
Dezvoltarea drijat de teste presupune crearea de teste automate, chiar
nainte de implementare, care clarific cerinele
Paii TDD pentru crearea unei funcii:
Addaug un test
Scriei o funcie de test (test_f()) care conine
cazuri de testare sub forma de aseriuni
(instruciuni assert).
La acest pas ne concentram la specificaiile funciei
f.
Definim funcia f: nume, parametrii, precondiii,
post-condiii, i corpul gol (instruciunea pass).
Rulm toate testele i verificm ca noul test pic
Pe parcursul dezvoltrii o sa avem mai multe funcii,
astfel o s avem mai multe funcii de test .
La acest pas ne asigurm ca toate testele
anterioare merg, iar testul nou adugat pic.
Scriem corpul funciei
La acest pas avem deja specificaiile, ne
concentrm doar la implementarea funciei conform
specificaiilor i ne asigurm ca noile cazuri de test
scrise pentru funcie trec (funcia de test)
La acest pas nu ne conentrm la aspecte
technice (cod duplicat, optimizri, etc).
Rulm toate testele i ne asigurm c trec
Rulnd testele ne asigurm c nu am stricat nimic i
noua funcie este implementat conform
specificaiilor
Refactorizare cod
La acest pas inbuntim codul, folosind
refactorizri
Dezvoltare dirijat de teste (test-driven development - TDD)
Dezvoltarea drijat de teste presupune crearea de teste automate, chiar
nainte de implementare, care clarific cerinele

Paii TDD pentru crearea unei funcii:


Addaug un test creaz teste automate
Rulm toate testele i verificm ca noul test pic
Scriem corpul funciei
Rulm toate testele i ne asigurm c trec
Refactorizm codul
TDD Pas 1. Creare de teste automate
Cnd lucrm la un task ncepem prin crearea unei funcii de test

Task: Calculeaza cel mai mare divizor comun

def test_gcd():
"""
test function for gdc
"""
assert gcd(0, 2) == 2
assert gcd(2, 0) == 2
assert gcd(2, 3) == 1
assert gcd(2, 4) == 2
assert gcd(6, 4) == 2
assert gcd(24, 9) == 3

Ne concentrm la specificarea funciei.


def gcd(a, b):
"""
Return the greatest common divisor of two positive integers.
a,b integer numbers, a>=0; b>=0
return an integer number, the greatest common divisor of a and b
"""
pass
TDD Pas 2 - Rulm testele
#run the test - invoke the test function
test_gcd()

Traceback (most recent call last):


File "C:/curs/lect3/tdd.py", line 20, in <module> test_gcd()
File "C:/curs/lect3/tdd.py", line 13, in test_gcd
assert gcd(0, 2) == 2
AssertionError

Validm c avem un test funcional se execut, eueaz.

Astfel ne asigurm c testul este executat i nu avem un test care


trece far a implementa ceva testul ar fi inutil
TDD Pas 3 Implementare
implementare funcie conform specificaiilor (pre/post condiii),
scopul este sa trac testul
soluie simpl, fr a ne concentra pe optimizri, evoluii
ulterioare, cod duplicat, etc.

def gcd(a, b):


"""
Return the greatest common divisor of two positive integers.
a,b integer numbers, a>=0; b>=0
return an integer number, the greatest common divisor of a and b
"""
if a == 0:
return b
if b == 0:
return a
while a != b:
if a > b:
a = a - b
else:
b = b - a
return a
TDD Pas 4 Executare funcii de test-
toate cu succes
>>> test_gcd()
>>>

Dac toate testele au trecut codul este testat, e conform


specificaiilor i nu s-au introdus erori (au trecut i testele scrise
anterior)

TDD Pas 5 Refactorizare cod


restructurearea codului folosind refactorizri
Refactorizare
Restructurarea codului, alternd structura intern fr a modifica
comportamentul observabil.
Scopul este de a face codul mai uor de:
neles
ntreinut
extins

Semnale c este nevoie de refactorizare (code smell) elemente ce pot


indica probleme mai grave de proiectare:
Cod duplicat
Metode lungi
Liste lungi de paramtetrii
Instruciuni condiionale care determin diferene de
comportament
Refactorizare: Redenumire funcie/variabil
redenumim funcia/variabila alegnd un nume sugestiv
def verify(k): def isPrime(nr):
""" """
Verify if a number is prime Verify if a number is prime
nr - integer number, nr>1 nr - integer number, nr>1
return True if nr is prime return True if nr is prime
""" """
l = 2 div = 2 #search for divider
while l<k and k % l>0: while div<nr and nr % div>0:
l=l+1 div=div+1
return l>=k #if the first divider is the
# number itself than nr is prime
return div>=nr;
Refactorizare: Extragerea de metode
parte dintr-o funcie se transform ntr-o funcie
separat
o expresie se transform ntr-o funcie
.
def startUI(): def getUserCommand():
list=[] """
print (list) Print the application menu
#read user command return the selected menu
menu = """ """
Enter command: menu = """
1-add element Enter command:
0-exit 1-add element
""" 0-exit
print(menu) """
cmd=input("") print(menu)
while cmd!=0: cmd=input("")
if cmd==1: return cmd
nr=input("Give element:")
add(list, nr) def startUI():
print list list=[]
#read user command print list
menu = """ cmd=getUserCommand()
Enter command: while cmd!=0:
1-add element if cmd==1:
0-exit nr=input("Give element:")
""" add(list, nr)
print(menu) print list
cmd=input("") cmd=getUserCommand()

startUI() startUI()
Refactorizare: Substituire algoritm
def isPrime(nr): def isPrime(nr):
""" """
Verify if a number is prime Verify if a number is prime
nr - integer number, nr>1 nr - integer number, nr>1
return True if nr is prime return True if nr is prime
""" """
div = 2 #search for divider for div in range(2,nr):
while div<nr and nr % div>0: if nr%div == 0:
div=div+1 return False
#if the first divider is the return True
# number itself than nr is prime
return div>=nr;
Calculator versiune procedural
Modular programming

Descompunerea programului n module (componente separate


interschimbabile) avnd n vedere:
separarea conceptelor
coeziunea elementelor dintr-un modul
cuplarea ntre module
ntreinerea i reutilizarea codului
arhitectura stratificat

Modulul este o unitate structural separat, interschimbabil cu


posibilitatea de a comunica cu alte module.
O colecie de funcii i variabile care implementeaz o funcionalitate bine
definit
Modul n Python
Un modul n Python este un fiier ce conine instruciuni i definiii Python.
Modul
nume: Numele fiierului este numele modulului plus extensia .py
variabila __name__
este __main__ dac modulul este executat de sine stttor
este numele modulului
docstring: Comentariu multiline de la nceputul modulului. Ofer o
descriere a modulului: ce conine, care este scopul, cum se foloseste, etc.
Variabila __doc__
instruciuni: definiii de funcii, variabile globale per modul, cod de
iniializare
Import de module
Modulul trebuie importat nainte de a putea folosi.
Instruciunea import:
Caut n namespace-ul global, dac deja exist modulul
nseamn ca a fost deja importat i nu mai e nevoie de alte
aciuni
Caut modulul i dac nu gasete se arunc o eroarea
ImportError
Dac modulul s-a gsit, se execut instruciunile din modul.

Instruciunile din modul (nclusiv definiiile de funcii) se execut doar o


singur dat (prima dat cnd modulul este importat n aplicaie).

from doted.package[module] import {module, function}}

from utils.numericlib import gcd

#invoke the gdc function from module utils.numericlib


print gdc(2,6)
from rational import *

#invoke the rational_add function from module rational


print rational_add(2,6,1,6)

import ui.console

#invoke the run method from the module ui.console


ui.console.run()
Calea unde se caut modulele (Module search path)
Instruciunea import caut fisierul modulname.py n:
directorul curent (directorul de unde s-a lansat aplicaia)
n lista de directoare specificat n variabila de mediu
PHYTONPATH
n lista de directoare specificat n variabila de mediu
PYTHONHOME (este calea de instalare Python; de exemplu
pe Unix, n general este .:/usr/local/lib/python.

Iniializare modul
Modulul poate conine orice instruciuni. Cnd modulul este importat prima
dat se execut toate instruciunile.Putem include instruciuni (altele dect
definiiile de funcii) care iniializeaz modulul.
Domeniu de vizibilitate n modul
La import:
se creaz un nou spaiu de nume
variabilele i funciile sunt introduse n noul spaiu de nume
doar numele modulului (__name__) este adugat n spaiul de nume
curent.

Putem folosi instruciunea built-in dir() dir(module_name) pentru a


examina coninutul modulului

#only import the name ui.console into the current symbol table
import ui.console

#invoke run by providing the doted notation ui.console of the


package
ui.console.run()

#import the function name gdc into the local symbol table
from utils.numericlib import gcd

#invoke the gdc function from module utils.numericlib


print gdc(2,6)

#import all the names (functions, variables) into the local


symbol table
from rational import *

#invoke the rational_add function from module rational


print rational_add(2,6,1,6)
Pachete n Python
Modalitate prin care putem structura modulele.
Dac avem mai multe module putem organiza ntr-o structur de directoare
Putem referi modulele prin notaia pachet.modul

Fiecare director care conine pachete trebuie sa conin un fiier __init__.py. Acesta
poate conine i instruciuni (codul de iniializare pentru pachet)
Eclipse + PyDev IDE (Integrated Development Environment)
Eclipse: Mediu de dezvoltare pentru python (printre altele).
Pydev: Plugin eclipse pentru dezvoltare aplicaii Python n Eclipse

Permite crearea, rularea, testarea, depanarea de aplicaii python

Instalare:
Java JRE 7 (varianta curenta de PyDev funcioneaz doar cu aceast
versiune de Java
Eclipse Luna (sau alta versiune de eclipse de la 3.5 n sus)
Instalat pluginul de PyDev

Detalii pe: pydev.org


http://pydev.org/manual_101_install.html

Proiect

Editor Python

ProjectExplorer: Pachete/Module

Outline: Funcii

Rulare/Depanare programe
Cum organizm aplicaia pe module i pachete

Se creaz module separate pentru:


Interfa utilizator - Funcii legate de interaciunea cu
utilizatorul. Conine instruciuni de citire tiprire, este
singurul modul care conine tiparire-citire
Domeniu (Domain / Application) Conine funcii legate de
domeniul problemei
Infrastructur Funcii utilitare cu mare potenial de
refolosire (nu sunt strict legate de domeniul problemei)
Coordonator aplicaie Iniializare/configurare i pornire
aplicaie
Calculator versiune modular
Organizarea aplicaiei pe funcii i module
Responsabiliti
Responsabilitate motiv pentru a schiba ceva
responsabilitate pentru o funcie: efectuarea unui calcul
responsabilitate modul: responsabilitile tuturo funciilor din modul

Principul unei singure responsabiliti - Single responsibility principle


(SRP)

O funcie/modul trebuie s aib o singur responsabilitate (un singur motiv de schimbare).

#Function with multiple responsibilities


#implement user interaction (read/print)
#implement a computation (filter)
def filterScore():
st = input("Start score:")
end = input("End score:")
for c in l:
if c[1]>st and c[1]<end:
print c

Multiple responsibiliti conduc la


Dificulti n nelegere i utilizare
Imposibilitatea de a testa
Imposibilitatea de a refolosi
Dificulti la ntreinere i evoluie
Separation of concerns

Principiu separrii responsabilitilor - Separation of concerns (SoC)


procesul de separare a unui program n responsabiliti care nu se suprapun

def filterScoreUI(): def testScore():


st = input("Start sc:") l = [["Ana", 100]]
end = input("End sc:") assert filterScore(l,10,30)==[]
rez = filtrareScore(l,st, end) assert filterScore(l,1,30)==l
for e in rez: l = [["Ana", 100],["Ion", 40],["P", 60]]
print e assert filterScore(l,3,50)==[["Ion", 40]]

def filterScore(l,st, end):


"""
filter participants
l - list of participants
st, end - integers -scores
return list of participants
filtered by st end score
"""
rez = []
for p in l:
if p[1]>st and p[1]<end:
rez.append(p)
return rez

Dependene
funcia: apeleaz o alt funcie
modul: orice funcie din modul apelaz o funcie din alt modul

Pentru a uura ntreinerea aplicaiei este nevoie de gestiunea dependenelor


Cuplare

Msoar intensitatea legturilor dintre module/funcii

Cu ct exist mai multe conexiuni ntre module cu att modulul este mai greu de neles,
ntreinut, refolosit i devine dificil izolarea prolemelor cu ct gradul de cuplare este mai
sczut cu att mai bine

Gradul de cuplare sczut(Low coupling) faciliteaz dezvoltarea de aplicai care pot fi uor
modificate (interdependena ntre module/funcii este minim astfel o modificare afecteaz doar
o parte bine izolat din aplicaie)
Coeziunea
Msoar ct de relaionate sunt resposabilitile unui element din program (pachet,modul,
clas)
Modulul poate avea:
Grad de coeziune ridicat (High Cohesion): elementele implementeaz
responsabiliti nrudite
Grad de coeziune sczut (Low Cohesion): implementeaz responsabiliti
diverse din arii diferite (fr o legatur conceptual ntre ele)

Un modul puternic coeziv ar trebui s realizeze o singur sarcin i sa necesite interaciuni


minime cu alte pri ale programului.

Dac elementele modulului implementeaza responsabiliti disparate cu att modulul este mai
greu de neles/ntreinut Modulele ar trebui sa aib grad de coeziune ridicat
Arhitectur stratificat (Layered Architecture)

Structurarea applicaiei trebuie s aib n vedere :


Minimizarea cuplrii ntre module (modulele nu trebuie sa cunoasc detalii despre alte
module, astfel schimbrile ulteroare sunt mai uor de implementat)
Maximizare coeziune pentru module (coninutul unui modul izoleaza un concept bine
definit)

Arhitectur stratificat este un ablon arhitectural care permite dezvoltarea de sisteme flexibile
n care componentele au un grad ridicat de independen

Fiecare strat comunic doar cu startul imediat urmtor (depinde doar doar de stratul
imediat urmtor)
Fiecare strat are o interfa bine definit (se ascun detaliile), interfa folosit de stratul
imediat superior
Arhitectur stratificat

Nivel prezentare (User interface / Presentation )


implementeaz interfaa utilizator (funcii/module/clase)

Nivel logic (Domain / Application Logic)


ofer funcii determinate de cazurile de utilizare
implementeaza concepte din domeniul aplicaiei
Infrastructur
funcii/module/clase generale, utilitare

Coordonatorul aplicaiei (Application coordinator)


asambleaz i pornete aplicaia
Layered Architecture simple example
#Ui
def filterScoreUI(): #manage the user interaction
st = input("Start sc:")
end = input("End sc:")
rez = filterScoreDomain(st, end)
for e in rez:
print (e)

#domain
l = [["Ion",50],["Ana",30],["Pop",100]]
def filterScoreDomain(st, end): #filter the score board
global l
rez = filterMatrix(l, 1, st, end)
l = rez
return rez

#Utility function - infrastructure


def filterMatrix(matrice, col, st, end): #filter matrix lines
linii = []
for linie in matrice:
if linie[col]>st and linie[col]<end:
linii.append(linie)
return linii
Organizarea proiectelor pe pachete/module
Erori i excepii
Erori de sintax erori ce apar la parsarea codului
while True print("Ceva"):
pass
File "d:\wsp\hhh\aa.py", line 1
while True print("Ceva"):
^
SyntaxError: invalid syntax

Codul nu e corect sintactic (nu respect regulile limbajului)


Excepii
Erori detectate n timpul rulrii.

Excepiile sunt aruncate n momentul n care o eroare este detectat:


pot fi aruncate de interpretorul python
aruncate de funcii pentru a semnala o situaie exceptional, o eroare
ex. Nu sunt satisfcute precondiiile

>>> x=0
>>> print 10/x

Trace back (most recent call last):


File "<pyshell#1>", line 1, in <module>
print 10/x
ZeroDivisionError: integer division or modulo by zero
def rational_add(a1, a2, b1, b2):
"""
Return the sum of two rational numbers.
a1,a2,b1,b2 integer numbers, a2<>0 and b2<>0
return a list with 2 int, representing a rational number a1/b2 + b1/b2
Raise ValueError if the denominators are zero.
"""
if a2 == 0 or b2 == 0:
raise ValueError("0 denominator not allowed")
c = [a1 * b2 + a2 * b1, a2 * b2]
d = gcd(c[0], c[1])
c[0] = c[0] / d
c[1] = c[1] / d
return c
Modelul de execuie (Execution flow)

Excepiile intrerup execuia normal a instruciunilor


Este un mechanism prin care putem ntrerupe execuia normal a unui bloc de instruciuni
Programul contiun execuia n punctul n care excepia este tratat (rezolvat) sau ntrerupe de
tot programul

def compute(a,b):
print ("compute :start ")
aux = a/b
print ("compute:after division")
rez = aux*10
print ("compute: return")
return rez

def main():
print ("main:start")
a = 40
b = 1
c = compute(a, b)
print ("main:after compute")
print ("result:",c*c)
print ("main:finish")

main()
Tratarea excepiilor (Exception handling)
Procesul sistematic prin care excepiile aprute n program sunt gestionate, executn
aciuni necesare pentru remedierea situaiei.

try:
#code that may raise exceptions
pass
except ValueError:
#code that handle the error
pass

Excepiile pot fi tratate n blocul de instruciuni unde apar sau n orice bloc exterior care
in mod direct sau indirect a apelat blocul n care a aprut excepia (excepia a fost
aruncat)
Dac excepia este tratat, acesta oprete rularea programului

raise, try-except statements


try:
calc_add (int(m), int(n))
printCurrent()
except ValueError:
print ("Enter integers for m, n, with n!=0")
Tratarea selectiv a excepiilor
avem mai multe clauze except,
este posibil sa propagm informaii despre excepie
clauza finally se execut n orice condiii (a aprut/nu a aprut excepia)
putem arunca excepii proprii folosind raise

def f():
# x = 1/0
raise ValueError("Error Message") # aruncm excepie

try:
f()
except ValueError as msg:
print "handle value error:", msg
except KeyError:
print "handle key error"
except:
print "handle any other errors"
finally:
print ("Clean-up code here")

Folosii excepii doar pentru:


A semnala o eroare semnalam situaia n care funcia nu poate respecta
post condiia, nu poate furniza rezultatul promis n specificaii
Putem folosi pentru a semnala nclcarea precondiiilor

Nu folosii excepii cu singurul scop de a altera fluxul de execuie


Specificaii
Nume sugestiv
scurta descriere (ce face funcia)
tipul i descrierea parametrilor
condiii asupra parametrilor de intrare (precondiii)
tipul, descrierea rezultatului
relaia ntre date i rezultate (postcondiii)
Excepii care pot fi aruncate de funcie, i condiiile in care se arunc

def gcd(a, b):


"""
Return the greatest common divisor of two positive integers.
a,b integer numbers
return an integer number, the greatest common divisor of a and b
Raise ValueError if a<=0 or b<=0
"""
Review calculator modular
Cateva probleme:
Starea calculatorului:
varianta cu variabil global:
avem mai multe variabile globale care pot fi cu uurint accesate din exterior
(posibil stricand starea calculatorului)
variabila global face testarea mai dificil
nu este o legtur clar ntre aceste variabile (starea calculatorului este
mprtiat n cod)
varianta far variabile globale:
starea calculatorului este expus (nu exist garanii ca metodele se apeleaza cu
un obiect care reprezint calculatorul)
trebuie sa transmitem starea, ca parametru pentru fiecare funcie legat de
calculator
Numere raionale
reprezentarea numerelor este expusa: ex: rez= suma(total[0],total[1],a,b) ,
putem cu uurina altera numrul raional (ex. Facem total[0] = 8 care posibil
duce la nclcarea reguli cmmdc(a,b) ==1 pentru orice numrul raional a/b
codul pentru adunare, nmultire, etc de numere rationale este diferit de modul in care
facem operaii cu numere intregi. Ar fi de preferat sa putem scrie r = r1+r2 unde
r,r1,r2 sunt numere rationale
Programare orientat obiect
Este metod de proiectare i dezvoltare a programelor:
Ofer o abstractizare puternic i flexibil
Programatorul poate exprima soluia n mod mai natural (se concentreaz
pe structura soluiei nu pe structura calculatorului)
Descompune programul ntr-un set de obiecte, obiectele sunt elementele
de baz
Obiectele interacioneaza pentru a rezolva problema, exist relaii ntre
clase
Clasele introduc tipuri noi de date, modeleaza elemente din spaiul
problemei, fiecare obiect este o instana a unui tip de data (clas)
Clas
Defineste in mod abstract caracteristicile unui lucru.
Descrie dou tipuri de atribute:
cmpuri (proprietati) descriu caracteristicile
metode (operaii) descriu comportamentul

Clasele se folosesc pentru crearea de noi tipuri de date (tipuri de date definite de utilizator)
Tip de date:
domeniu
operatii

Clasele sunt folosite ca un sablon pentru crearea de obiecte (instante), clasa defineste
elementele ce definesc starea si comportamentul obiectelor.
Definitie de clas n python

class MyClass:
<statement 1>
.
<statement n>

Este o instruciune executabil, introduce un nou tip de date cu numele specificat.

Instruciunile din interiorul clasei sunt n general definiii de funcii, dar i alte instruciuni
sunt permise

Clasa are un spaiu de nume propriu, definiiile de funcii din interiorul clasei (metode)
introduc numele funciilor n acest spaiu de nume nou creat. Similar i pentru variabile
Obiect
Object (instan) este o colecie de date i funcii care opereaz cu aceste date

Fiecare obiect are un tip, este de tipul clasei asociate: este instaa unei clase

Obiectul:
inglobeaz o stare: valorile campurilor
folosind metodele:
putem modifica starea
putem opera cu valorile ce descriu starea obiectelor

Fiecare obiect are propiul spaiu de nume care conine campurile i metodele.
Creare de obiecte. Creare de instane a unei clase (__init__)
Instanierea une clase rezulte in obiecte noi (instane). Pentru creara de obiecte se
foloseste notaie similar ca i la funcii.

x = MyClass()

Operaia de instaniere (apelul unei clase) creaz un obiect nou, obiectul are tipul
MyClass

O clas poate defini metoda special __init__ care este apelat n momentul instanierii

class MyClass:
def __init__(self):
self.someData = []

__init__ :
creaz o instan
foloseste self pentru a referi instana (obiectul) curent (similar cu this din alte
limbaje orientate obiect)

Putem avea metoda __init__ care are i ali parametrii n afar de self
Campuri

x = RationalNumber(1,3)
y = RationalNumber(2,3)
x.m = 7
x.n = 8
y.m = 44
y.n = 21

class RationalNumber:
"""
Abstract data type for rational numbers
Domain: {a/b where a and b are integer numbers b!=0}
"""

def __init__(self, a, b):


"""
Creates a new instance of RationalNumber
"""
#create a field in the rational number
#every instance (self) will have this field
self.n = a
self.m = b

self.n = a vs n=a

1 Creaz un atribut pentru instana curent


2 Creaz o variabil local funciei
Metode
Metodele sunt funci definite in interiorul clasei care au acces la valorile campurilor unei
instane.

n Python metodele au un prim argument: instana curent


Toate metodele primesc ca prim parametru obiectul curent (self)

def testCreate():
"""
Test function for creating rational numbers
"""
r1 = RationalNumber(1,3) #create the rational number 1/3
assert r1.getNominator()==1
assert r1.getDenominator()==3
r1 = RationalNumber(4,3) #create the rational number 4/3
assert r1.getNominator()==4
assert r1.getDenominator()==3

class RationalNumber:
"""
Abstract data type rational numbers
Domain: {a/b where a,b integer numbers, b!=0, greatest common divisor
a, b =1}
"""
def __init__(self, a, b):
"""
Initialize a rational number
a,b integer numbers
"""
self.__nr = [a, b]

def getDenominator(self):
"""
Getter method
return the denominator of the rational number
"""
return self.__nr[1]

def getNominator(self):
""""
Getter method
return the nominator of the method
"""
return self.__nr[0]
Metode speciale. Suprncrcarea operatorilor. (Operator overloading)
__str__ - conversie in tipul string (print representation)
def __str__(self):
"""
provide a string representation for the rational number
return a string
"""
return str(self.__nr[0])+"/"+str(self.__nr[1])

__lt__ , __le__, __gt__, __ge__- comparaii (<,<=,>,>=)


def testCompareOperator(): def __lt__(self, ot):
""" """
Test function for < > Compare 2 rational numbers (less than)
""" self the current instance
r1 = RationalNumber(1, 3) ot a rational number
r2 = RationalNumber(2, 3) return True if self<ot,False otherwise
assert r2>r1 """
assert r1<r2 if self.getFloat()<ot.getFloat():
return True
return False

__eq__ - verify if equals


def testEqual(): def __eq__(self, other):
""" """
test function for == Verify if 2 rational are equals
""" other - a rational number
r1 = RationalNumber(1, 3) return True if the instance is
assert r1==r1 equal with other
r2 = RationalNumber(1, 3) """
assert r1==r2 return self.__nr==other.__nr
r1 = RationalNumber(1, 3)
r1 = r1.add(RationalNumber(2, 3))
r2 = RationalNumber(1, 1)
assert r1==r2
Operator overloading

__add__(self, other) - pentru a folosi operatorul +


def testAddOperator(): def __add__(self,other):
""" """
Test function for the + operator Overload + operator
""" other - rational number
r1 = RationalNumber(1,3) return a rational number,
r2 = RationalNumber(1,3) the sum of self and other
r3 = r1+r2 """
assert r3 == RationalNumber(2,3) return self.add(other)

Metoda __mul__(self, other) - pentru operatorul *

Metoda __setItem__(self,index, value) dac dorim ca obiectele noastre sa se comporte


similar cu liste/dicionare, sa putem folosi []
a = A()
a[index] = value

__getItem__(self, index) sa putem foloi obiecul ca si o secven


a = A()
for el in a:
pass

__len__(self) - pentru len

__getslice__(self,low,high) - pentru operatorul de slicing


a = A()
b = a[1:4]

__call__(self, arg) - to make a class behave like a function, use the ()


a = A()
a()
Vizibilitate i spaii de nume n Python

Spaiu de nume (namespace) este o mapare intre nume i obiecte


Namespace este implementat n Python folosind dictionarul
Cheie: Nume
Valoare Object

Clasa introduce un nou spaiu de nume


Metodele, campurile sunt nt-un spaiu de nume sparat, spaiu de nume corespunztor
clase.

Toate regulile (legare de nume, vizibilitate/scope, paramterii formali/actuali, etc.) legate de


denumiri(funciion, variable) sunt acelasi pentru attributele clasei (methode, campuri) ca si
pentru orice alt nume in python, doar trebuie luat n considerare ca avem un namespace dedicat
clasei
Atribute de clas vs atribute de instane

Variabile membre (cmpuri)


atribute de instae valorile sunt unice pentru fiecare instan (obiect)
atribute de clas valoarea este partajata de toate instanele clasei (toate obiectele de
acelai tip)

class RationalNumber:
"""
Abstract data type for rational numbers
Domain: {a/b where a and b are integer numbers b!=0}
"""
#class field, will be shared by all the instances
numberOfInstances =0

def __init__(self, a, b):


"""
Creates a new instance of RationalNumber
"""
self.n = a
self.m = b
RationalNumber.numberOfInstances+=1 # accessing class fields

numberOfInstances =0
def __init__(self,n,m):
self.n = n
self.m = m
RationalNumber.numberOfInstances+=1

def testNumberInstances():
assert RationalNumber.numberOfInstances == 0
r1 = RationalNumber(1,3)
#show the class field numberOfInstances
assert r1.numberOfInstances==1
# set numberOfInstances from the class
r1.numberOfInstances = 8
assert r1.numberOfInstances==8 #access to the instance field
assert RationalNumber.numberOfInstances==1 #access to the class field

testNumberInstances()
Class Methods
Funcii din clas care nu opereaza cu o instana.
Alte limbaje: metode statice

class RationalNumber:
#class field, will be shared by all the instances
numberOfInstances =0

def __init__(self,n,m):
"""
Initialize the rational number
n,m - integer numbers
"""
self.n = n
self.m = m
RationalNumber.numberOfInstances+=1

@staticmethod
def getTotalNumberOfInstances():
"""
Get the number of instances created in the app
"""
return RationalNumber.numberOfInstances

def testNumberOfInstances():
"""
test function for getTotalNumberOfInstances
"""
assert RationalNumber.getTotalNumberOfInstances()==0
r1 = RationalNumber(2, 3)
assert RationalNumber.getTotalNumberOfInstances()==1

testNumberOfInstances()

ClassName.attributeName folosit pentru a accesa un atribut asociat clasei


(camp,metoda)
Decoratorul @staticmethod este folosit pentru a marca o funcie static. Aceste funcii
nu au ca prim argument (self) obiectul curent.
Principii pentru crearea de noi tipuri de date
ncapsulare

Datele care reprezint starea i metodele care manipuleaza datele sunt strns legate, ele
formeaz o unitate coeziv.
Starea si comportamentul ar trebui ncapsulat n acelasi unitate de program (clasa)

Ascunderea informaiilor
Reprezentarea interna a obiectelor (a strii) trebuie protejat fa de restul aplicaiei.
Ascunderea reprezentrii protejeaz integritatea datelor i nu permite modificarea starii din
exteriorul clasei, astfel se evit setarea, accidentala sau voita, unei stari inconsistente.

Clasa comunica cu exteriorul doar prin interfaa public (mulimea tuturor metodelor vizibile in
exterior) i ascunde orice detalii de implementare (modul n care am reprezentat datele,
algoritmii folosii,etc).

De ce:
Definirea unei interfee clare i ascunderea detaliilor de implementare asigur ca alte module
din aplicaie sa nu pot face modificri care ar duce la stari inconsistente. Permite evoluia
ulterioar (schimbare reprezentare, algoritmi etc) fr s afectm restul aplicaiei

Limitai interfaa (metodele vizibile n exterior) astfel nct s existe o libertate n modificarea
implementrii (modificare fr a afecta codul client)

Codul client trebuie s depind doar de interfaa clasei, nu de detalii de implementare. Dac
folosii acest principiu, atunci se pot face modificri fr a afecta restul aplicaiei
Membri publici. Membrii privai Ascunderea implementrii in Python

Trebuie sa protejm (ascundem) reprezentarea intern a clasei (implementarea)

In Python ascunderea implementrii se bazeaza pe convenii de nume.


_name sau __name pentru un atribut semnaleaza faptul ca atributul este privat

Un nume care incepe cu _ sau __ semnaleaza faptul ca atributul (camp, metode) ar trebui tratat
ca fiind un element care nu face parte din interfaa public. Face parte din reprezentarea intern
a clasei, nu ar trebui accesat din exterior.
Recomandri
Creai metode pentru a accesa campurile clasei (getter)
folositi conveniile de nume _,__ pentru a delimita interfaa public a clasei de
detaliile de implementare
Codul client ar trebui sa funcioneze (fr modificri) chiar daca schimbam
reprezentarea intern, atta timp ct interfaa public rmne neschimbat.
Clasa este o abstractizare, o cutie neagra (black box)
Specificaiile funciilor trebuie sa fie independente de reprezentare
Cum crem clase
Folosim Dezvoltare dirijat de teste

Specificaiile (doumentaia) pentru clase includ:


scurt descriere
domeniul ce fel de obiecte se pot crea. n general descrie campurile clasei
Constrngeri ce se aplic asupra datelor membre: Ex. Invariants condiii care sunt
adevrate pentru ntreg ciclu de viaa al obiectului
class RationalNumber:
"""
Abstract data type rational numbers
Domain:{a/b where a,b integer numbers, b!=0, greatest common divisor a, b =1}
Invariant:b!=0, greatest common divisor a, b =1
"""
def __init__(self, a, b):

Se creaza funcii de test pentru:


Crearea de instane
Fiecare metod din clas

Campurilie clasei (reprezentarea) se declar private (__nume). Se creaz metode getter pentru
a accesa cmpurile clasei
Tipuri abstracte de date (Abstract data types)
Tip abstract de date:
operaiile sunt specificate independent de felul n care operaia este
implementat
operaiile sunt specificate independent de modul de reprezentare a datelor

Un tip abstract de date este: Tip de date+ Abstractizarea datelor + ncapsulare

Review Calculator rational varianta orientat obiect

Putem schimba cu usurina reprezentarea intern pentru clasa RationalNumber (folosim


a,b n loc de lista [a,b])
Diagrame UML

Unified Modeling Language (UML) - este un limbaj standardizat de


modelare destinat vizualizrii, specificrii, modelrii i documentrii
aplicaiilor.

UML include un set de notaii grafice pentru a crea modele vizuale ce


descriu sistemul.
Diagrame de clase
Diagrama UML de clase (UML Class diagrams) descrie structura
sistemului prezentnd clasele,atributele i relaiile intre aceste clase
class RationalNumber:
def __init__(self, a, b):
"""
Initialize a rational number
a,b integer numbers
"""
self.__nr = [a, b]
def getDenominator(self):
"""
Getter method return the
denominator
"""
return self.__nr[1]
def getNominator(self):
""""
Getter method return the nominator
"""
return self.__nr[0]
def add(self, a):

Clasele sunt reprezentate prin dreptunghiuri ce conin trei zone:


Partea de sus numele clasei
Partea din mijloc cmpurile/atributele clasei
Partea de jos metodele/operaiile
Relaii UML
O relaie UML este un termen general care descrie o legatur logic ntre
dou elemente de pe o diagram de clase.
Un Link este relaia ntre obiectele de pe diagram. Este reprezentat
printr-o linie care conecteaza dou sau mai multe dreptunghiuri.

Associeri
Asocierile binare se reprezint printr-o linie ntre dou clase.

O asociere poate avea nume, capetele asocieri pot fi adnotate cu nume de


roluri, multiplicitate, vizibilitate i alte proprieti. Asocierea poate fi uni-
direcional sau bi-direcional
Agregare
Agregarea este o asociere specializat. Este o asociere ce reprezint
relaia de parte-ntreg (part-whole) sau apartenena (part-of).

class Car: class Engine:


def __init__(self,eng,col): def __init__(self,cap,type):
""" """
Initialize a car initialize the engine
eng - engine cap positive integer
col - string, ie White type string
""" """
self.__engine = eng self.__capacity = cap
self.__color = col self.__type = type
def getColor(self):
""" def getCapacity(self):
Getter method for color """
return string Getter method for the capacity
""" """
return self.__color return self.__capacity
def getEngine(self): def getType(self):
""" """
Getter method for engine Getter method for type
return engine return string
""" """
return self.__engine return self.__type
Dependene, Pachete
Relaia de dependen este o asociere n care un element depinde sau
foloseste un alte element
Exemple de dependene:
creaz instane
are un parametru
foloseste un obiect n interiorul unei metode
Principii de proiectare
Creaz aplicaii care:
Sunt uor de neles, modificat, ntreinut, testat
Clasele abstracte, ncapsulare, ascunderea reprezentrii, uor de
testat, uor de folosit i refolosit

Scop general: gestiunea dependenelor


Single responsibility
Separation of concerns
Low Coupling
High Cohesion

Enun (Problem statement)


Scriei un program care gestiuneaz studeni de la o facultate
(operaii CRUD Create Read Update Delete)

Funcionaliti (Features)
F1 Adauga student
F2 vizualizare studeni
F3 caut student
F4 terge student

Plan de iteraii
IT1 - F1; IT2 F2; IT3 F3; IT4 - F4
Scenariu de rulare (Running scenario)
user app description

a add a student

give student id

give name

Ion

new student added

a add student

give student id

give name

id already exists, name can


not be empty
Arhitectur stratificat (Layered architecture)
Layer (strat) este un mecanism de structurare logic a elementelor ce
compun un sistem software
ntr-o arhitectur multi-strat, straturile sunt folosite pentru a aloca
responsabiliti n aplicaie.
Layer este un grup de clase (sau module) care au acelai set de
dependene cu alte module i se pot refolosi n circumstane similare.

User Interface Layer (View Layer, UI layer sau Presentation


layer)
Application Layer (Service Layer sau GRASP Controller
Layer)
Domain layer (Business Layer, Business logic Layer sau
Model Layer)
Infrastructure Layer (acces la date modaliti de
persisten, logging, network I/O ex. Trimitere de email,
sau alte servicii technice)
abloane Grasp
General Responsibility Assignment Software Patterns (or Principles)
conin recomandri pentru alocarea responsabilitilor pentru clase obiecte
ntr-o aplicaie orientat obiect.

High Cohesion
Low Coupling
Information Expert
Controller
Protected Variations
Creator
Pure Fabrication
High Cohesion
Aloc responsabilitile astfel nct coeziunea n sistem rmne
ridicat

High Cohesion este un principiu care se aplic pe pacursul dezvoltrii n


ncercarea de a menine elementele n sistem:
responsabile de un set de activiti nrudite
de dimensiuni gestionabile
uor de neles

Coeziune ridicat (High cohesion) nseamna ca responsabilitile pentru un


element din sistem sunt nrudite, concentrate n jurul aceluiai concept.

nprirea programelor n clase i starturi este un exemplu de activitate care


asigur coeziune ridicat n sistem.

Alternativ, coeziune slab (low cohesion) este situaia n care elementele


au prea multe responsabiliti, din arii diferite. Elementele cu coeziune
slab sunt mai greu de neles, reutilizat , ntreinut i sunt o piedic pentru
modificrile necesare pe parcursul dezvoltrii unui sistem
Low Coupling
Aloc responsabiliti astfel nct cuplarea rmne slab (redus)

Low Coupling ncurajeaz alocarea de responsabilitai astfel nct avem:


dependene puine ntre clase;
inpact sczut n sistem la schimbarea unei clase;
potenial ridicat de refolosire;

Forme de cuplare:
TypeX are un cmp care este de TypeY.
TypeX are o medod care refer o instan de tipul TypeY n orce
form (parameterii, variabile locale, valoare returnat, apel la
metode)
TypeX ete derivat direct sau indirect din clasa TypeY.
Information Expert
Aloc responsabilitatea clasei care are toate informaiile necesare
pentru a ndeplini sarcina

Information Expert este un principiu care ajut s determinm care este


clasa potrivit care ar trebui s primeasc responsabilitatea (o metod
noua, un cmp, un calcul).

Folosind principiu Information Expert ncercm sa determinm care sunt


informaiile necesare pentru a realiza ce se cere, determinm locul n care
sunt aceste informaii i alocm responsabilitatea la clasa care conine
informaiile necesare.

Information Expert conduce la alocarea responsabilitii n clasa care


conine informaia necesar pentru implementare. Ajut s rspundem la
ntrebarea Unde se pune metoda, cmpul
Information Expert

Point of Sale application


Cine este responsabil cu calcului
totalului?

Avem nevoie de toate SaleItems


pentru a calcula totalul.

Information Expert Sale

Conform Expert

SaleItem ar trebui sa fie responsabil


cu calculul subtotalului (quantity *
price)

1. Menine ncapsularea
2. Promoveaz cuplare slab
3. Promoveaz clase puternic coezive
4. Poate sa conduc la clase complexe - dezavantaj
Creator
Crearea de obiecte este o activitate important ntr-un sistem orientat
obiect. Care este clasa responsabil cu crearea de obiecte este o
proprietate fundabental care definete relaia ntre obiecte de diferite
tipuri.

ablonul Creator descrie modul n care alocm responsabilitatea de a crea


obiecte n sistem

n general o clasa B ar trebui s aib responsibilitatea de a crea obiecte de


tip A dac unul sau mai multe (de preferat) sunt adevrate:
Instana de tip B conine sau agreg instane de tip A
Instana de tip B gestioneaz instane de tip A
Instana de tip B folosete extensiv instane de tip A
Instana de tip B are informaiile necesare pentru a iniializa
instana A.
Work items

Task
T1 Create Student
T2 Validate student
T3 Store student (Create repository)
T4 Add student (Create Controller)
T5 Create UI

Task: create Student


def testCreateStudent(): class Student:
""" def __init__(self, id, name, adr):
Testing student creation """
""" Create a new student
st = Student("1", "Ion", "Adr") id, name, address String
assert st.getId() == "1" """
assert st.getName() == "Ion" self.id = id
assert st.getAdr() == "Adr" self.name = name
self.adr = adr

def getId(self):
return self.id

def getName(self):
return self.name

def getAdr(self):
return self.adr
Protected Variations
Cum alocm responsabilitatea astfel nct variaiile curente i viitoare nu
vor afecta sistemul (nu va fi necesar o revizuire a sistemului, nu trebuie sa
facem schimbri majore n sistem)?

Protected variations: Crem o nou clas care ncapsuleaz aceste variaii.

ablonul Protected Variations protejeaz elementele sistemului de


variaiile/modificrile altor elemente din sistem (clase, obiecte, subsisteme)
ncapsulnd partea instabil ntr-o clas separat (cu o interfa public
bine delimitat care ulterior, folosind polimorfism, poate introduce variaii
prin noi implementri).
Task: Validate student
Design posibil pentru validare:

Algoritmul de validare:
Poate fi o metoda in clasa student
o metoda statica, o funcie
ncapsulat ntr-o clas separat
Poate semnala eroarea prin:
returnare true/false
returnare lista de erori
excepii care conin lista de erori

Clas Validator : aplic Principiu Protect Variation

def testStudentValidator(): class StudentValidator:


""" """
Test validate functionality Class responsible with validation
""" """
validator = StudentValidator() def validate(self, st):
st = Student("", "Ion", "str") """
try: Validate a student
validator.validate(st) st - student
assert False raise ValueError
except ValueError: if: Id, name or address is empty
assert True """
st = Student("", "", "") errors = ""
try: if (st.id==""):
validator.validate(st) errors+="Id can not be empty;"
assert False if (st.name==""):
except ValueError: errors+="Name can not be empty;"
assert True if (st.adr==""):
errors+="Address can not be
empty"
if len(errors)>0:
raise ValueError(errors)
Pure Fabrication
Cnd un element din sistem ncalc primcipiul coeziunii ridicate i cuplare
slab (n general din cauza aplicrii succesive a ablonului expert):
Aloc un set de responsabiliti la o clas artificial (clas ce nu reprezint
ceva n domeniul problemei) pentru a oferi coeziune ridicat, cuplare slab
i reutilizare

Pure Fabrication este o clas ce nu reprezint un concept din domeniul


problemei este o clas introdus special pentru a menine cuplare slab i
coeziune ridicat n sistem.

Problema: Stocare Student (in memorie, fiier sau baz de date)


Expert pattern Clasa Student este expert, are toate informaiile, pentru
a realiza aceast operaie
Pure Fabrication - Repository
Problema: Stocare Student (in memorie, fiier sau baz de date)
Expert pattern Clasa Student este expert, are toate informaiile, pentru
a realiza aceast operaie
Dac punem responsabilitatea persistenei in clasa Student, rezult o clas
slab coeziva, cu potenial limitat de refolosire

Soluie Pure Fabrication

Clas creat cu responsabilitatea de a


salva/persista obiecte Student

Clasa student se poate reutiliza cu uurin


are High cohesion, Low coupling

Clasa StudentRepository este responsabil cu


problema gestiunii unei liste de studeni (s
ofere un depozit - persistent pentru obiecte
de tip student)

ablonul Repository
Un repository reprezint toate obiectele de un anumit tip ca si o mulime
de obiecte.
Obiecte sunt adugate, terse, modificate iar codul din repository
insereaza, sterge obiectele dintr-un depozit de date persistent.
Task: Create repository
def testStoreStudent():
st = Student("1", "Ion", "Adr")
rep = InMemoryRepository()
assert rep.size()==0
rep.store(st)
assert rep.size()==1
st2 = Student("2", "Vasile", "Adr2")
rep.store(st2)
assert rep.size()==2
st3 = Student("2", "Ana", "Adr3")
try:
rep.store(st3)
assert False
except ValueError:
pass

class InMemoryRepository:
"""
Manage the store/retrieval of students
"""
def __init__(self):
self.students = {}

def store(self, st):


"""
Store students
st is a student
raise RepositoryException if we have a student with the same id
"""
if st.getId() in self.students:
raise ValueError("A student with this id already exist")

if (self.validator!=None):
self.validator.validate(st)

self.students[st.getId()] = st
GRASP Controller
Scop: decuplarea sursei de evenimente de obiectul care gestioneaz
evenimentul. Decuplarea startului de prezentare de restul aplicaiei.

Controller este definit ca primul obiect dup stratul de interfa utilizator.


Interfaa utilizator folosete un obiect controller, acest obiect este
responsabil de efectuarea operaiilor cerute de utilizator.
Controller coordoneaz (controleaz) operaiile necesare pentru a realiza
aciunea declanat de utilizator.

Controlerul n general folosete alte obiecte pentru a realiza operaia, doar


coordoneaz activitatea.

Controllerul poate ncapsula informaii despre starea curent a unui use-


case. Are metode care corespund la o aciune utilizator
Task: create controller
def tesCreateStudent():
"""
Test store student
"""
rep = InMemoryRepository()
val = StudentValidator()
ctr = StudentController(rep, val)
st = ctr.createStudent("1", "Ion", "Adr")
assert st.getId()=="1"
assert st.getName()=="Ion"
try:
st = ctr.createStudent("1", "Vasile", "Adr")
assert False
except ValueError:
pass
try:
st = ctr.createStudent("1", "", "")
assert False
except ValueError:
pass

class StudentController:
"""
Use case controller for CRUD Operations on student
"""
def __init__(self, rep, validator):
self.rep = rep
self.validator = validator

def createStudent(self, id, name, adr):


"""
store a student
id, name, address of the student as strings
return the Student
raise ValueError if a student with this id already exists
raise ValueError if the student is invalid
"""
st = Student(id, name, adr)
if (self.validator!=None):
self.validator.validate(st)
self.rep.store(st)
return st
Application coordinator
Dependency injection (DI) este un principiu de proiectare pentru
sisteme orientat obiect care are ca scop reducerea cuplrii ntre
componentele sistemului.

De multe ori un obiect folosete (depinde de) rezultatele produse de alte


obiecte, alte pri ale sistemului.

Folosind DI, obiectul nu are nevoie s cunoasc modul n care alte pri ale
sistemului sunt implementate/create. Aceste dependene sunt oferite (sunt
injectate), inpreun cu un contract (specificaii) care descriu
comportamentul componentei

#create validator
validator = StudentValidator()
#crate repository
rep = InMemoryRepository(None)
#create console provide(inject) a validator and a repository
ctr = StudentController(rep, validator)
#create console provide controller
ui = Console(ctr)
ui.showUI()

Review aplicaia student manager de revazut abloanele ce apar


Arhitectur stratificat (Layered architecture)
Layer (strat) este un mecanism de structurare logic a elementelor ce compun un
sistem software
ntr-o arhitectur multi-strat, straturile sunt folosite pentru a aloca responsabiliti n
aplicaie.
Layer este un grup de clase (sau module) care au acelai set de dependene cu alte
module i se pot refolosi n circumstane similare.

User Interface Layer (View Layer, UI layer sau Presentation layer)


Application Layer (Service Layer sau GRASP Controller Layer)
Domain layer (Business Layer, Business logic Layer sau Model
Layer)
Infrastructure Layer (acces la date modaliti de persisten,
logging, network I/O ex. Trimitere de email, sau alte servicii
technice)
Aplicaia StudentCRUD

Review applicaie
Entiti

Entitate (Entity) este un obiect care este definit de identitatea lui (se identific cu
exact un obiect din lumea real).

Principala caracteristic a acestor obiecte nu este valoarea atributelor, este


faptul ca pe intreg existenta lor (in memorie, scris in fisier, incarcat, etc) se mentine
identitatea si trebuie asigurat consistenta (sa nu existe mai multe entitati care descriu
acelai obiect).

Pentru astfel de obiecte este foarte important sa se defineasc ce inseamn a fi egale.

def testIdentity():
#attributes may change
st = Student("1", "Ion", "Adr")
st2 = Student("1", "Ion", "Adr2")
assert st==st2

#is defined by its identity


st = Student("1", "Popescu", "Adr")
st2 = Student("2", "Popescu", "Adr2")
assert st!=st2
class Student:
def __init__(self, id, name, adr):
"""
Create a new student
id, name, address String
"""
self.__id = id
self.__name = name
self.__adr = adr

def __eq__(self,ot):
"""
Define equal for students
ot - student
return True if ot and the current instance represent the same student
"""
return self.__id==ot.__id

Atributele entitii se poat schimba dar identitatea rmbe acelai (pe ntreg existena
lui obiectul reprezint acelai obiect din lumea real )

O identitate greit conduce la date invalide (data corruption) i la inposibilitatea de a


implementa corect anumite operaii.
Obiecte valoare (Value Objects)

Obiecte valoare: obiecte ce descriu caracteristicile unui obiect din lumea reala,
conceptual ele nu au identitate.
Reprezint aspecte descriptive din domeniu. Cnd ne preocup doar atributele unui
obiect (nu i identitatea) clasificam aceste obiecte ca fiind Obiecte Valoare (Value
Object)

def testCreateStudent():
"""
Testing student creation
Feature 1 - add a student
Task 1 - Create student
"""
st = Student("1", "Ion", Address("Adr", 1, "Cluj"))
assert st.getId() == "1"
assert st.getName() == "Ion"
assert st.getAdr().getStreet()=="Adr"

st = Student("2", "Ion2", Address("Adr2", 1, "Cluj"))


assert st.getId() == "2"
assert st.getName() == "Ion2"
assert st.getAdr().getStreet() == "Adr2"
assert st.getAdr().getCity() == "Cluj"
class Address: class Student:
""" """
Represent an address Represent a student
""" """
def __init__(self,street,nr,city): def __init__(self, id, name, adr):
self.__street = street """
self.__nr = nr Create a new student
self.__city = city id, name String
address - Address
def getStreet(self): """
return self.__street self.__id = id
self.__name = name
def getNr(self): self.__adr = adr
return self.__nr
def getId(self):
def getCity(self): """
return self.__city Getter method for id
"""
return self.__id
Agregate i Repository

Grupai entiti i obiecte valoare n agregate. Alegei o entitate radcin (root) care
controleaz accesul la toate elementele din agregat.

Obiectele din afara agregatului ar trebui s aib referina doar la entitatea principal.

Repository creaz illuzia unei colecii de obiecte de acelai tip. Creai Repository
doar pentru entitatea principal din agregat

Doar StudentRepository (nu i AddressRepository)


Fiiere text n Python
Funcia Built in: open() returneaza un obiect reprezentnd fiierul
Cel mai frecvent se foloseste apelul cu dou argumente: open(filename,mode).
Filename un string, reprezint calea ctre fiier(absolut sau relativ)
Mode:
"r" open for read
"w" open for write (overwrites the existing content)
"a" open for append

Metode:
write(str) scrie string n fiier
readline() - citire linie cu line, returneaz string
read() - citete tot fiierul, returneaz string
close() - nchide fiier, elibereaz resursele ocupate

Excepii:
IOError arunc aceast excepie daca apare o eroare de intrare/ieire (no file, no
disk space, etc)
Exemple Python cu fiiere text

#open file for write (overwrite if exists, create if not)


f = open("test.txt","w")
f.write("Test data\n")
f.close()

#open file for write (append if exist, create if not)


f = open("test.txt","a")
f.write("Test data line 2\n")
f.close()

#open for read


f = open("test.txt","r")
#read a line from the file
line = f.readline()
print line
f.close()

#open for read


f = open("test.txt","r")
#read a line from the file
line = f.readline().strip()
while line!="":
print line
line = f.readline().strip()
f.close()

#open for read


f = open("test.txt","r")
#read the entire content from the file
line = f.read()
print line
f.close()
#use a for loop
f = open("etc/test.txt")
for line in f:
print line
f.close()
Repository cu fiiere
class StudentFileRepository:
"""
Store/retrieve students from file
"""
def __loadFromFile(self):
"""
Load students from file
"""
try:
f = open(self.__fName, "r")
except IOError:
#file not exist
return []
line = f.readline().strip()
rez = []
while line!="":
attrs = line.split(";")
st = Student(attrs[0], attrs[1], Address(attrs[2], attrs[3], attrs[4]))
rez.append(st)
line = f.readline().strip()
f.close()
return rez

def store(self, st):


"""
Store the student to the file.Overwrite store
st - student
Post: student is stored to the file
raise DuplicatedIdException for duplicated id
"""
allS = self.__loadFromFile()
if st in allS:
raise DuplicatedIDException()
allS.append(st)
self.__storeToFile(allS)

def __storeToFile(self,sts):
"""
Store all the students in to the file
raise CorruptedFileException if we can not store to the file
"""
#open file (rewrite file)
f = open(self.__fName, "w")
for st in sts:
strf = st.getId()+";"+st.getName()+";"
strf = strf + st.getAdr().getStreet()+";"+str(st.getAdr().getNr())
+";"+st.getAdr().getCity()
strf = strf+"\n"
f.write(strf)
f.close()
Dynamic Typing
Verificarea tipului se efectueaza n timpul execuiei (runtime) nu n timpul
compilrii (compile-time).

n general n limbajele cu dynamic typing valorile au tip, dar variabilele nu. Variabila
poate referi o valoare de orice tip

Duck Typing

Duck typing este un stil de dynamic typing n care metodele i cmpurile obiectelor
determin semantica valid, nu relaia de motenire de la o clas anume sau
implementarea unei interfee.

Interfaa publica este dat de multimea metodelor i cmpurilor accesibile din


exterior. Dou clase pot avea acelai interfaa public chiar dac nu exista o relaie de
motenire de la o clas de baz comun

Duck test: When I see a bird that walks like a duck and swims like a duck and quacks
like a duck, I call that bird a duck

class Student: class Professor:


def __init__(self, id, name): def __init__(self, id, name, course):
self.__name = name self.__id = id
self.__id = id self.__name = name
def getId(self): self.__course = course
return self.__id
def getId(self):
def getName(self): return self.__id
return self.__name
def getName(self):
return self.__name

def getCourse(self):
return self.__course
l = [Student(1, "Ion"), Professor("1", "Popescu", "FP"), Student(31, "Ion2"),
Student(11, "Ion3"), Professor("2", "Popescu3", "asd")]

for el in l:
print el.getName()+" id "+str(el.getId())

def myPrint(st):
print el.getName(), " id ", el.getId()

for el in l:
myPrint(el)
Duck typing Repository

Fiindc interfaa public a clasei:


GradeRepository i GradeFileRepository
StudentRepository i StudentFileRepository
sunt identice controllerul funcioneaza cu oricare obiect, fr modificri.

#create a validator
val = StudentValidator()
#create repository
repo = StudentFileRepository("students.txt")
#create controller and inject dependencies
ctr = StudentController(val, repo)
#create Grade controller
gradeRepo = GradeFileRepository("grades.txt")
ctrgr = GradeController(gradeRepo, GradeValidator(), repo)
#create console ui and provide (inject) the controller
ui = ConsoleUI(ctr,ctrgr)
ui.startUI()
#create a validator
val = StudentValidator()
#create repository
repo = StudentRepository()
#create controller and inject dependencies
ctr = StudentController(val, repo)
#create Grade controller
gradeRepo = GradeRepository()
ctrgr = GradeController(gradeRepo, GradeValidator(), repo)
#create console ui and provide (inject) the controller
ui = ConsoleUI(ctr,ctrgr)
ui.startUI()
Asocieri ntre obiecte din domeniu
n lumea real, conceptual sunt multe relaii de tip many-to-many dar modelarea
acestor relaii n aplicaie nu este ntodeauna fezabil.

Cnd modeln obiecte din lumea real n aplicaiile noastre, asocierile complic
implementarea i ntreinerea aplicaiei.
Asocierile bidirecionale de exemplu presupun ca fiecare obiect din asociere se
poate folosi/nelege/refolosi doar npreun

Este important s simplificm aceste relaii ct de mult posibil, prin:


Impunerea unei direcii (transformare din bi-direcional n unidirecional)
Reducerea mutiplicitii
Eliminarea asocierilor ne-eseniale

Scopul este sa modelm lumea real ct mai fidel dar n acelai timp sa simplificm
modelul pentru a nu complica implementare.
Asocieri

Exemplu Catalog

gr = ctr.assign("1", "FP", 10) st = Student("1", "Ion",


assert gr.getDiscipline()=="FP" Address("Adr", 1, "Cluj"))
assert gr.getGrade()==10
assert gr.getStudent().getId()=="1" rep = GradeRepository()
assert gr.getStudent().getName()=="Ion" grades = rep.getAll(st)
assert grades[0].getStudent()==st
assert grades[0].getGrade()==10
Ascunderea detaliilor legate de persisten

Repository trebuie s ofere iluzia c obiectele sunt n memorie astfel codul client
poate ignora detaliile de implementare.
n cazul n care repository salveaz datele se n fiier, trebuie sa avem n vedere
anumite aspecte.

n exemplul de mai sus GradeRepository salveaz doar id-ul studentului (nu toate
campurile studentului) astfel nu se poate implementa o funcie getAll n care se
returneaza toate notele pentru toi studenii. Se poate n scimb oferi metoda
getAll(st) care returneaz toate notele pentru un student dat

def store(self, gr):


"""
Store a grade
post: grade is in the repository
raise GradeAlreadyAssigned exception if we already have a grade
for the student at the given discipline
raise RepositoryException if there is an IO error when writing to
the file
"""
if self.find(gr.getStudent(), gr.getDiscipline())!=None:
raise GradeAlreadyAssigned()

#open the file for append


try:
f = open(self.__fname, "a")
grStr = gr.getStudent().getId()+","+gr.getDiscipline()
grStr =grStr+","+str(gr.getGrade())+"\n"
f.write(grStr)
f.close()
except IOError:
raise RepositorException("Unable to write a grade to the file")
Obiecte de transfer (DTO - Data transfer objects)

Funcionalitate: Primi 5 studeni la o disciplin. Prezentai in format tabelar : nume


student, nota la disciplina dat

Avem nevoie de obiecte speciale (obiecte de transfer) pentru acest caz de utilizare.
Funciile din repository nu ajung pentru a implementa (nu avem getAll()).
Se creaza o noua clas care conine exact informaiile de care e nevoie.

n repository:
def getAllForDisc(self,disc):
"""
Return all the grades for all the students from all disciplines
disc - string, the discipline
return list of StudentGrade's
"""
try:
f = open(self.__fname, "r")
except IOError:
#the file is not created yet
return None
try:
rez = [] #StudentGrade instances
line = f.readline().strip()
while line!="":
attrs = line.split(",")
#if this line refers to the requested student
if attrs[1]==disc:
gr = StudentGrade(attrs[0], attrs[1], float(attrs[2]))
rez.append(gr)
line = f.readline().strip()
f.close()
return rez
except IOError:
raise RepositorException("Unable to read grades from the file")
DTO Data transfer obiect

n controller:
def getTop5(self,disc):
"""
Get the best 5 students at a given discipline
disc - string, discipline
return list of StudentGrade ordered descending on
the grade
"""
sds = self.__grRep.getAllForDisc(disc)
#order based on the grade
sortedsds = sorted(sds, key=lambda studentGrade:
studentGrade.getGrade(),reverse=True)
#retain only the first 5
sortedsds = sortedsds[:5]
#obtain the student names
for sd in sortedsds:
st = self.__stRep.find(sd.getStudentID())
sd.setStudentName(st.getName())
return sortedsds
Motenire

Motenirea permite definirea de clase noi (clase derivate) reutiliznd clase existente (clasa de
baz). Clasa nou creat moteneste comportamentul (metode) i caracteristicile (variabile membre,
starea) de la clasa de baz

Dac A i B sunt dou clase unde B moteneste de la clasa A (B este derivat din clasa A sau clasa B
este o specializare a clasei A) atunci:
clasa B are toate metodele si variabilele membre din clasa A
clasa B poate redefini metode din clasa A
clasa B poate adauga noi membrii (variabile, metode) pe lng cele motenite de la clasa A.

Reutilizare de cod
Una din motivele pentru care folosim motenire este reutilizarea codului existent ntr-o clas (motenire
de implementare).

Comporamentul unei clase de baze se poate moteni de clasele derivate.


Clasa dericvat poate:
poate lsa metoda nemodificat
apela metoda din clasa de baz
poate modifica (suprascrie) o metod.
Motenire n Python
Syntax:
class DerivedClassName(BaseClassName):

Clasa derivat motenete:


cmpuri
metode

Dac acessm un membru (cmp, metod) : se caut n clasa curent, dac nu se gsete
atunci cautarea continu n clasa de baz
class B(A): class A:
""" def __init__(self):
This class extends A print "Initialise A"
A is the base class,
B is the derived class def f(self):
B is inheriting everything from class A print "in method f from A"
"""
def __init__(self): def g(self):
#initialise the base class print "in method g from A"
A.__init__(self)
print "Initialise B"

def g(self):
"""
Overwrite method g from A
"""
#we may invoke the function from the
base class
A.f(self)
print "in method g from B"

b = B()
#f is inherited from A
b.f()
b.g()
Clasele Derivate pot suprascrie metodele clasei de baza.

Suprascrierea poate nlocui cu totul metoda din clasa de baz sau poate extinde funcionalitatea
(se execut i metoda din clasa de baz dar se mai adaug cod)

O metod simpl s apelm o metod n clasa de baz:


BaseClassName.methodname (self,arguments)
Diagrame UML Generalizare (motenire)

Relaia de generalizare ("is a") indic faptul c o clas (clasa derivat) este o
specializare a altei clase (clasa de baz). Clasa de baz este generalizarea clasei
derivate.

Orice instan a clasei derivate este si o instana a clasei de baz.


Repository cu Fiiere
class StudentFileRepository(StudentRepository):
"""
Repository for students (stored in a file)
"""
pass

class StudentFileRepository(StudentRepository):
"""
Store/retrieve students from file
"""
def __init__(self,fileN):
#properly initialise the base class
StudentRepository.__init__(self)
self.__fName = fileN
#load student from the file
self.__loadFromFile()

def __loadFromFile(self):
"""
Load students from file
raise ValueError if there is an error when reading from the file
"""
try:
f = open(self.__fName,"r")
except IOError:
#file not exist
return
line = f.readline().strip()
while line!="":
attrs = line.split(";")
st = Student(attrs[0],attrs[1],Address(attrs[2], attrs[3], attrs[4]))
StudentRepository.store(self, st)
line = f.readline().strip()
f.close()
Suprascriere metode

def testStore():
fileName = "teststudent.txt"
repo = StudentFileRepository(fileName)
repo.removeAll()

st = Student("1","Ion",Address("str",3,"Cluj"))
repo.store(st)
assert repo.size()==1
assert repo.find("1") == st
#verify if the student is stored in the file
repo2 = StudentFileRepository(fileName)
assert repo2.size()==1
assert repo2.find("1") == st
def store(self,st):
"""
Store the student to the file.Overwrite store
st - student
Post: student is stored to the file
raise DuplicatedIdException for duplicated id
"""
StudentRepository.store(self, st)
self.__storeToFile()

def __storeToFile(self):
"""
Store all the students in to the file
raise CorruptedFileException if we can not store to the file
"""
f = open(self.__fName,"w")
sts = StudentRepository.getAll(self)
for st in sts:
strf = st.getId()+";"+st.getName()+";"
strf = strf + st.getAdr().getStreet()
+";"+str(st.getAdr().getNr()) +";"+st.getAdr().getCity()
strf = strf+"\n"
f.write(strf)
f.close()
Excepii

def __createdStudent(self):
"""
Read a student and store in the apllication
"""
id = input("Student id:").strip()
name = input("Student name:").strip()
street = input("Address - street:").strip()
nr = input("Address - number:").strip()
city = input("Address - city:").strip()
try:
self.__ctr.create(id, name,street,nr,city)
except ValueError as msg:
print (msg)

def __createdStudent(self):
"""
Read a student and store in the apllication
"""
id = input("Student id:").strip()
name = input("Student name:").strip()
street = input("Address - street:").strip()
nr = input("Address - number:").strip()
city = input("Address - city:").strip()
try:
self.__ctr.create(id, name,street,nr,city)
except ValidationException as ex:
print (ex)
except DuplicatedIDException as ex:
print (ex)
class ValidationException(Exception):
def __init__(self,msgs):
"""
Initialise
msg is a list of strings (errors)
"""
self.__msgs = msgs
def getMsgs(self):
return self.__msgs

def __str__(self):
return str(self.__msgs)
Ierarhie de excepii

class StudentCRUDException(Exception):
pass
class ValidationException(StudentCRUDException):
def __init__(self,msgs):
"""
Initialise
msg is a list of strings (errors)
"""
self.__msgs = msgs
def getMsgs(self):
return self.__msgs
def __str__(self):
return str(self.__msgs)

class RepositorException(StudentCRUDException):
"""
Base class for the exceptions in the repository
"""
def __init__(self, msg):
self.__msg = msg
def getMsg(self):
return self.__msg
def __str__(self):
return self.__msg
class DuplicatedIDException(RepositorException):
def __init__(self):
RepositorException.__init__(self, "Duplicated ID")
def __createdStudent(self):
"""
Read a student and store in the apllication
"""
id = input("Student id:").strip()
name = input("Student name:").strip()
street = input("Address - street:").strip()
nr = input("Address - number:").strip()
city = input("Address - city:").strip()
try:
self.__ctr.create(id, name,street,nr,city)
except StudentCRUDException as ex:
print (ex)
Layered arhitecture Structur proiect
Layered architecture GUI Example
Tkinter este un toolkit GUI pentru Python (este disponibil pe majoritatea platformelor Unix , pe
Windows i Mac)

Review - aplicaia StudentCRUD cu GUI

Tkinter (sau orice alt GUI ) nu se cere la examen


Testarea programelor
Testarea este observarea comportamentului unui program n multiple execuii.
Se execut programul pentru ceva date de intrare i se verific daca rezultate sunt corecte n
raport cu intrrile.

Testarea nu demonstreaz corectitudinea unui program (doar ofer o anumit sigurana ,


confiden). In general prin testare putem demonstra c un program nu este corect, gsind un
exemplu de intrri pentru care rezultatele sunt greite.

Testarea nu poate identifica toate erorile din program.


Metode de testare
Testare exhaustiv
Verificarea programului pentru toate posibilele intrri.
Imposibil de aplicat n practiv, avem nevoie de un numr finit de cazuri de testare.

Black box testing (metoda cutiei negre)


Datele de test se selecteaza analiznd specificaiile (nu ne uitm la implementare).
Se verific dac programul respect specificaiile.
Se aleg cazur de testare pentru: valori obinuite, valori limite, condiii de eroare.

White box testing (metoa cutiei transparente)


Datele de test se aleg analiznd coul surs. Alegem datele astfel nct se acoper toate
ramurile de execuie (n urma executri testelor, fiecare instruciune din program este executat
mcar odat)
White box vs Black Box testing
def isPrime(nr):
"""
Verify if a number is prime
return True if nr is prime False if not
raise ValueError if nr<=0
"""
if nr<=0:
raise ValueError("nr need to be positive")
if nr==1:#1 is not a prime number
return False
if nr<=3:
return True
for i in range(2,nr):
if nr%i==0:
return False
return True

Black Box White Box (cover all the paths)


test case pentru prim/compus test case pt 0
test case pentr 0 test case pt negative
test case pentru numere test case pt 1
negative test case pt 3
test case pt prime (fr divizor)
test case pt neprime
def blackBoxPrimeTest(): def whiteBoxPrimeTest():
assert (isPrime(5)==True) assert (isPrime(1)==False)
assert (isPrime(9)==False) assert (isPrime(3)==True)
try: assert (isPrime(11)==True)
isPrime(-2) assert (isPrime(9)==True)
assert False try:
except ValueError: isPrime(-2)
assert True assert False
try: except ValueError:
isPrime(0) assert True
assert False try:
except ValueError: isPrime(0)
assert True assert False
except ValueError:
assert True
Nivele de testare
Testele se pot categoriza n funcie de momentul n care se creaz (n cadrul procesului de
dezvoltare) sau n funcie de specificitatea testelor.

Unit testing
Se refer la testarea unei funcionalitai izolate, n general se refer la testarea la nivel de
metode. Se testeaz funciile sau pri ale programului, independent de restul applicaiei

Integration testing
Consider ntreaga aplicaie ca un ntreg. Dup ce toate funciile au fost testate este nevoie de
testarea comportamentului general al programului.
Testare automat (Automated testing)
Testare automat presupune scrierea de programe care realizeaz testarea (n loc s se
efectueze manual).
Practic se scrie cod care compara rezultatele efective pentru un set de intrri cu rezultatele
ateptate.

TDD:
Paii TDD:
teste automate
scrierea specificaiilor (inv, pre/post, excepii)
implementarea codului
PyUnit - bibliotec Python pentru unit testing
modulul unittest ofer:
teste automate
modalitate uniform de pregatire/curaare (setup/shutdown) necesare pentru teste
fixture
agregarea testelor
test suite
independena testelor fa de modalitatea de raportare

import unittest
class TestCaseStudentController(unittest.TestCase):
def setUp(self):
#code executed before every testMethod
val=StudentValidator()
self.ctr=StudentController(val, StudentRepository())
st = self.ctr.create("1", "Ion", "Adr", 1, "Cluj")

def tearDown(self):
#cleanup code executed after every testMethod

def testCreate(self):
self.assertTrue(self.ctr.getNrStudents()==1)
#test for an invalid student
self.assertRaises(ValidationEx,self.ctr.create,"1", "", "", 1, "Cj")

#test for duplicated id


self.assertRaises(DuplicatedIDException,self.ctr.create,"1", "I",
"A", 1, "j")

def testRemove(self):
#test for an invalid id
self.assertRaises(ValueError,self.ctr.remove,"2")

self.assertTrue(self.ctr.getNrStudents()==1)

st = self.ctr.remove("1")
self.assertTrue(self.ctr.getNrStudents()==0)
self.assertEquals(st.getId(),"1")
self.assertTrue(st.getName()=="Ion")
self.assertTrue(st.getAdr().getStreet()=="Adr")

if __name__ == '__main__':
unittest.main()
Depanare (Debugging)
DepanareaDebugging este activitatea prin care reparm erorile gsite n urma testrii.
Dac testarea indic prezena unei erori atunci prin depanare n cercm s identificm
cauza erorii, modaliti de rezolvare. Scopul este sa elminm eroarea.
Se pate realiz folosind:
instructiuni print
instrumente specializate oferite de IDE

Depanarea este o activitate neplcut, pe ct posibil, trebuie evitat.

Perspectiva Eclipse pentru depanare

Debug view
prezint starea curent de execuie (stack trace)
execuie pas cu pas, resume/pause
Variables view
inspectarea variabilelor
Inspectarea programelor
Any fool can write code that a computer can understand. Good programmers write code that
humans can understand

Prin stilul de programare nelegem toate activitile legate de scrierea de programe i


modalitile prin care obinem cod: uor de citit, uor de neles, uor de intreinut.

Stil de programare
Principalul atribut al codului surs este considerat uurina de a citi (readability).
Un program, ca i orice publicaie, este un text care trebuie citit i neles cu uurin de orice
programator.
Elementele stilului de programare sunt:
comentarii
formatarea textului (indentare, white spaces)
specificaii
denumiri sugestive (pentru clase, funcii, variabile) din program
denumiri sugestive
folosirea conveniilor de nume

Convenii de nume (naming conventions):


clase: Student, StudentRepository
variabile: student, nrElem (nr_elem)
funcii: getName, getAddress, storeStudent (get_name,get_address,
store_student)
constante: MAX

Este inportant sa folosii acelai reguli de denumire in toat aplicaia


Recursivitate
O noiune e recursiv dac e folosit n propria sa definiie.

O funcie recursiv: funcie care se auto-apeleaz.


Rezultatul este opinut 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: nprim problema ntr-o variant mai simpl al aceleai probleme
plus ceva pai simplii
ex. apel cu n-1, sau doua apeluri recusive 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 creaz 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)
print 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 creaz 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
hardwareul 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.
Estimeaza timpul de execuie pentru toate intrrile pisibile.
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 inrare sunt mari
Complexitate
caz favorabil - datele de intrare care conduc la timp de execuie minim
best-case complexity (BC): BC ( A) min
ID
E(I )

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

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 multimea tutoror datelor de intrare posibile pentru un n fixat

Obs. Dimensiunea datelor (n) este fixat (un numar 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 2 n 2 ).
putem ignora constante mici dac n aceste constante nu afecteaz ordinea de mrime.

Ex : T (n) 13 n3 42 n 2 2 n log 2 n 3 n
Fiindc 0 log 2 n n, n 1 i n n, n 1 , putem conclude c temenul n3 domin aceast
expresie cand n este mare
Ca urmare, timpul de execuie a algoritmului crete cu ordinul lui n3 , 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)
Definiia alternativ: Spunem c T (n) O( f (n)) dac nlim
f ( n)
este 0 sau o constant, dar nu .

Observaii.
T ( n)
1. Dac T (n) 13 n3 42 n 2 2 n log2 n 3 n , atunci nlim 13 . Deci, putem spune c
n3
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(n 3 ) , 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)
Definiia alternativ: Spunem c T (n) ( f (n)) dac nlim
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)
Definiia alternativ Spunem c T (n) ( f (n)) dac nlim
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)
3. Dac T (n) 13 n3 42 n 2 2 n log 2 n 3 n , atunci nlim 3
13 . Deci, T (n) ( n 3 ) . Acest
n
lucru poate fi dedus i din faptul c T (n) O(n 3 ) i T (n) (n 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 efectueaza 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): n
s = 0 T (n)=(i=1) 1=n T ( n)(n)
for i in range(1,n+1):
s=s+i Complexitate (Overall complexity) (n)
return s
Cazurile Favorabil/Mediu/Defavorabil sunt identice
def f2(n): n
i = 0 T (n)=(i=1) 1=n T ( n)(n)
while i<=n:
#atomic operation
i = i + 1 Overall complexity (n)
Cazurile Favorabil/Mediu/Defavorabil sunt identice
def f3(l):
"""
Caz favorabil:
l list of numbers primul element e numr par: T (n)=1 (1)
return True if the list contains
an even nr
Caz defavorabil:
""" Nu avem numere pare n list: T (n)=n (n)
poz = 0
while poz<len(l) and l[poz]%2 !=0: Caz mediu:
poz = poz+1 While poate fi executat 1,2,..n ori (acelai probabilitate).
return poz<len(l)
Numrul de pai = numrul mediu de iteraii

T (n)=(1+2+...+n)/ n=(n+1)/2 T (n) (n)

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

(2n2)
T (n)=2n (i=1) 1( 2n2)(2n 1)/ 2(2n2)

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

(2n2) (2n2)
T (n)=(i=1) C i =(i=1) (2ni )/ 2=...(n 2)

Overall complexity O( n2 )
Formule cu sume:

1 n
i 1
suma constant.

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

2
suma ptratic

n
1
i ln(n) O(1)
i 1
suma armonic
n
c n 1 1
ci
i 1 c 1
, c 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(log2 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(2 n ), O(n 3 ), 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 nlime h este N (h) 3 N (0) (1 3 3 ... 3 ) 3


h 1 2 h 1 i

i 0
Exemple
{
def recursiveSum(l): 1 for n=0
""" Recurrence: T (n)= T ( n1)+1 otherwise
Compute the sum of numbers
l - list of number
return int, the sum of numbers
""" T (n)= T (n1)+1
#base case T (n1)= T (n2)+1
if l==[]: T (n2)= T (n3)+1 => T (n)=n+1 (n)
return 0
#inductive step ...= ...
return l[0]+recursiveSum(l[1:]) T (1)= T (0)+1

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

T (n)=2n 1 (2 n )
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
O , , .

Toate obsevaiile 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 T (n)=n (n)
return int, the sum of numbers
"""
rez = 0
for nr in l:
rez = rez+nr
return rez

{T (n1)+1
def recursiveSum(l): 0 for n=1
""" Recuren: T (n)=
Compute the sum of numbers otherwise
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
Algoritmi de cutare

datele sunt n memorie, o secven de nregistrri (k1, k2, ..., kn )


se caut o nregistrare avnd un cmp egal cu o valoare dat
cheia de cutare.
Dac am gsit nregistrarea, se returneaz poziia nregistrrii n
secven
dac cheile sunt ordonate atunci ne intereseaz poziia n care
trebuie inserat o nregistare nou astfel nct ordinea se menine
Specificaii pentru cutare:

Date: a,n,(ki, i=0,n-1);


Precondiii: nN, n0;
Rezultate: p;
Post-condiii: (0pn-1 and a = kp) or (p=-1 dac
cheia nu exist).
Cutare secvenial cheile nu sunt ordonate
def searchSeq(el,l): def searchSucc(el,l):
""" """
Search for an element in a list Search for an element in a list
el - element el - element
l - list of elements l - list of elements
return the position of the element return the position of first occurrence
or -1 if the element is not in l or -1 if the element is not in l
""" """
poz = -1 i = 0
for i in range(0,len(l)): while i<len(l) and el!=l[i]:
if el==l[i]: i=i+1
poz = i if i<len(l):
return poz return i
return -1
(n1)
T (n)=(i=0) 1=n(n) Best case: the element is at the first position
T (n)(1)
Worst-case: the element is in the n-1 position
T (n)(n)
Average case: while can be executed 0,1,2,n-1 times
T (n)=(1+2+...+n1)/ n(n)
Overall complexity O( n)
Specificaii pentru cutare chei ordonate:

Date a,n,(ki, i=0,n-1);


Precondiii: nN, n0, and k0 < k1 < .... < kn-1 ;
Rezultate p;
Post-condiii: (p=0 and a k0) or (p=n and a > kn-1) or
((0<pn-1) and (kp-1 < a kp)).
Cutare secvenial chei ordonate
def searchSeq(el,l): def searchSucc(el,l):
""" """
Search for an element in a list Search for an element in a list
el - element el - element
l - list of ordered elements l - list of ordered elements
return the position of first occurrence return the position of first occurrence
or the position where the element or the position where the element
can be inserted can be inserted
""" """
if len(l)==0: if len(l)==0:
return 0 return 0
poz = -1 if el<=l[0]:
for i in range(0,len(l)): return 0
if el<=l[i]: if el>=l[len(l)-1]:
poz = i return len(l)
if poz==-1: i = 0
return len(l) while i<len(l) and el>l[i]:
return poz i=i+1
return i

(n1)
T (n)=(i=0) 1=n(n) Best case: the element is at the first position
T (n)(1)
Worst-case: the element is in the n-1 position
T (n)(n)
Average case: while can be executed 0,1,2,n-1 times
T (n)=(1+2+...+n1)/ n(n)
Overall complexity O( n)
Algoritmi de cutare

cutare secvenial
se examineaz succesiv toate cheile
cheile nu sunt ordonate
cutare binar
folosete divide and conquer
cheile sunt ordonate
Cutare binar (recursiv)
def binaryS(el, l, left, right):
"""
Search an element in a list
el - element to be searched
l - a list of ordered elements
left,right the sublist in which we search
return the position of first occurrence or the insert position
"""
if left>=right-1:
return right
m = (left+right)/2
if el<=l[m]:
return binaryS(el, l, left, m)
else:
return binaryS(el, l, m, right)

def searchBinaryRec(el, l):


"""
Search an element in a list
el - element to be searched
l - a list of ordered elements
return the position of first occurrence or the insert position
"""
if len(l)==0:
return 0
if el<l[0]:
return 0
if el>l[len(l)-1]:
return len(l)
return binaryS(el, l, 0, len(l))
Recuren cutare binar

(1), if n 1

T ( n) n
T (1), otherwise
2
Cutare binar (iterativ)
def searchBinaryNonRec(el, l):
"""
Search an element in a list
el - element to be searched
l - a list of ordered elements
return the position of first occurrence or the position where the element can be
inserted
"""
if len(l)==0:
return 0
if el<=l[0]:
return 0
if el>=l[len(l)-1]:
return len(l)
right=len(l)
left = 0
while right-left>1:
m = (left+right)/2
if el<=l[m]:
right=m
else:
left=m
return right
Complexitate

Timp de execuie
Algoritm best worst case average overall
case
SearchSeq (n) (n) (n) (n)

SearchSucc (1) (n) (n) O(n)

SearchBin (1) (log 2 n) (log 2 n) O(log 2 n)


Vizualizare cautri
Cutare in python - index()
l = range(1,10)
try:
poz = l.index(11)
except ValueError:
# element is not in the list

- __eq__
class MyClass:
def __init__(self,id,name):
self.id = id
self.name = name

def __eq__(self,ot):
return self.id == ot.id

def testIndex():
l = []
for i in range(0,200):
ob = MyClass(i,"ad")
l.append(ob)

findObj = MyClass(32,"ad")
print "positions:" +str(l.index(findObj))
Searching in python- in
l = range(1,10)
found = 4 in l

iterable (definii __iter__ and __next__)


class MyClass2: def __next__(self):
def __init__(self): """
self.l = [] Return the next element in the iteration
raise StopIteration exception if we are at the end
def add(self,obj): """
self.l.append(obj) if (self.iterPoz>=len(self.l)):
raise StopIteration()
def __iter__(self):
""" rez = self.l[self.iterPoz]
Return an iterator object self.iterPoz = self.iterPoz +1
""" return rez
self.iterPoz = 0
return self def testIn():
container = MyClass2()
for i in range(0,200):
container.add(MyClass(i,"ad"))
findObj = MyClass(20,"asdasd")
print findObj in container
#we can use any iterable in a for
container = MyClass2()
for el in container:
print (el)
Performan - cutare

def measureBinary(e, l):


sw = StopWatch()
poz = searchBinaryRec(e, l)
print (" BinaryRec in %f sec; poz=%i" %(sw.stop(),poz))

def measurePythonIndex(e, l):


sw = StopWatch()
poz = -2
try:
poz = l.index(e)
except ValueError:
pass #we ignore the error..
print (" PythIndex in %f sec; poz=%i" %(sw.stop(),poz))

def measureSearchSeq(e, l):


sw = StopWatch()
poz = searchSeq(e, l)
print (" searchSeq in %f sec; poz=%i" %(sw.stop(),poz))

search 200 search 10000000


BinaryRec in 0.000000 sec; poz=200 BinaryRec in 0.000000 sec; poz=10000000
PythIndex in 0.000000 sec; poz=200 PythIndex in 0.234000 sec; poz=10000000
PythonIn in 0.000000 sec PythonIn in 0.238000 sec
BinaryNon in 0.000000 sec; poz=200 BinaryNon in 0.000000 sec; poz=10000000
searchSuc in 0.000000 sec; poz=200 searchSuc in 2.050000 sec; poz=10000000
Sortare
Rearanjarea datelor dintr-o colecie astfel nct o cheie verific o relaie de ordine dat

internal sorting - datele sunt n memorie


external sorting - datele sunt n fiier

Elementele coleciei sunt nregistrri, o nregistrare are una sau mai multe cmpuri
Cheia K este asociat cu fiecare nregistrare, n general este un cmp.

Colecia este sortat:


cresctor dup cheia K : if K(i) K(j) for 0 i<j<n
descresctor: if K(i) K(j) for 0 i<j<n
Sortare intern n memorie

Date n,K; {K=(k1,k2,...,kn)}


Precondiii: kiR, i=1,n
Rezultate K';
Post-condiii: K' e permutare al lui K, avnd elementele
sortate,
k'1 k'2 ... k'n.
Sortare prin selecie (Selection Sort)

se determin elementul avnd cea mai mica cheie, interschimbare elementul cu


elementul de pe prima poziie
reluat procedura penru restul de elemente pn cnd toate elementele au fost
considerate.
Sortare prin selecie
def selectionSort(l):
"""
sort the element of the list
l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(0,len(l)-1):
ind = i
#find the smallest element in the rest of the list
for j in range(i+1,len(l)):
if (l[j]<l[ind]):
ind =j
if (i<ind):
#interchange
aux = l[i]
l[i] = l[ind]
l[ind] = aux
Complexitate timp de execuie

Numrul total de comparaii este:


n 1 n
n (n 1)
1
i 1 j i 1 2
(n 2 )

Este independent de datele de intrare:


caz favorabil/defavorabil/mediu sunt la fel, complexitatea este ( n 2 ) .
Complexitate spaiu de memorie
Sortare prin selecie este un algoritm In-place:
memoria adiional (alta dect memoria necesar pentru datele de intrare) este (1)

In-place . Algoritmul care nu folote memorie adiional (doar un mic factor


constant).
Out-of-place sau not-in-space. Algoritmul folosete memorie adiional pentru
sortare.

Selection sort is an in-place sorting algorithm.


Sortare prin selecie direct (Direct selection sort)
def directSelectionSort(l):
"""
sort the element of the list
l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(0,len(l)-1):
#select the smallest element
for j in range(i+1,len(l)):
if l[j]<l[i]:
swap(l,i,j)

n 1 n
n (n 1)
Overall time complexity: 1
i 1 j i 1 2
(n 2 )
Sortare prin inserie - Insertion Sort

se parcurg elementele

se insereaz elementul curent pe poziia corect n sub-


secvena deja sortat.

n sub-secvena ce conine elementele deja sortate se in


elementele sortate pe tot parcursul algoritmului, astfel dup
ce parcurgem toate elementele secvena este sortat n
ntregime
Sortare prin inserie
def insertSort(l):
"""
sort the element of the list
l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(1,len(l)):
ind = i-1
a = l[i]
#insert a in the right position
while ind>=0 and a<l[ind]:
l[ind+1] = l[ind]
ind = ind-1
l[ind+1] = a
Insertion Sort - complexitate timp de execuie
n
n (n 1)
T (n) (i 1) (n 2 )
Caz defavorabil: i 2 2

Avem numrul maxim de iteraii cnd lista este ordonat descresctor

n 2 3 n n 1
Caz mediu: (n 2 )
4 i 1 i

Pentru un i fixat i un k, 1 k i , probabilitatea ca xi s fie al k-lea cel mai mare


1
element n subsecvena x1 , x 2 ,..., x i este i
Astfel, pentru i fixat, putem deduce:

Numarul de Probabilitatea sa avem caz


iteraii while numrul de iteraii while din
prima coloan
1 1 un caz n care while se execut
i odat: xi xi-1
2 1 un caz n care while se execut de
i do ori: xi xi-2
... 1 ...
i
i-1 2 un caz n care while se execut de i-1
i ori: xi x1 and x1xi x2
Rezult c numrul de iteraii while medii pentru un i fixat este:

1 1 1 2 i 1 1
c i 1 2 ... (i 2) (i 1)
i i i i 2 i

Caz favorabil: T ( n) 1 n 1 ( n)
i 2

lista este sortat


Sortare prin inserie

complexitate general este O(n 2 ) .

Complexitate spaiu de meorie

complexitate memorie aditional este: (1) .

Sortare prin inserie este un algoritm in-place.


Sortare prin selecie direct
def directSelectionSort(l):
"""
sort the element of the list
l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(0,len(l)-1):
#select the smallest element
for j in range(i+1,len(l)):
if l[j]<l[i]:
swap(l,i,j)

n 1 n
n (n 1)
Overall time complexity: 1
i 1 j i 1 2
(n 2 )
Sortare prin inserie - Insertion Sort

se parcurg elementele

se insereaz elementul curent pe poziia corect n sub-


secvena deja sortat.

n sub-secvena ce conine elementele deja sortate se in


elementele sortate pe tot parcursul algoritmului, astfel dup
ce parcurgem toate elementele secvena este sortat n
ntregime
Sortare prin inserie
def insertSort(l):
"""
sort the element of the list
l - list of element
return the ordered list (l[0]<l[1]<...)
"""
for i in range(1,len(l)):
ind = i-1
a = l[i]
#insert a in the right position
while ind>=0 and a<l[ind]:
l[ind+1] = l[ind]
ind = ind-1
l[ind+1] = a
Metoda bulelor - Bubble sort

Compar elemente consecutive, dac nu sunt n ordinea dorit, se interschib.

Procesul de comparare continu pn cnd nu mai avem elemente consecutive ce


trebuie interschimbate (toate perechile respect relaia de ordine dat).
Sortare prin metoda bulelor
def bubbleSort(l):
sorted = False
while not sorted:
sorted = True #assume the list is already sorted
for i in range(1,len(l)):
if l[i-1]>l[i]:
swap(l, i, i-1)
sorted = False #the list is not sorted yet
Complexitate metoda bulelor

Caz favorabil: (n) . Lista este sortat

Caz defavorabil: (n 2 ) . Lista este sortat descresctor

Caz mediu (n 2 ) .

Coplexitate general este O(n 2 )

Complexitate ca spaiu adiional de memorie este (1) .

este un algoritm de sortare in-place .


QuickSort

Bazat pe divide and conquer


Divide: se partiioneaz lista n 2 astfel nct elementele din dreapta
pivotului sunt mai mici dect elementele din stnga pivotului.
Conquer: se sorteaz cele dou subliste
Combine: trivial dac partitionarea se face n acelai list

Partiionare: re-aranjarea elementelor astfel nct elementul numit pivot ocup locul
final n secven. Dac poziia pivotului este i :
kj ki kl , for Left j < i < l Right
Quick-Sort
def partition(l,left,right):
""" def quickSortRec(l,left,right):
Split the values:
smaller pivot greater #partition the list
return pivot position pos = partition(l, left, right)
post: left we have < pivot
right we have > pivot #order the left part
""" if left<pos-1:
pivot = l[left] quickSortRec(l, left, pos-1)
i = left #order the right part
j = right if pos+1<right:
while i!=j: quickSortRec(l, pos+1, right)
while l[j]>=pivot and i<j:
j = j-1
l[i] = l[j]
while l[i]<=pivot and i<j:
i = i+1
l[j] = l[i]
l[i] = pivot
return i
QuickSort complexitate timp de execuie

Timpul de execuie depinde de distribuia partiionrii (cte elemente


sunt mai mici dect pivotul cte sunt mai mari)

Partiionarea necesit timp linear.

Caz favorabil:, partiioanarea exact la mijloc (numere mai mici ca


pivotul = cu numere mai mari ca pivotul):
n
T ( n) 2 T ( n)
2

Complexitatea este (n log 2 n) .


QuickSort Caz favorabil
QuickSort Caz defavorabil

Partiionarea tot timpul rezult ntr-o partiie cu un singur element i o partiie cu


n-1 elemente
n
T (n) T (1) T (n 1) (n) T (n 1) (n) (k ) (n 2 ) .
k 1

caz defavorabil: dac elementele sunt n ordine invers


QuickSort Caz mediu
Se alterneaz cazurile:
caz favorabil (lucky) cu complecitatea (n log 2 n) (notm cu L )
caz defavorabil (unlucky) cu complexitatea (n ) (notm cu U ). 2

Avem recurena:
n
L ( n ) 2 U ( n) lucky case
2
U (n) L(n 1) (n) unlucky case

Rezult
n n n
L(n) 2 L 1 (n) 2 L 1 (n) ( n log 2 n) ,
2 2 2

Complexitatea caz mediu: T (n) L(n) (n log 2 n) .


Coplexitatea ca timp de execuie pentru sortri:

Complexity
Algorithm worst-case average
SelectionSort (n 2 ) (n 2 )
InsertionSort (n 2 ) (n 2 )
BubbleSort (n 2 ) (n 2 )
QuickSort (n 2 ) ( n log 2 n)
Python - Quick-Sort
def qsort(list):
"""
Quicksort using list comprehensions

"""
if list == []:
return []
else:
pivot = list[0]
lesser = qsort([x for x in list[1:] if x < pivot])
greater = qsort([x for x in list[1:] if x >= pivot])
return lesser + [pivot] + greater
List comprehensions generatoare de liste
[x for x in list[1:] if x < pivot]
rez = []
for x in l[1:]:
if x<pivot:
rez.append(x)

Variant concis de a crea liste


creaz liste unde elementele listei rezult din operaii asupra unor elemente dintr-o alt
secven
paranteze drepte coninnd o expresie urmat de o clauz for , apoi zero sau mai multe clauze
for sau if
Python Parametrii optionali parametrii cu nume

Putem avea parametrii cu valori default;


def f(a=7,b = [],c="adsdsa"):

Dac se apeleaz metoda fr parametru actual se vor folosi valorile default


def f(a=7,b = [],c="adsdsa"): Console:
print (a) 7
print (b) []
print (c) adsdsa

f()

Argumentele se pot specifica n orice ordine


f(b=[1,2],c="abc",a=20) Console:
20
[1, 2]
abc

Parametrii formali se adaug ntr-un dicionar (namespace)


Trebuie oferit o valoare actual pentru fiecare parametru formal - prin orice metod: standard, prin nume,
default value
Tipuri de parametrii cum specificm parametru actual

positional-or-keyword : parametru poate fi transmis prin poziie sau prin nume (keyword)
def func(foo, bar=None):

keyword-only : parametru poate fi transmis doar specificnd numele


def func(arg, *, kw_only1, kw_only2):
Tot ce apare dupa * se poate transmite doar prin nume

var-positional : se pot transmite un numr arbitrar de parametri poziionali


def func(*args):
Valorile transmise se pot accesa folosind args, args este un tuplu

var-keyword: un numr arbitrar de parametrii prin nume


def func(**args):

Valorile transmise se pot accesa folosind args, args este un dictionar


Sortare n python - list.sort() / funcie build in : sorted
sort(*, key=None,reverse=None)
Sorteaz folosind operatorul <. Lista curent este sortat (nu se creaz o alt list)
key o funcie cu un argument care calculeaza o valoare pentru fiecare element, ordonarea se face
dupa valoarea cheii. n loc de o1 < o2 se face key(o1) < key(o2)
reverse true daca vrem sa sortm descresctor
l.sort() l.sort(reverse=True)
print (l) print (l)

sorted(iterable[, key][, reverse])


Returneaz lista sortat
l = sorted([1,7,3,2,5,4]) def keyF(o1):
print (l) return o1.name

l = sorted([1,7,3,2,5,4],reverse=True) ls = sorted(l,keyF)
print (l)

Sort stability
Stabil (stable) dac avem mai multe elemente cu aceli cheie, se menine ordinea iniial
Python funcii lambda (anonymous functions, lambda form)
Folosind lambda putem crea mici funcii
lambda x:x+7

Funciile lambda pot fi folosite oriunde e nevoie de un obiect funcie


def f(x): print ( (lambda x:x+7)(5) )
return x+7

print ( f(5) )

Putem avea doar o expresie.


Sunt o metod convenient de a crea funcii mici.
Similar cu funciile definite n interiorul altor funcii, funciile lambda pot referi
variabile din namespace
l = []
l = [] l.append(MyClass(2,"a"))
l.append(MyClass(2,"a")) l.append(MyClass(7,"d"))
l.append(MyClass(7,"d")) l.append(MyClass(1,"c"))
l.append(MyClass(1,"c")) l.append(MyClass(6,"b"))
l.append(MyClass(6,"b")) #sort on id
#sort on name ls = sorted(l,key=lambda o:o.id)
ls = sorted(l,key=lambda o:o.name) for x in ls:
for x in ls: print (x)
print (x)
TreeSort

Algoritmul creaz un arbore binar cu proprietatea c la orice nod din arbore,


arborele stng conine doar elemente mai mici dect elementul din rdcin iar
arborele drept conine doar elemente mai mari dect rdcina.

Dac parcurgem arborele putem lua elementele n ordine cresctoare/descresctoare.

Arborele este construit incremental prin inserarea succesiv de elemente. Elementele


se insereaz astfel nct se menine proprietatea ca n stnga avem doar elemente mai
mici n dreapta doar elemente mai mari dect elementul din rdcin.

Elementul nou inserat tot timpul ajunge ntr-un nod terminal (frunz) n arbore.
MergeSort

Bazat pe divide and conquer.

Secvena este nprit n dou subsecvene i fiecare subsecvena este sortat. Dupa
sortare se interclaseaz cele dou subsecvene, astfel rezult secvena sortat n
ntregime.

Pentru subsecvene se aplic acelai abordare pn cnd ajungem la o subsecven


elementar care se poate sorta fr nprire (secven cu un singur element).
Interclasare (Merging)

Date m, (xi, i=1,m), n, (yi, i=1,n);

Precondiii: {x1 <= x2...<=xm} i {y1 <= y2 <=... <=yn}

Rezultate k, (zi, i=1,k);

Post-condiii: {k=m+n} i {z1<=z2<=...<=zk} i (z1,z2,..., zk) este o permutare a


valorilor (x1, ..., xm,y1,..., yn)

complexitate interclasare: (m n) .
Spaiu de memorare adiional pentru merge sort (1)
Technici de programare

strategii de rezolvare a problemelor mai dificile


algoritmi generali pentru rezolvarea unor tipuri de
probleme
de multe ori o problem se poate rezolva cu mai multe
technici se alege metoda mai eficient
problema trebuie s satisfac anumite criterii pentru a
putea aplica technica
descriem algoritmul general pentru fiecare technic
Divide and conquer Metoda divizrii - pai

Pas 1 Divide - se mparte problema n probleme mai mici


(de acelai structur)
mprirea problemei n dou sau mai multe probleme disjuncte care se poate rezolva
folosind acelai algoritm

Pas 2 Conquer se rezolv subproblemele recursiv


Step3 Combine combinarea rezultatelor
Divide and conquer algoritm general
def divideAndConquer(data):
if size(data)<a:
#solve the problem directly
#base case
return rez
#decompose data into d1,d2,..,dk
rez_1 = divideAndConquer(d1)
rez_2 = divideAndConquer(d2)
...
rez_k = divideAndConquer(dk)
#combine the results
return combine(rez_1,rez_2,...,rez_k)

Putem aplica divide and conquer dac:


O promblem P pe un set de date D poate fi rezolvat prin rezolvarea aceleiai probleme P pe un alt set
de date D= d1, d2, ..., dk, de dimensiune mai mic dect dimensiunea lui D

Complexitatea ca timp de execuie pentru o problem rezolvat folosind divide and conquer poate fi descris de
recurena:

solving trivial problem, if n is small enough


T ( n)
k T (n / k ) time for dividing time for combining , otherwise
Divide and conquer 1 / n-1
Putem divide datele n: date de dimensiune 1 i date de dimensiune n-1
Exemplu: Caut maximul
def findMax(l):
"""
find the greatest element in the list
l list of elements
return max
"""
if len(l)==1:
#base case
return l[0]
#divide into list of 1 elements and a list of n-1 elements
max = findMax(l[1:])
#combine the results
if max>l[0]:
return max
return l[0]
Complexitate timp
Recurena: T (n)=
{T (n1)+1
1 for n=1
otherwise

T ( n)= T (n1)+1
T (n1)= T (n2)+1
T (n2)= T (n3)+1 => T (n)=1+1+...+1=n (n)
...= ...
T (2)= T (1)+1
Divizare n date de dimensiune n/k
def findMax(l):
"""
find the greatest element in the list
l list of elements
return max
"""
if len(l)==1:
#base case
return l[0]
#divide into 2 of size n/2
mid = len(l) /2
max1 = findMax(l[:mid])
max2 = findMax(l[mid:])
#combine the results
if max1<max2:
return max2
return max1

Complexitate ca timp:
Recurena: T (n)=
{2 T (n /12)+1
for n=1
otherwise
k (k 1)
T (2 )= 2 T (2 )+1
(k1) 2 (k 2)
2 T (2 )= 2 T ( 2 )+2
Notm: n=2 k => k =log2 n 2 2 T ( 2(k 2))= 2 3 T (2(k 3))+22 =>
...= ...
2(k 1) T (2)= 2 k T (1)+2(k1)
1 2. k (k +1) k
T (n)=1+2 +2 ..+2 =(2 1)/(21)=2 21=2 n1(n)
Divide and conquer - Exemplu
Calculai x k unde k 1 numr ntreg
Aborare simpl: x k =kk...k - k-1 nmuliri (se poate folosi un for) T (n) ( n)

Rezolvare cu metoda divizrii:

{
(k / 2) (k /2)
x k = x(k / 2) x(k / 2) for k even
x x x for k odd

def power(x, k):


"""
Divide: calculeaz k/2
compute x^k Conquer: un apel recursiv pentru a calcul x(k / 2)
x real number
k integer number Combine: una sau doua nmuliri
return x^k
""" Complexitate: T (n)(log 2 n)
if k==1:
#base case
return x
#divide
half = k/2
aux = power(x, half)
#conquer
if k%2==0:
return aux*aux
else:
return aux*aux*x
Divide and conquer
Cutare binar ( T (n)(log2 n) )
Divide imprim lista n dou liste egale
Conquer cutm n stnga sau n dreapta
Combine nu e nevoie
Quick-Sort ( T (n)( n log2 n) mediu)
Merge-Sort
Divide imprim lista n dou liste egale
Conquer sortare recursiv pentru cele dou liste
Combine interclasare liste sortate
Backtracking
se aplic la probleme de cutare unde se caut mai multe soluii
genereaz toate soluiile (dac sunt mai multe) pentru problem
caut sistematic prin toate variantele de soluii posibile
este o metod sistematic de a itera toate posibilele configuraii n spaiu de cutare
este o technic general trebuie adaptat pentru fiecare problem n parte.
Dezavantaj are timp de execuie exponenial

Algoritm general de descoperire a tuturor soluiilor unei probleme de calcul


Se bazeaz pe construirea incremental de soluii-candidat, abandonnd
fiecare candidat parial imediat ce devine clar c acesta nu are anse s devin
o soluie valid
Metoda generrii i testrii (Generate and test)
Problem Fie n un numr natural. Tiprii toate permutrile numerelor 1, 2, ..., n.
Pentru n=3
def perm3(): [0, 1, 2]
for i in range(0,3): [0, 2, 1]
for j in range(0,3): [1, 0, 2]
for k in range(0,3): [1, 2, 0]
#a possible solution [2, 0, 1]
possibleSol = [i,j,k] [2, 1, 0]
if i!=j and j!=k and i!=k:
#is a solution
print possibleSol

Metoda generrii i testrii - Generate and Test


Generare: se genereaz toate variantele posibile de liste de lungime 3 care conin doar
numerele 0,1,2
Testare: se testeaz fiecare variant pentru a verifica dac este soluie.
Generare i testare toate combinaiile posibile

x1 1 2 3

x2 1 2 3 1 2 3 1 2 3

x3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3

Probleme:

Numrul total de liste generate este 33, n cazul general nn

iniial se genereaz toate componentele listei, apoi se verifica dac lista este o permutare in
unele cazul nu era nevoie sa continum generarea (ex. Lista ce incepe cu 1,1 sigur nu conduce
la o permutare

Nu este general. Funcioneaz doar pentru n=3

n general: dac n este afncimea arborelui (numrul de variabile/componente n soluie) i presupunnd c fiecare
component poate avea k posibile valori, numrul de noduri n arbore este k n . nseamn c pentru cutarea n ntreg
arborele avem o complexitate exponenial, O(k n ) .
nbuntiri posibile

s evitm crearea comlet a soluiei posibile n cazul n care tim cu siguran c nu se


ajunge la o soluie.
Dac prima component este 1, atunci nu are sens s asignam 1 s pentru a doua component

x1 1 2 3

x2 1 2 3 1 2 3 1 2 3

x3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3

lucrm cu liste pariale (soluie parial)


extindem lista cu componente noi doar dac sunt ndeplinite anumite condiii (condiii de
continuare)
dac lista parial nu conine duplicate
Generate and test - recursiv
folosim recursivitate pentru a genera toate soluiile posibile (soluii candidat)
def generate(x,DIM): [0, 0, 0]
if len(x)==DIM: [0, 0, 1]
print x [0, 0, 2]
if len(x)>DIM: [0, 1, 0]
return [0, 1, 1]
x.append(0) [0, 1, 2]
for i in range(0,DIM): [0, 2, 0]
x[-1] = i [0, 2, 1]
generate(x[:],DIM) [0, 2, 2]
[1, 0, 0]
generate([],3) ...
Testare se tiprete doar soluia

def generateAndTest(x,DIM): [0, 1, 2]


if len(x)==DIM and isSet(x): [0, 2, 1]
print x [1, 0, 2]
if len(x)>DIM: [1, 2, 0]
return [2, 0, 1]
x.append(0) [2, 1, 0]
for i in range(0,DIM):
x[-1] = i
generateAndTest(x[:],DIM)
generateAndTest([],3)

n continuare se genereaza toate listele ex: liste care ncep cu 0,0


ar trebui sa nu mai generm dac conine duplicate Ex (0,0) aceste liste cu siguran nu
conduc la rezultat la o permutare
Reducem spatiu de cutare nu generm chiar toate listele posibile

Un candidat e valid (merit s continum cu el) doar dac nu conine duplicate


def backtracking(x,DIM): [0, 1, 2]
if len(x)==DIM: [0, 2, 1]
print x [1, 0, 2]
if len(x)>DIM: [1, 2, 0]
return #stop recursion [2, 0, 1]
x.append(0) [2, 1, 0]
for i in range(0,DIM):
x[-1] = i
if isSet(x):
#continue only if x can conduct to a solution
backtracking(x[:],DIM)

backtracking([], 3)

Este mai bine dect varianta genereaz i testeaz, dar complexitatea ca timp de execuie este tot
exponenial.
Permutation problem
x=( x 0, x 1,. .. , x n ) , xi (0,1,. . , n1)
rezultat:
e o solutie: xi x j for any i j

8 Queens problem:
Plasai pe o tabl de sah 8 regine care nu se atac.
Rezultat: 8 poziii de regine pe tabl
Un rezultat partial e valid: dac nu exist regine care se atac
nu e pe aceli coloana, linieor sau diagonal
Numrul total de posibile poziii (att valide ct i invalide):
combinri de 64 luate cte 8, C(64, 8) 4.5 109)
Genereaz i testeaz nu rezolv problma n timp rezonabil

Ar trebui sa generm doar poziii care pot conduce la un rezultat (sa reducem spaiu de cutare)
Dac avem deja 2 regine care se atac nu ar trebui s mai continum cu aceast
configuraie
avem nevoie de toate soluiile
Backtracking
spaiu de cutare: S = S1 x S2 x ... x Sn;
x este un vector ce reprezint soluia;
x[1..k] n S1 x S2 x ... x Sk este o soluie candidat; este o configuraie parial care ar putea conduce la rezultat; k
este numrul de componente deja construit;
consistent o funcie care verific dac o soluie parial este soluie candidat (poate conduce la rezultat)
soluie este o funcie care verific dac o soluie candidat x[1..k] este o soluie pentru problem.
Algoritmul Backtracking recursiv
def backRec(x):
x.append(0) #add a new component to the candidate solution
for i in range(0,DIM):
x[-1] = i #set current component
if consistent(x):
if solution(x):
solutionFound(x)
backRec(x[:]) #recursive invocation to deal with next components

Algoritm mai general (componentele soluiei pot avea domenii diferite (iau valori din domenii
diferite)
def backRec(x):
el = first(x)
x.append(el)
while el!=None:
x[-1] = el
if consistent(x):
if solution(x):
outputSolution(x)
backRec(x[:])
el = next(x)
Backtracking
Cum rezolvm problema folosind algoritmul generic:
trebuie sa reprezentm soluia sub forma unui vector X =( x 0, x1, ... x n ) S 0 x S 1 x ... x S n
definim ce este o soluie candidat valid (condiie prin care reducem spaiu de cutare)
definim condiia care ne zice daca o soluie candidat este soluie

def consistent(x):
"""
The candidate can lead to an actual
permutation only if there are no duplicate elements
"""
return isSet(x)

def solution(x):
"""
The candidate x is a solution if
we have all the elements in the permutation
"""
return len(x)==DIM
Metoda Greedy

o strategie de rezolvare pentru probleme de optimizare


aplicabil unde optimul global se poate afla selectnd succesiv optime locale
permite rezolvarea problemei fara a reveni asupra alegerilor facute pe parcurs
folosit n multe probleme practice care necesite selecia unei mulimi de elemente care
satisfac anumite condiii i realizeaz un optim
Probleme

Problema rucsacului

Se d un set de obiecte, caracterizate prin greutate i utilitate, i un rucsac care are capacitatea totala
W. Se caut o submultime de obiecte astfel nct greutatea total este mai mic dect W i suma utilitii
obiectelor este maximal.

Monede

Se da o sum M i tipuri (ex: 1, 5, 25) de monede (avem un numar nelimitat de monede


din fiecare tip de moned). S se gseasc o modalitate de a plti suma M de bani folosind ct
mai puine monezi.
Forma general a unei probleme Greedy-like

Avnd un set de obiecte C candidai pentru a face parte din soluie, se cere s se gseasc un subset B
(B C) care indeplinete anumite condiii (condiii interne ) i maximizeaz (minimizeaz) o funcie de
obiectiv.

Dac un subset X ndeplinete condiiile interne atunci subsetul X este acceptabil (posibil)
Unele probleme pot avea mai multe soluii acceptabile, caz n care se caut o soluie ct mai bun,
daca se poate solutia ceea mai bun (cel care realizeaza maximul pentru o funcie obiectiv).

Pentru a putea rezolva o problema folosind metoda Greedy, problema trebuie s satisfac proprietatea:
dac B este o soluie acceptabil i XB atunci i X este o soluie acceptabil
Algoritmul Greedy

Algoritmul Greedy gsete soluia incremetal, construind soluii acceptabile care se tot extind
pe parcurs. La fiecare pas soluia este exstins cu cel mai bun candidat dintre candidaii rmai
la un moment dat. (Strategie greedy - lacom)

Principiu (strategia) Greedy :


adaug succesiv la rezultat elementul care realizeaz optimul local
o decizie luat pe parcurs nu se mai modific ulterior
Algoritmul Greedy

Poate furniza soluia optim (doar pentru anumite probleme)


alegerea optimului local nu garanteaza tot timpul optimul global
solutie optim - dac se gsete o modalitate de a alege (optimul local) astfel ncat se ajunge la
solutie optim
n unele situaii se prefer o soluie, chiar i suboptim, dar obinut n timp rezonabil
Construiete treptat soluia (fr reveniri ca n cazul backtracking)
Furnizeaz o singur soluie
Timp de lucru polinomial
Greedy python

def greedy(c):
"""
Greedy algorithm
c - a list of candidates
return a list (B) the solution found (if exists) using the greedy
strategy, None if the algorithm
selectMostPromissing - a function that return the most promising
candidate
acceptable - a function that returns True if a candidate solution can be
extended to a solution
solution - verify if a given candidate is a solution
"""
b = [] #start with an empty set as a candidate solution
while not solution(b) and c!=[]:
#select the local optimum (the best candidate)
candidate = selectMostPromissing(c)
#remove the current candidate
c.remove(candidate)
#if the new extended candidate solution is acceptable
if acceptable(b+[candidate]):
b.append(candidate)

if solution(b):
return b
#there is no solution
return None
Algoritm Greedy - elemente

1. Mulimea candidat (candidate set) de unde se aleg elementele soluiei


2. Funcie de selecie (selection function) alege cel mai bun candidat pentru a fi adugat la
soluie;
3. Acceptabil (feasibility function) folosit pentru a determina dac un candidat poate contribui la
soluie
4. Funcie obiectiv ( objective function) o valoare pentru soluie i pentru orice soluie parial
5. Soluie (solution function), - indic dac am ajuns la soluie
Exemplu

Se da o sum M i tipuri (ex: 1, 5, 25) de monede (avem un numar nelimitat de monede


din fiecare tip de moned). S se gseasc o modalitate de a plti suma M de bani folosind ct
mai puine monezi.
Set Candidat:
Lista de monede - COINS = {1, 5, 25, 50}
Soluie Candidat:
o list de monede - X =( X X . , X ) unde X COINS moned
0, 1,. k i

Funcia de selecie:
candidate solution: X =( X X . , X )
0, 1,. k

alege moneda cea mai mare care e mai mic dect ce mai e de pltit din sum

Acceptabil (feasibility function):


k
Soluie candidat: X =( X X . , X ) S= X M
0, 1,. k
(i=0) i

suma monedelor din soluia candidat nu depete suma cerut

Soluie:
k
Soluie candidat: X =( X X . , X ) S= X =M
0, 1,. k
(i=0) i

suma monedelor din soluia candidat este egal cu suma cerut


Monede cod python

#Let us consider that we have a sum M of money and coins of 1, 5, 25 units (an unlimited number of coins).
#The problem is to establish a modality to pay the sum M using a minimum number of coins.
def selectMostPromissing(c): def acceptable(b):
""" """
select the largest coin from the remaining verify if a candidate solution is valid
c - candidate coins basically verify if we are not over the sum M
return a coin """
""" sum = _computeSum(b)
return max(c) return sum<=SUM
def solution(b): def printSol(b):
""" """
verify if a candidate solution is an actual solution Print the solution: NrCoinns1 * Coin1 + NrCoinns2 *
basically verify if the coins conduct to the sum M Coin2 +...
b candidate solution """
""" solStr = ""
sum = _computeSum(b) sum = 0
return sum==SUM for coin in b:
nrCoins = (SUM-sum) / coin
def _computeSum(b): solStr+=str(nrCoins)+"*"+str(coin)
""" sum += nrCoins*coin
compute the payed amount with the current candidate if SUM-sum>0:solStr+=" + "
return int, the payment print solStr
b candidate solution
"""
sum = 0
for coin in b:
nrCoins = (SUM-sum) / coin
#if this is in a candidate solution we need to
use at least 1 coin
if nrCoins==0: nrCoins =1
sum += nrCoins*coin
return sum
Greedy

1. Algoritmul Greedy are complexitate polinomial - O(n 2 ) unde n este numrul de elemente din lista
candidat C
2. nainte de a aplica Greedy este nevoie s demonstrm c metoda gsete soluia optim. De multe ori
demonstraia este netrivial
3. Exist o mulime de probleme ce se pot rezolva cu greedy. Ex: Algoritmul Kruskal determinarea
arborelui de acoperire, Dijkstra, Bellman-Kalaba drum minim ntrun graf neorientat
4. Exist probleme pentru care Greedy nu gsete soluia optim. n unele cazuri se prefer o soluie
obinut n timp rezonabil (polinomial) care e aprope de soluia optim, n loc de soluia optim obinut
n timp exponenial (heuristics algorithms).
Programare Dinamic

Se poate folosi pentru a rezolva probleme de optimizare, unde:


soluia este rezultatul unui ir de decizii, d1, d2, ..., dn,

principiul optimalitii este satisfcut.


n general timp polinomial de execuie
tot timpul gsete soluia optim.
Rezolv problema combinn sub soluii de la subprobleme, calculeaz subsoluia doar o singur
data (salvnd rezultatul pentru a fi refolosit mai trziu).

Fie stritle s0, s1, ..., sn-1, sn, unde s0 este starea iniial, sn este starea final, prin aplicariea succesiv a
deciziilor d1, d2, ..., dn se ajunge la starea final (decizia di duce din starea si-1 n starea si, pentru i=1,n):

d1 d2 ... dn
s0 s1 s2 ... sn-1 sn
Programare Dinamic

Caracteristici:
principiul optimalitii;
probleme suprapuse (overlapping sub problems);
memoization.

Principiul optimalitii
optimul general implic optimul parial
la greedy aveam optimul local implic optimul global
ntr-o secven de decizii optime, fiecare decizie este optim.
Principiul nu este satisfcut pentru orice fel de problem. In general nu e adevrat n cazul in care
subsecvenele de decizii nu sunt independente i optimizarea uneia este n conflict cu optimizarea
de la alta secvena de decizii.
Principiul optimalitii
Dac d1 , d 2 ,..., d n este o secven de decizii care conduce optim sistemul din starea iniial
s 0 n starea final s n , atunci una din utmtoarele sunt satisfcute:

1). d k , d k 1 ,..., d n este o secven optim de decizii care conduce sistemul din starea s k 1 n
starea s n , k , 1 k n . ( forward variant decizia dk depinde de deciziile dk+1..dn)
2). d 1 , d 2 ,..., d k este o secven optim de decizii care conduce sistemul din starea s 0 n

starea s k , k , 1 k n . (backward variant)


3). d k 1 , d k 2 ,..., d n i d1 , d 2 ,..., d k sunt secvene optime de decizii care conduc sistemul din

starea s k n starea s n , respectiv, din starea s 0 n starea s k , k , 1 k n . (mixed variant)

Sub-Probleme suprapuse (Overlapping Sub-problems)


O problema are sub-probleme suprapuse daca poate fi inprit n subprobleme care se refolosesc de mai
multe ori

Memorizare (Memorization)
salvarea rezultatelor de la o subproblem pentru a fi refolosit
Cum aplic m programare dinamic

Principiul optimalitii (oricare variant: forward, backward or mixed) este demonstrat.


Se definete structura solutiei optime.
Bazat pe principiul optimalitii, valoarea soluiei optime se definete recursiv. Se definete
o recuren care indic modalitatea prin care se opine optimul general din optime pariale.
Soluia optim se calculeaz in manier bottom-up, ncepem cu subproblema cea mai
simpl pentru care soluia este cunoscut.
Cea mai lung sublist cresctoare

Se d o list a1 , a 2 ,..., a n . Determinai cea mai lung sublist cresctoare a i1 , ai2 ,..., ais a listei date.
Soluie:
Pricipiul optimalitii
varianta nainte forward
The structure of the optimal solution
Construim dou iruri: l l1 .l 2 ,...l n i p p1 . p 2 ,... p n .

lk lungime sublistei care incepe cu elementul ak .

pkindexul elementului a care urmeaz dup elementul ak n sublista cea mai lung care
incepe cu a k .
Definiia recursiv
l n 1, p n 0

l k max{1 li | ai a k , k 1 i n}, k n 1, n 2,...1

p k arg max{1 li | ai a k , k 1 i n}, k n 1, n 2,...,1


Cea mai lung sublist cresc toare python

def longestSublist(a):
"""
Determines the longest increasing sub-list
a - a list of element
return sublist of x, the longest increasing sublist

"""
#initialise l and p
l = [0]*len(a)
p = [0]*len(a)
l[lg-1] = 1
p[lg-1]=-1
for k in range(lg-2, -1, -1):
print p, l
p[k] = -1
l[k]=1
for i in range(k+1, lg):
if a[i]>=a[k] and l[k]<l[i]+1:
l[k] = l[i]+1
p[k] = i

#identify the longest sublist


#find the maximum length
j = 0
for i in range(0, lg):
if l[i]>l[j]:
j=i
#collect the results using the position list
rez = []
while j!=-1:
rez = rez+[a[j]]
j = p[j]
return rez