Sunteți pe pagina 1din 127

Introducere

Pentru rezolvarea unei probleme pe calculator, se parcurg urmtorii pai:


1. se detecteaz problema;
2. se adaug restriciile impuse de domeniul care a creat problema;
3. se creeaz un model matematic;
4. se concepe un algoritm (un mod) de rezolvare a problemei;
5. se scrie un program;
6. se interpreatez rezultatele de la calculator n termeni specifici domeniului
problemei.
Paii 1, 2, 3 sunt n general reunii sub numele de analiz. Paii 4, 5 constituie programarea.
Pasul 6 face parte din implementare.
Deci algoritmul reprezint un pas intermediar ntre modelul matematic i programul realizat
ntr-un limbaj conceput pentru calculator.
Originea cuvntului algoritm este legat de numele Abu Jafar Mohammed IbnMus al
Khowrizm, autor arab al unei cri de matematic.
Proprietile pe care trebuie s le aib un algoritm sunt:
1. Fiecare pas trebuie s conin o operaie bine definit;
2. Pasul care urmeaz fiecrui pas trebuie s fie unic definit;
3. Fiecare pas trebuie s fie efectuabil, adic s poat fi executat cu creionul pe hrtie.
4. Trebuie s se termine n timp finit. c viitoarele maini vor fi mult mai rapide.
Ce vom studia n legtur cu algoritmii:
1. Cum s concepem un algoritm? Crearea unui algoritm nu va fi niciodat pe deplin
automatizat, va rmne o problem a omului n primul rnd i abia apoi a mainii.
2. Exprimarea noastr va fi restrns, din necesitatea de a scrie uor programe
corecte, la algoritmi structurai.
3. Vom nva s verificm corectitudinea algoritmilor concepui.
4. Nu ajunge s construim algoritmi coreci Trebuie s cunoatem i performanele
algoritmilor creai.
Obiectivele cursului
Lucrarea de fa i propune s fie un curs introductiv n teoria algoritmilor.
Se vor introduce noiunile de algoritm, subalgoritm, complexitatea
algoritmilor, diferite metode de elaborare a algoritmilor, probleme clasice.
Cursul algoritmic este un curs de baz pentru meseria de informatician.
La sfritul acestui curs, studenii vor fi capabili s:
elaboreze algoritmi
sa analizeze complexitatea algoritmului
s demonstreze corectitudinea algoritmului

Cerine preliminare
Cursul se va susine studenilor nainte de orice alt curs.
Cunotinele acumulate la acest curs pot fi utile la nelegerea problemelor ridicate
de compilatoare i la elaborarea programelor n diverse limbaje de programare.
Resurse
Pentru a nelege algoritmi proiectai i pentru a vedea cum lucreaz se va folosi
orice limbaj (de exemplu c++ sau Pascal)
Structura cursului
Cursul de algoritmica este structurat n dou module, astfel: primul modul
cuprinde patru uniti de nvare, iar al doilea modul cuprinde ase uniti de
nvare. La rndul su, fiecare unitate de nvare cuprinde: obiective, aspecte
teoretice privind tematica unitii de nvare respective, exemple, teste de
autoevaluare precum i probleme propuse spre discuie i rezolvare.
La sfritul fiecrui modul sunt date teste de autoevaluare. Rezolvarea acestor teste
se gsete la sfritul cursului.
Durata medie de studiu individual
Parcurgerea de ctre studeni a unitilor de nvare ale cursului de baze de date
(att aspectele teoretice ct i rezolvarea testelor de autoevaluare i rezolvarea
problemelor propuse) se poate face n 2-3 ore pentru fiecare unitate.
Evaluarea
La sfritul semestrului, fiecare student va primi o not, care va cuprinde: un
examen scris cu materia din modulele care va deine o pondere de 60% n nota
final i notele aferente algoritmilor elaborai la seminar si programelor elaborate
la laborator.
Spor la treab

CUPRINS
Modulul 1. Limbajul pseudocod i structuri elementare .......................................................... 4
M1.U1. Istoric; comentarii ................................................................................................... 4
M1.U2. Limbajul de descriere al algoritmilor: pseudocod. ................................................... 7
M1.U3. Analiza algoritmilor .............................................................................................. 12
M1.U4. Structuri elementare .............................................................................................. 20
M1.U5.Recursivitate.. ........................................................................................................ 29
Modulul 2. Metode de elaborare a algoritmilor. ..................................................................... 36
M2.U1. Metoda Backtracking. ........................................................................................... 36
M2.U2. Generarea submulimilor ...................................................................................... 41
M2.U3. Metoda Divide et Impera ...................................................................................... 53
M2.U4. Sortare i statistici de ordine ................................................................................. 61
M2.U5. Metoda Greedy ..................................................................................................... 71
M2.U6. Metoda programrii dinamice. .............................................................................. 81
Rspunsuri la teste................................................................................................................. 90
M1.U2.3. Test de autoevaluare a cunotinelor................................................................... 90
M1.U4.3. Test de autoevaluare a cunotinelor................................................................... 96
M1.U5.3. Test de autoevaluare a cunotinelor..................................................................102
M2.U1.3. Test de autoevaluare a cunotinelor..................................................................104
M2.U2.3. Test de autoevaluare a cunotinelor..................................................................111
M2.U3.3. Test de autoevaluare a cunotinelor..................................................................113
M2.U4.3. Test de autoevaluare a cunotinelor..................................................................116
M2.U5.3. Test de autoevaluare a cunotinelor..................................................................118
M2.U6.3. Test de autoevaluare a cunotinelor..................................................................123
Bibliografie ..........................................................................................................................127

Modulul 1. Limbajul pseudocod i structuri elementare


Cuprins
Introducere ............................................................................................................................. 3
U1. Istoric, definiii, generaliti ............................................................................................ 4
U2. Limbajul pseudocod ........................................................................................................ 7
U3. Analiza algoritmilor ...................................................................................................... 12
U4. Structuri elementare ...................................................................................................... 20
U5.Recursivitate.28

Introducere.
De la modelul matematic nu se poate, n general, trece direct la program n orice limbaj ar fi
el. Este nevoie de un limbaj intermediar care s pun n eviden structuri specifice care s
fie, pe urm, transpuse ntr-un limbaj de programare. Metoda a parcurs un drum lung, din
antichitate i a devenit ceea ce este doar n legtur cu apariia calculatorului.

M1.U1. Istoric; comentarii


M1.U1.1. Introducere
Este important s tim cum a evoluat noiunea i modul de reprezentare a
algoritmilor.
M1.U1.2. Obiectivele unitii de nvare
La sfritul acestei uniti de nvare studenii vor fi capabili s:
neleag modul cum a evoluat noiunea de algoritm
S neleag c trebuie totdeauna s scriem mai nti algoritmul i abia
apoi programul.

Durata medie de parcurgere a acestei uniti de nvare este de o or.

Istoric; comentarii
Ab Abdallah Muhammad ibn Musa al-Khwrizm, angajat al Casei nelepciunii din
Bagdad, a fost un savant persan, (780 - 850). El a tradus crile lui Euclid despre introducerea
sistemului de numeraie poziional i rezolvarea algebric a ecuaiilor de gradul I i II fapt care
a adus o important contribuie la dezvoltarea matematicii. Opera lui al-Khwrizm a ajuns n
Europa prin intermediul lui Fibonacci. Numele su a suferit numeroase transformri, fie prin
latinizare, fie prin omisiuni i din Al-Khwarizmi s-a ajuns la Algorism/Algoritm termen folosit
pentru referirea la o serie determinat de pai urmai pentru rezolvarea unei probleme, iar
numele al-jabr dat unei operaii folosite n cadrul algoritmului de rezolvare a ecuaiilor, a
devenit Algebra.
Dezvoltarea tiinei de a scrie un algoritm este strns legat de dezvoltarea
calculatoarelor i de aplicabilitatea lor n cele mai diverse domenii.
Calculatoarele pot fi folosite pentru a rezolva probleme, numai cu ajutorul unor programe
corespunztoare de rezolvare. Noiunea de program (programare) a suferit schimbri n istoria
informaticii. La nceput problemele rezolvate cu ajutorul calculatorului erau simple i
algoritmii, pentru rezolvarea lor, nu erau prea complicai. Prin program se nelegea rezultatul
scrierii unui algoritm ntr-un limbaj de programare. Din cauza creterii complexitii
problemelor, astzi adesea se concep pachete de mai multe programe pentru rezolvarea unei
probleme.
O definiie matematic, riguroas, pentru noiunea de algoritm, este greu de dat, pentru
c este o noiune primar (nu exist genul proxim). Descriem, n continuare, ce se nelege prin
algoritm. Ne familiarizm cu aceast noiune prezentnd mai multe exemple de algoritmi i
observnd ce au ei n comun. Cel mai vechi exemplu este algoritmul lui Euclid, algoritm care
determin cel mai mare divizor comun a dou numere naturale.
Algoritmul reprezint un pas intermediar ntre modelul matematic i programul realizat
ntr-un limbaj conceput pentru calculator.
Proprietile pe care trebuie s le aib un algoritm sunt:
1. Fiecare pas trebuie s conin o operaie bine definit (de exemplu urmtoarele operaii nu
sunt bine definite: 4/0 sau scade 10 sau 20 din 30);
2. Pasul care urmeaz fiecrui pas trebuie s fie unic definit;
3. Fiecare pas trebuie s fie efectuabil, adic s poat fi executat cu creionul pe hrtie: operaii
cu numere ntregi - da, dar operaii cu numere reale n general - nu.
4. Trebuie s se termine n timp finit. Se poate spune c timpul trebuie s fie rezonabil de scurt
i totui vor fi considerai algoritmi care pe mainile actuale necesit zeci de ani pentru c se
presupune c viitoarele maini vor fi mult mai rapide.
Ce vom studia n legtur cu algoritmii?
1. Cum s concepem un algoritm? Crearea unui algoritm nu va fi niciodat pe deplin
automatizat, va rmne o problem a omului n primul rnd i abia apoi a mainii. n aceast
ordine de idei, vom studia n acest curs metode de elaborare a algoritmilor.

2. Exprimarea noastr va fi restrns, din necesitatea de a scrie uor programe corecte, la


algoritmi structurai, adic algoritmi care folosesc anumite structuri specifice care vor fi expuse
la nceputul cursului.
3. Vom nva s verificm corectitudinea algoritmilor concepui. Un algoritm corect trebuie s
rezolve problema propus pentru orice date de intrare. Nu se pune problema verificrii cu
anumite date (acest lucru ine de verificarea programului), ci problema unei demonstraii a
corectitudinii programului (n general ca o teorem).
4. Nu ajunge s construim algoritmi coreci ci trebuie s cunoatem i performanele
algoritmilor creai. Pot exista mai muli algoritmi care s rezolve aceeai problem, unii mai
greu sau mai uor de neles (de urmrit), unii mai lungi n numrul de pai descrii, dar cu
performan urmrit prin analiz (numrul de pai elementari executai, n legtur cu
dimensiunea intrrii). O alt problem este cea legat de dimensiunea memoriei necesare
execuiei programului realizat dup un anumit algoritm.
Rezumat
Originea cuvntului algoritm este legat de numele Abu Jafar Mohammed
IbnMus al Khowrizm, autor arab al unei cri de matematic.
Un algoritm trebuie s aib urmtoarele proprieti:
1. Fiecare pas trebuie s conin o operaie bine definit;
2. Pasul care urmeaz fiecrui pas trebuie s fie unic definit;
3. Fiecare pas trebuie s fie efectuabil.
4. Trebuie s se termine n timp finit.

M1.U2. Limbajul de descriere al algoritmilor: pseudocod.


M1.U2.1. Introducere
De la modelul matematic construim algoritmul pentru care vom folosi limbajul
pseudocod.
M1.U2.2. Obiectivele unitii de nvare
La sfritul acestei uniti de nvare studenii vor fi capabili s:
S utilizeze structurile de baz ale pseudocodului.
S scrie algoritmi simpli.

Durata medie de parcurgere a acestei uniti de nvare este de 3 ore.

Exist dou limbaje de descriere a algoritmilor: schema logic i pseudocodul.


Limbajul schemei logice are avantaje de explicaie grafic, dar i dezavantaje legate de
structurare i translatare, aa c vom folosi pseudocodul. Acest limbaj folosete cuvinte cheie
(subliniate), cuvinte care definesc variabile, constante i text explicativ (ntre acolade). Putem
distinge urmtoarea structur general:
Start Numele programului
citete( date de intrare )
prelucreaz datele
scrie( rezultate )
Stop
Putem specifica:
Liste de date (a, b, x)
Vectori (a(i), i = 1, n)
Matrici ((x(i, j), j = 1, n), i = 1,m)
Constante (Viteza=, v)
Operaia cea mai des folosit este atribuirea.
Instruciunea a=b+c trebuie neleas ca:
b i c au valori calculate dinainte sau citite ca date de intrare.
valorile lui b i c se adun n procesor i rezultatul se va afla ntr-o memorie a
procesorului
din aceast memorie , rezultatul adunrii se duce n a.
Atunci cnd pasul urmtor depinde de o anumit situaie cucerit pn atunci, el este rezultatul
unei decizii care va fi scris astfel:

dac condiie logic


atunci (grup de pai 1)
altfel (grup de pai 2)
sfrit dac
Exemple
Exemplul 1.
Start Algoritm 1
Citeste( i, a )
dac i 1
atunci a = a + i
altfel a = a i
sfrit dac
scrie( a=, a )
Stop
Acest algoritm scrie a = 10 dac intrarea este (0, 10), iar pentru intrarea (2, 10)
scrie a = 8.
Exemplul 2. Urmtorul algoritm gsete cel mai mare dintre trei numere:
Start Algoritm 2
citete( a, b, c )
max = a
dac max < b atunci
max = b
sfrit dac
dac max < c atunci
max = c
sfrit dac
scrie( Maximul este , max )
Stop
Urmrii algoritmul Algoritm 2 pentru valorile de intrare (10, 1, 3), (2, 2, 5),
(1, 12, 2).
Exist grupuri de instruciuni care se execut n mod repetat. Aceste structuri se numesc
cicluri i apar des n elaborarea algoritmilor. Prima form de ciclare, pe care o studiem,se
numete ciclu cu numrtor i se folosete atunci cnd se tie de cte ori se execut un grup de
instruciuni. Are forma:
pentru ind = vi, vf, pas
grup de pai
sfrit pentru

Dac pas = 1, atunci el se poate omite. Grupul de pai din interiorul ciclului poate s nu
se execute niciodat, dac vi > vf (pentru pas > 0), respectiv dac vi < vf (pentru pas < 0). De
exemplu, ciclul de mai jos nu se execut dac n < 1:
pentru ind = 1, n
grup de pai
sfrit pentru
Exemple
Exemplul 3. Algoritmul de mai jos calculeaz suma componentelor unui
vector a(i), i = 1, n:
Start Algoritm 3
citete( n, (a(i), i = 1, n) )
s = 0 {0 este element neutru pentru adunare}
pentru i = 1, n, 1
s = s + a(i)
sfrit pentru
scrie( suma este , s )
Stop
Exemplul 4. Pentru a numra componentele pozitive (np), negative (nn) i cele
nule (nz) ale unui vector vom avea urmtorul algoritm:
Start Algoritm 4
citete( n, (a(i), i = 1, n) )
np = 0
nn = 0
nz = 0
pentru i = 1, n
dac a(i) > 0 atunci
np = np + 1
altfel
dac a(i) < 0 atunci
nn = nn + 1
altfel
nz = nz + 1
sfrit dac
sfrit dac
sfrit pentru
scrie( Pozitive= , np, Negative= , np, Nule= , nz )
Stop
Scriei un algoritm care face produsul numerelor diferite de 0 dintr-un vector
cu n componente (A(i),i=1,n)

Acest tip de ciclu se numete ciclu cu numrtor. Mai avem i cicluri cu condiie, care poate fi
nainte sau dup corpul ciclului (grupul de pai ce se execut de fiecare dat). Ciclul cu test
anterior are forma:
ct timp (condiie logic)
grup de pai
sfrit ct timp
Corpul ciclului poate s nu se execute niciodat, dac codiia logic este de la nceput fals.
Exemple
Exemplul 5. Algoritmul urmtor (algoritmul lui Euclid) servete la determinarea celui mai mare divizor comun a dou numere:
Start Euclid
citete( a, b )
m=a
n=b
ct timp n 0
r = rest(m/n)
m=n
n=r
sfrit ct timp
scrie( CMMDC dintre , a, i , b, este , m )
Stop
1. Urmrii algoritmul pentru intrrile (10, 3) i (258, 12).
2. Scriei un algoritm care adun componentele unui vector pn la prima
component negativ.
Ciclul cu test posterior are forma:
repet
grup de pai
pn cnd condiie logic
Grupul de pai se execut cel puin o dat.
Exemple
Exemplul 6. Se cere listarea combinrilor din {1, 2, 3, 4, 5} luate cte 3:
Start Combinri
pentru i = 1, 3
j=i
repet
j=j+1
k=j
repet
10

k=k+1
scrie( i,-,j,-,k ) {afieaz o combinaie}
pn cnd k = 5
pn cnd j = 4
sfrit pentru
Stop
Organizai execuia algoritmului de mai sus folosind un tabel de gestionare a
datelor de intrare i de ieire.
Rezumat
Un algoritm are o structur bine definit.
Instruciunile prezentate, mpreun cu noiunea de subalgoritm formeaz un
vocabular suficient pentru scrierea algoritmilor.
Sau prezentat: forma general a unui algoritm, citirea i scrierea datelor,
atribuirea, instruciunea de decizie i cele trei forme de ciclare.
M1.U2.3. Test de autoevaluare a cunotinelor
1) Se definete produsul scalar a doi vectori A i B, de aceeai dimensiune n
n

A(i ) B (i ) . S se scrie algoritmul pentru calculul

cu formula: PS
i 1

produsului scalar.
2). Se d n, vectorul A(i), i=1, n i un numr x. S se scrie un algoritm care
gsete indicele j pentru care A(j)=x (problema cutrii).
3. Cum se modific algoritmul de mai sus dac tim c vectorul este sortat
cresctor?
4. Se dau n i vectorul A(i), i=1, n. S se gseasc cel mai mic element.
5. Reluai problema anterioar n situaia n care trebuie s aflai cte elemente
minime sunt. ncercai s rezolvai problema folosind o singur parcurgere a
irului.
6. Se dau n i vectorul A(i), i=1, n. Ce numr apare cel mai des i de cte ori?
7. Cum se modific algoritmul precedent dac vectorul este deja sortat ?

11

M1.U3. Analiza algoritmilor


M1.U3.1. Introducere
Putem uor s ne dm seama c pentru a rezolva o anumit problem pot exista
mai muli algoritmi. De exemplu, se tie deja c pentru aflarea celui mai mare
divizor comun a dou numere se poate folosi fie algoritmul lui Euclid, fie un
algoritm bazat pe descompunerea numerelelor n produse de factori primi.
Se pune ntrebarea:
Care dintre aceti algoritmi este mai bun?
i n general:
Cum putem compara doi algoritmi care rezolv aceeai problem?
Cu rspunsul la aceste ntrebri se ocup analiza algoritmilor.
M1.U3.2. Obiectivele unitii de nvare
La sfritul acestei uniti de nvare studenii vor fi capabili s:
estimeze timpul necesar execuiei unui algoritm
utilizeze corect funciile asimptotice.

Durata medie de parcurgere a acestei uniti de nvare este de 2 ore.

Analiza algoritmilor
A analiza un algoritm nseamn a preciza ce resurse (de memorie i timp) o s aib
nevoie atunci cnd va fi translatat n program care rezolv pe calculator problema respectiv.
Vom parcurge treptele spre noiunile de baz ale analizei algoritmilor studiind doi
algoritmi de sortare. Primul de care ne ocupm este sortarea prin inserie.
Pentru problema sortrii avem: la intrare un ir de n valori numerice (a1, a2, . . . , an)
i vrem la ieire un ir (a_1, a_2, . . . , a_n), obinut prin permutarea valorilor iniiale, astfel
nct a_1 a_2 a_n. Ideile algoritmului descris mai jos (sortare prin interclasare) pot fi
nelese dac ne gndim la aranjarea crilor de joc: avem pe mas, cu faa n jos, o serie de
cri pe care le putem lua una cte una n mn, deplasndu-le pn la locul pe care trebuie s l
ocupe.
Exemplu
Fie irul A = 3 1 5 2 7 4 .Acest ir va fi parcurs de un cursor i (se ia cte o
carte) i mutat n cadrul irului cu ajutorul unui cursor j care parcurge pe A_
pn la locul potrivit:
pentru i = 1: A_ = 3
pentru i = 2, elementul A(2) = 1 este mai mic dect 3, deci trebuie
pus n faa lui: A_ = 1 3
pentru i = 3: A_ = 1 3 5
12

pentru i = 4: A_ = 1 2 3 5
pentru i = 5: A_ = 1 2 3 5 7
pentru i = 6: A_ = 1 2 3 4 5 7
Urmrii, ntr-un tablel, execuia algoritmului de mai jos cu datele de intrare:
A=11, 3, 44, 22, 35, 6, 33,
S observm c nu avem nevoie de un spaiu suplimentar pentru rezultat i putem face
ceea ce se numete sortare in place (pe loc) (i.e. n afara irului nu se in dect cel mult un
numr constant de elemente ale irului) n pseudocod algoritmul poate fi scris astfel:
Start Sortins
1. citete( n, (A(i), i = 1, n) )
2. pentru i = 2, n
3. k = A(i) {insereaz A(i) n irul sortat A(1), . . . , A(i 1)}
4. j = i 1
5. ct timp j > 0 i A(j) > k
6. A(j + 1) = A(j)
7. j = j 1
sfrit ct timp
8. A(j + 1) = k {a terminat inserarea}
sfrit pentru
9. scrie( A(i), i = 1, n )
Stop
Dintre resursele consumate ne va interesa n primul rnd timpul de rulare. Timpul de
rulare al unui algoritm pentru o anumit intrare (instan) este exprimat prin numrul de pai
elementari executai.
Pentru nceput vom considera ca pas elementar fiecare linie parcurs din algoritmul n
pseudocod i timpul pentru un astfel de pas o constant diferit pentru fiecare linie (dei este
evident c unele din aceste constante sunt egale) ncepem prin a prezenta algoritmul Sortins cu
timpul fiecrei instruciuni i numrul de execuii ale liniei respective. Vom nota cu ti numrul
de execuii ale ciclului de la linia 5 (evident c el este diferit pentru fiecare valoare a lui i).
Numr
linie

Instruciune

Timp

Numr de operaii

Start Sortins
1

citete( n, (A(i), i = 1, n) )

c1

n+1

pentru i = 2, n

c2

n-1

k = A(i)

c3

n-1

13

j=i1

c4

n-1
n

ct timp j > 0 i A(j) > k

c5

A(j + 1) = A(j)

c6

j=j1

c7

i 2

ti

n
i 2

ti 1

n
i 2

ti 1

sfrit ct timp
8

A(j + 1) = k

c8

n-1

c9

sfrit pentru
9

scrie( A(i), i = 1, n )
Stop
n

T ( n)

c1 (n 1) c 2 (n 1) c3 (n 1) c 4 (n 1) c5

i 2

t i (c 6

c7 ) (t i 1) (c6
i 2

c7 ) (t i 1)
i 2

c8 (n 1) c9 n

Cum putem s-l aflm pe ti? O s ne punem, de fapt, problema intervalului n care se
plimb, studiind cele dou cazuri care corespund limitelor intervalului.
Cel mai rapid caz se obine cnd irul este gata sortat i deci n linia 5 vom avea A(j)
k pentru orice j = 2, . . . , n deci n acest caz ti = 1, iar T(n) = a* n + b, unde a, b constante, deci
T funcie liniar de n.
Cel mai ru caz se obine cnd irul dat este sortat descresctor, ceea ce nseamn c
pe linia 5 vom avea A(j) > k pentru orice j de la i1 la 1, deci dac t i i 1 , atunci
n
n(n 1)
(n 2)(n 1)
i
, deci T (n) an 2 bn c este o funcie
(i 2)
i 2
i 2
2
2
ptratic de n.
S vedem acum dac sortarea se poate face altfel, eventual mai repede. Pentru aceasta
vom adopta o alt metod, divide et impera. Aceast metod pornete de la ideea c
problema pus pentru o intrare mare (numr mare de date) este mai greu de rezolvat dect o
problem cu un numr redus de date, de exemplu un ir format dintr-un singur element.
Principiul divide et impera funcioneaz astfel:
1. se mparte problema n mai multe subprobleme
2. se rezolv fiecare din subprobleme (n general recursiv)
3. se combin soluiile paiale ntr-o soluie final
Vom reveni cu amnunte despre aceast metod mai trziu.
n

(i 1)

14

Algoritmul sortrii prin interclasare (mergesort) se bazeaz pe metoda divide et


impera. S vedem ce nseamn interclasarea: din dou iruri sortate cresctor s se creeze irul
sortat tot cresctor care conine toate elementele celor dou iruri iniiale.
Aceasta d urmtorul algoritm:
Start Interclasare
citete( n, (A(i), i = 1, n) )
citete( m, (B(j), j = 1,m) )
k=0
i=1
j=1
repet
k=k+1
dac A(i) < B(j) atunci
C(k) = A(i)
i=i+1
altfel
C(k) = B(j)
j=j+1
sfrit dac
pn cnd i > n sau j >m
{Ciclul de mai jos poate s nu se execute nici o dat}
pentru il = i, n
k=k+1
C(k) = A(il)
sfrit pentru
{Ciclul de mai jos poate s nu se execute nici o dat}
pentru jl = j, m
k=k+1
C(k) = B(jl)
sfrit pentru
{Din cele dou cicluri pentru de mai sus, se execut exact unul singur}
scrie(C(kl), kl = 1, m + n)
Stop
Se observ c timpul total de execuie pentru algoritmul de interclasare este T(n,m) =
c(n + m) unde c este timpul maxim necesar execuiei instruciunilor din interiorul ciclurilor
repet i pentru (c nu depinde de m, n).
Urmrii, ntr-un tablel, execuia algoritmului de mai sus. cu datele de intrare:
A=3, 5, 66, 77, 109, 304, iar B= 2, 4, 6, 8, 33, 55, 66, 88
Pentru algoritmul de sortare prin interclasare prezentm noiunea de subalgoritm.

15

Algoritmi parametrizai (subalgoritmi)


Rareori un algoritm, ca aceia prezentai pn acum, constituie probleme independente;
n general, algoritmii pentru rezolvarea unor probleme complexe au pri care se combin ntre
ele.
Pentru punerea n eviden a unei astfel de structuri modulare vom folosi algoritmi
parametrizai numii i subalgoritmi. Parametrii vor fi variabile ale cror valori vin din
algoritmul care cheam i se ntorc napoi n acesta, deci efectele produse asupra parametrilor
de intrare vor exista i dup ce subalgoritmul se termin.
Algoritmul Interclas parametrizat ar fi: Interclas(A, n, B, m, C) ceea ce nseamn:
interclaseaz A (avnd n componente) cu B (avnd m componente) i obine C.
ntr-un algoritm parametrizat nu mai au ce cuta instruciunile de intrare i nici instruciunea
Stop; n locul acesteia din urm vom folosi Return care nseamn ntoarce-te n
(sub)algoritmul chemtor.
Exemplu: Interclas(A, n,B,m, C).
Corespondena dintre parametrii de chemare i cei din subalgoritm se face poziional.
Pentru uurarea exprimrii, vom considera i subalgoritmi de tip funcie care returneaz o
valoare. n acest caz, valoarea care se determin de ctre subalgoritm este atribuit numelui
funciei.
Exemplu
ArieCerc(r)
ArieCerc = r r
Return
Apelul unui astfel de subalgoritm se face la fel ca mai nainte. n plus, valoarea
returnat poate fi folosit ntro expresie sau ntro atribuire:
(Relativ la limbaje de programare: transmiterea parametrilor se face prin
adres.)
STotal = ArieCerc(10) 5
S ne reamintim...
Algoritmii parametrizai (subalgoritmii) sunt algoritmi care sunt apelai
din alt algoritm prin parametri care sunt i de intrare i de ieire i se
termin n return (revenire n algoritmul chemtor).
n continuare vom folosi o variant special a interclasrii numit Merge(A, p, q, r).
Acest algoritm interclaseaz elementele A(p), . . . , A(q) cu elementele A(q + 1), . . . , A(r) i
pune rezultatul n locul A(p), . . . , A(r).
Aa cum am precizat la nceputul acestui capitol algoritmul sortrii prin interclasare
Mergesort se bazeaz pe metoda divide et impera, ai crei pai au fost deja prezentai.
Evideniem n continuare paii acestui algoritm:

16

1. mparte irul iniial n dou iruri


2. Rezolv (recursiv) fiecare din cele dou subprobleme
3. Combin (interclaseaz) cele dou subiruri sortate pentru a obine irul sortat
Exemplu
Fie irul: 2 1 5 3
PAS1. mprire: 2 1 5 3
PAS2. sortare (recursiv)
(a) mpire: 2 1
(b) Sortarea este evident, fiecare parte avnd cte un element
(c) interclasare 1 2
(a) mpire: 5 3
(b) Sortarea este evident, fiecare parte avnd cte un element
(c) interclasare 3 5
PAS3. interclasare: 1 2 3 5
Urmrii acest algoritm pentru:
A= 3,5,4,8,11,12,10,2
Algoritmul va fi:
Mergesort(A, p, r)
dac p < r atunci
q = _p+r
2 _ {partea ntreag}
{q este indicele elementului de la jumtatea irului} cheam Mergesort(A, p, q)
cheam Mergesort(A, q + 1, r)
cheam Merge(A, p, q, r)
sfrit dac
Return
Acest algoritm poate fi folosit de exemplu astfel:
Start Sortare
citete( n, (A(i), i = 1, n) )
cheam Mergesort(A, 1, n)
scrie( Sirul sortat este: , (A(i), i = 1, n) )
Stop
Observaii:
1. Sortarea const de fapt din interclasarea perechilor de iruri de un element, de dou
elemente, . . . , pn cnd interclasm o pereche de iruri de n/2 elemente, obinnd irul sortat.
2. Dac la un moment dat un ir are un numr impar de elemente, atunci se va mpri n dou
subiruri de dimeniuni care difer printr-o unitate (subirurile sunt ct mai echilibrate ca numr
de elemente).

17

Verificai execuia algoritmului pentru irul 3 2 5 6 4 1 0 7

Notaia asimptotic
Pentru a vedea cum se comport timpul de execuie al unui algoritm pentru intrri mari
vom defini funciile , , O.
Spunem c T(n) = (f(n)) dac N, c1, c2 > 0 astfel nct: 0 < c1f(n) < T(n) < c2f(n), n > N
Spunem c T(n) = (f(n)) dac N, c1 > 0 astfel nct: 0 < c1f(n) < T(n), n > N 3.2)
Spunem c T(n) = O(f(n)) dac N, c1 > 0 astfel nct: T(n) < c1f(n), n > N
Lsm n seama cititorului s demonstreze proprietile de mai jos.
Fie F {,,O}. Atunci:
1. Tranzitivitate:
T(n) = F(f(n)) i f(n) = F(g(n)) T(n) = F(g(n))
2. Reflexivitate:
T(n) = F(T(n))
3. Simetrie:
f(n) = (g(n)) g(n) = (f(n))
4. Bisimetrie:
f(n) = O(g(n)) g(n) = (f(n))
f(n) = (g(n)) g(n) = O(f(n))
5. Dac M astfel nct g(n) > f(n), n > M atunci (f(n) + g(n)) = (g(n))
6. T(n) = (f(n)) {T(n) = O(f(n)) i T(n) = (f(n))}
7. (cf(n)) = (f(n)), c constant > 0
Aplicnd proprietile 1-7 se demonstreaz imediat urmtoarele:
1. T(n) = a n2 + b n + c T(n) = (n2)
2. T(n) = c1 n log n + c2 n + c3 T(n) = (n log n)
Din cele de mai sus, precum i analiza fut la algoritmul de sortare prin inserie rezulta c
complexitatea acestuia este T(n) = (n2).
Calculul complexitii algoritmului de sortare prin interclasare se efectueaz mai jos.
Cnd un algoritm conine o apelare a lui nsui se numete recursiv. n acest caz timpul
de execuie se descrie printr-o ecuaie recursiv. Pentru rezolvarea unei asemenea ecuaii vom
folosi metode matematice care vor fi descrise n capitolul dedicat metodei Divide et impera.
Ne vom limita acum la a scrie recurena pentru sortare prin interclasare.
Dac timpul pentru un ir cu n elemente este T(n), atunci timpul pentru irul cu n/2
a
, dac.n 1
n
T
(
n
)
elemente va fi T (n/2), deci:
2 T
c n , dac.n 1
2

18

Mai departe vom demonstra c T(n) = (n log2 n). Deci complexitatea algoritmului
Mergesort este (n log2 n).
n final, deducem c complexitatea algoritmului de sortare prin inserie ((n2)) este mai
mare dect cea a algoritmului Mergesort ((n log2 n)), altfel spus Mergesort este mai
performant dect sortarea prin inserie.
Rezumat
Pentru compararea a doi algoritmi, cel mai utilizat criteriu este cel al timpului
de execuie relativ la dimensiunea datelor de intrare. Calculul complexitii
unui algoritm este o etap obligatorie a soluiei acestuia. Pentru exprimarea
complexitii se folosesc notaiile O(), (), () care sunt legate de viteza de
variaie a timpului cerut pentru rezolvarea unei instane, n funcie de
dimensiunea intrrii i care sunt cu att mai corecte cu ct n este mai mare.
Subalgoritmii sunt o modalitate de a concepe blocuri de cod parametrizate,
apelabile prin intermediul unei interfee (liste de parametri de apel). Ele
efectueaz prelucrri asupra datelor transmise, rezultatul returnat fiind folosit
mai apoi de algoritmul apelant. De asemenea, reprezint o form de
abstractizare, utilizat n toate paradigmele de programare.
M1.U3.3. Test de autoevaluare a cunotinelor
1. Determinai complexitatea algoritmilor din unitatea de nvare precedent..
2. Se dau n, ((A(i, j), j = 1, n), i = 1, n) (A este o matrice ptratic, cu n linii i
n coloane).
S se calculeze produsul elementelor diferite de 0: (a) din toat matricea;
(b) de pe diagonala principal;
(c) de sub diagonala principal.

19

M1.U4. Structuri elementare


M1.U4.1. Introducere
Aproape n orice algoritm se folosesc structuri suplimentare. Cele mai uzuale sunt
prezentate aici.
M1.U4.2. Obiectivele unitii de nvare
. La sfritul acestei uniti de nvare studenii vor fi capabili s:
foloseasc structurile liniare stiva i coada
s foloseasc arborii binari.

Durata medie de parcurgere a acestei uniti de nvare este de trei ore.

Structuri liniare
O structur liniar este o mulime de n 0 componente x(1), x(2), . . . x(n) cu
proprietile:
1. cnd n = 0 spunem c structura este vid;
2. dac n > 0 atunci x(1) este primul element iar x(n) este ultimul element;
3. oricare ar fi x(k) unde k {2, ..., n1} exist un predecesor x(k 1) i un succesor x(k + 1).
Ne va interesa s executm cu aceste structuri urmtoarele operaii:
- adugarea unui element;
- extragerea unui element;
- accesarea unui element x(k) din structur;
- combinarea a dou sau mai multe structuri ntr-una singur;
- ruperea unei structuri n mai multe structuri;
- sortarea elementelor unei structuri;
- cutarea unui element al structurii care are anumite proprieti;
- operaii specifice
Stiva
Una din cele mai cunoscute structuri liniare este stiva. O stiv este caracterizat prin
disciplina de intrare i de ieire. S considerm o mulime de cri puse una peste alta; exist o
prim carte care se poate lua foarte uor (TOP) i o carte carte care se poate lua numai dac
senltur toate celelate cri (BOTTOM).
Disciplina unei stive este ultimul intrat, primul ieit (disciplina care nu v-ar place s
fie respectat cnd stai la rnd la lapte), prescurtat LIFO (Last In, First Out).
Diferenele fa de un vector sunt:
- un vector are o lungime fix, nonzero, cunoscut aprioric;

20

- o stiv poate fi vid;


- stiva are un numr variabil de elemente n timpul execuiei unui algoritm
Se pune problema reprezentrii concrete a unei stive n memoria unui calculator.
Putem aloca o stiv n dou moduri: secvenial sau nlnuit.
Alocarea secvenial a stivei
Folosim vectorul ca fiind structura cea mai apropiat de structura real a memoriei. n
vectorul (x(i), i = 1, n) considerm:

adic din cele n componente ale unui vector doar primele k elemente fac parte din stiv.
Algoritmul de intrare n stiv este:
a Stiva
dac k = n atunci Depire
altfel
k=k+1
x(k) = a
sfrit dac
Return
Algoritmul de ieire (scoatere) din stiv este:
a Stiva
dac k = 0 atunci Stiva vid
altfel
a = x(k)
k=k1
sfrit dac
Return
Alocarea nlnuit a stivei
n alocarea nlnuit fiecare element al structurii este nsoit de adresa de memorie la
care se afl precedentul element. Vom folosi semnul * cu sensul nici o adres de memorie.
Vom avea vrful stivei pus n eviden (ancorat) n INC i elementul de la baza stivei va avea
conine n cmpul LEG *, ca n figur:

(ordinea 1, 2, . . . k este ordinea intrrii n stiv).

21

n acest caz intrarea n stiv va folosi stiva de locuri libere1 (aceast stiv se numete
LIBERE), pentru a obine noi locuri la introducerea n stiv. Vom prezenta n continuare
algoritmii de intrare/ieire dintr-o stiv n cazul n care alocarea ei a fost nlnuit. Algoritmul
de intrare n stiv este:
a Stiva {a este informaia introdus n stivc}
y LIBERE{alocare de memorie pentru elementul nou}
INFO(y) = a
LEG(y) = INC
INC = y
Return
La nceput, INC =*, stiva neconinnd nici un element.
Algoritmul de ieire din stiv este:
a Stiva {a este informaia extras din vrful stivei nevide}
dac INC =* atunci Stiva este vid
altfel
a = INFO(INC)
aux = INC
INC = LEG(INC)
aux LIBERE{Eliberarea spaiului de memorie}
sfrit dac
Return
S ne reamintim...
Stiva este o structur liniar cu diciplina ultimul intrat .. primul ieit.

Coada
O alt structur de date folosit n concepia algoritmilor este coada. O coad este
caracterizat i ea de o disciplin de intrare/ieire, bineneles diferit de cea a stivei. De data
aceasta putei s v gndii la coada la lapte: primul care s-a aezat al coad va fi primul servit,
adic primul care iese din coad.
Disciplina unei cozi este deci primul venit, primul plecat (first in, first
out - FIFO). Cunoscut n cadrul limbajelor de programare drept heap; obinerea de memorie
se face prin alocare dinamic, eliberarea ei prin dealocare. O coad poate fi vid i are i ea un
numr variabil de elemente n timpul execuiei unui algoritm.
Alocarea secvenial a cozii
Coada alocat secvenial i va gsi locul tot ntr-un vector (X(i), i =1, n):

22

Din cele n componente ale vectorului, doar componentele x(F), . . . , x(S) fac parte din coad.
Algoritmul de intrare n coad este:
a Coada
dac S = n atunci Depire
altfel
S=S+1
x(S) = a
sfrit dac
Return
Algoritmul de ieire din coad este:
a Coada
dac F > S atunci Coada este vid
altfel
a = x(F)
F=F+1
sfrit dac
Return
Se poate imagina uor c procednd n acest mod (scond din fa i introducnd la
sfrit), coada migreaz spre dreapta i poate s ajung n situaia de depire cnd de fapt
mai exist mult loc gol (n vectorul x) pe primele poziii. Apare astfel ideea de a folosi
elementele vectorului ca i cum ar fi dispuse circular, precum n figur

23

Lsm n seama cititorului s scrie algoritmii de intrare/ieire din coada circular.


Alocarea nlnuit a cozii
Alocarea nlnuit se face similar cu alocarea nlnuit a stivei n noduri de tipul:

Algoritmul de intrare n coad este:


a Coada
y LIBERE
INFO(y) = a
LEG(y) =
dac FA=* atunci {Coada este vid} FA=y
altfel
LEG(SFRIT)=y
sfrit dac
SFRIT=y
Return
Iniial, coada este vid, i.e. FA=SFRIT=*.
Algoritmul de ieire din coad este:
a Coada
dac FA=* atunci Coada este vid
altfel
a = INFO(FA)
aux =FAA
FALEG(FA)
aux LIBERE
sfrit dac
Return
S ne reamintim...
Coada este o structur liniar cu disciplina primul intrat primul ieit.

24

Arbori binari
Un arbore n care orice vrf (nod) are 0, 1 sau 2 descendeni se numete arbore binar.
Un arbore binar care are vrfuri cu 0 sau 2 descendeni se numete arbore binar strict. Un
astfel de arbore se poate folosi, de exemplu, la reprezentarea n memorie a unei expresii
aritmetice.
2
Exemplu: expresia (a b) c
se va reprezenta astfel:
c3

**

Unde ** este semnul pentru ridicarea la putere.


Frunzele acestui arbore (nodurile fr descendeni) conin operanzii, iar celelalte noduri
conin operatorii. Deoarece nu toate operaiile sunt comutative, este foarte important dac un
nod este descendent pe stnga sau pe dreapta. Rdcina unui arbore este nodul care nu este
descendentul nimnui.
Alocarea unui arbore binar se poate face:
- secvenial
- nlnuit
- mixt
Alocarea secvenial a unui arbore binar
n vectorul (x(i), i = 1, n) vom avea urmtoarele reguli:
- rdcina este n x(1);
- pentru fiecare nod x(i), descendentul din stnga este x(2i) iar cel din dreapta este x(2i + 1);
- dac nu exist descendent, se pune *.
Reprezentarea secvenial a arborelui din figura 4.2 este:

Observaie: Poziiile x(10), . . . x(13), x(16), . . . x(31) conin *.


Vom volosi aceast alocare n capitolul destinat sortrii pentru a construi o structur de date
asemntoare, numit HEAP.

25

Alocarea nlnuit a unui arbore binar


n acest caz se folosesc noduri de forma INFO | LS | LD unde LS i LD conin adresele
de memorie ale nodurilor descendent stng, respectiv descendent drept. Reprezentarea
nlnuit a arborelui din figura de mai sus este dat n aici

Alocarea mixt a unui arbore binar


La acest tip de alocare se folosesc trei vectori INFO, LS, LD unde LS(i) i LD(i) conin
indicii unde se afl memorai descendentul stng, respectiv descendentul drept ai nodului i.
Reprezentarea mixt a acestui arbore este:
INFO LS LD

La arborii binari ne intereseaz problema parcurgerii lor. inem cont de faptul c:


- arborele binar se definete recursiv ca: rdcin, subarbore stng, subarbore drept (fiecare
subarbore este la rndul lui un arbore binar, posibil vid);
- mai nti se va parcurge subarborele stng apoi cel drept.

26

Dup poziia rdcinii (R) relativ la subarborele stng (S) i subarborele drept (D), avem
urmtoarele trei tipuri de parcurgeri ale unui arbore binar:
1. postordine (SRD);
2. preordine (RSD)
3. inordine (SRD)
Exemple de parcurgeri:
1. Parcurgerea arborelui din figura 4.2 n preordine:
- + a b c / 2 ** c 3 acest scriere se numete scriere polonez prefixat a unei expresii
aritmetice.
2. Parcurgerea arborelui din figura 4.2 n inordine:
a + b c- 2 / c ** 3
3. Parcurgerea arborelui din figura 4.2 n postordine:
a b + c 2 c 3 ** / - acest scriere se numete scriere polonez postfixat a unei expresii
aritmetice.
Dm n continuare algoritmul iterativ pentru parcurgerea n preordine a unui arbore
binar. Vom avea nevoie de o STIV care va conine nodurile succesive parcurse pe stnga care
urmeaz s fie parcurse i pe dreapta; rdacina arborelui se afl n rad.
Preordine iterativ(rad)
STIVA = {STIVA este vid} nodCurent = rad
maiAmNoduri = adevarat
repet
parcurgPeStanga = adevarat
repet
dac nodCurent _= atunci
scrie( INFO(nodCurent) )
{parcurgere pe stnga}
nodCurent STIV A
nodCurent = LS(nodcurent)
altfel
parcurgPeStanga = fals
sfrit dac
pn cnd parcurgPeStanga = fals
{parcurgere pe dreapta}
dac STIV A = atunci
maiAmNoduri = fals
altfel
nodCurent STIV A
nodCurent = LD(nodCurent)
sfrit dac

27

pn cnd maiAmNoduri = adevarat


Return
Se pot da algoritmi iterativi i pentru celelalte dou parcurgeri (vezi problema 2 de la
exerciii). De asemenea se pot da algoritmi recursivi pentru parcurgeri, mai uor de neles, dar
ceva mai ineficieni.
Rezumat
Pentru prelucrarea datelor se folosesc diferite structuri adecvate: liste, stive,
cozi, arbori (sunt cele mai des utilizate, dar mai multe vor fi studiate ntrun
curs dedicat de structuri de date). Exist diferite tipuri de reprezentri ale
acestor structuri: pe baz de tablouri, folosind alocarea dinamic, sau o variant
mixt (la arbori). Stiva i coada au politici specifice de acces, iar arborele poate
fi parcurs n moduri diferite pentru procesarea datelor.
M1.U4.3. Test de autoevaluare a cunotinelor
1. Scriei algoritmii de intrare/ieire din coada circular.
2. Scriei algoritmii iterativi pentru parcurgerea arborilor binari n inordine i
postordine.
3. Se d o stiv alocat nlnuit i o valoare x. S se extrag din stiv, dac
exist, elementul cu INFO = x.
4. Se d o stiv i o valoare x. S se introduc n stiv dup elementul cu INFO
= x un element cu INFO = y.
5. S se implementeze o coad folosind dou stive. S se analizeze
complexitatea.
6. S se implementeze o stiv prin dou cozi. S se analizeze complexitatea.
7. S se dea algoritmi recursivi pentru parcurgerea n preordine, postordine,
inordine a unui arbore binar.

28

M1.U5.Recursivitate..
MI.U5.1. Introducere
Recursivitatea reprezint un mod de elaborare a algoritmilor, caracterizat
prin faptul c un subalgoritm se apeleaz pe el nsui (direct sau indirect).

MI.U5.2. Obiectivele unitii de nvare


La sfritul acestei uniti de nvare studenii vor fi capabili s:
neleag modul de lucru cu algoritmi recursivi
construiasc corect algoritmi recursivi

Durata medie de parcurgere a acestei uniti de nvare este de trei ore.

Recursivitatea a aprut din necesiti practice, permind implementarea unei funcii


matematice recursive. Nu reprezint o modalitate mai puternic sau mai slab din punct de
vedere al rezultatelor ce se pot obine comparativ cu metodele iterative; sa demonstrat c
aceste dou abordri sunt egale ca i putere de calcul. Algoritmii recursivi nu sunt mai rapizi
dect algoritmii iterativi echivaleni, dar uneori sunt mai expresivi i mai uor de ntreinut
(modificat). Pentru nelegerea algoritmilor recursivi, este esenial nelegerea lucrului cu
stiva. nainte de a se efectua un apel de subalgoritm (recursiv sau nu), se salveaz pe stiv
adresa de memorie a instruciunii de la care se va continua execuia dup ntoarcerea din apel.
La intrarea ntrun subalgoritm se rezerv spaiu pe stiv pentru parametrii transmii i pentru
variabilele care au fost declarate n subalgoritm (variabile locale). La ieirea din apel, stiva se
elibereaz de informaia depus la intrarea n apel, iar adresa urmtoarei instruciuni ce se va
executa se preia de pe vrful stivei (i de asemenea aceast informaie va fi scoas de pe stiv).
Urmtorul program nu are o utilitate zdrobitoare, dar ilustreaz cele spuse mai
nainte. Pentru a simplifica expunerea, vom considera c transmiterea parametrilor se
face prin valoare (pe stiv se copiaz valorile parametrilor) i nu prin adres (cnd pe
stiv se copiaz adresa de memorie a parametrilor efectivi). n cazul exemplului de
mai jos, ntruct n corpul subalgoritmului nu se modific valoarea lui n, rezultatul ar fi
acelai indiferent de convenia de apel.

29

Start Exemplu
citete( n )
Recursiv(n)
scrie( Sfrit )
Stop
Recursiv(n)
i = 2 n
{i este variabil local, alocat n subalgoritmul Recursiv}
dac n = 0 atunci
Return
altfel
Recursiv(n 1)
i = i + 1{100}
scrie( i )
sfrit dac
Return
Verificai execuia pentru n egal cu 3..

Ca i comentarii sunt trecute nite adrese la care se presupune a se afla


instruciunile corespunztoare. S presupunem c se va citi n = 2. Evoluia stivei programului
este:
La intrarea n algoritmul Exemplu, se rezerv spaiu pe stiv pentru variabila n, n care dup
citire se depune valoarea 2:
n=2
nainte de apelul Recursiv(2) din algoritmul Exemplu se scrie pe stiv adresa de memorie a
instruciunii care se va executa dup ce se va reveni din acest apel (n cazul nostru
instruciunea de scriere cu adresa 10):
adr = 10
n=2
Se apeleaz Recursiv(2); se scrie pe stiv valoara parametrului n = 2, se rezerv spaiu de
memorie pentru i (pentru care nu se atribuie o valoare anume la nceput):
i
n=2
adr = 10
n=2
Se calculeaz i = 2 n = 4, valoare care se depune n locul rezervat pentru i la acest apel (n
cazul nostru, n vrful stivei):
i=4
n=2
adr = 10
n=2
Se face testarea n = 0; deoarece este condiia nu este ndeplinit, se intr pe ramura altfel.

30

Avem din nou apel: nainte de acesta se salveaz adresa de memorie a instruciunii de la care
se va continua execuia (incrementarea de la adresa 100):
adr = 100
i=4
n=2
adr = 10
n=2
Se intr n Recursiv(1); se salveaz pe stiv valoarea parametrului n = 1, se rezerv spaiu de
memorie pentru i (pentru care nu se atribuie o valoare anume la nceput):
i
n=1
adr = 100
i=4
n=2
adr = 10
n=2
Se calculeaz i = 2 n = 2, valoare care se depune n locul rezervat pentru i la acest apel (n
cazul nostru, n vrful stivei):
i=2
n=1
adr = 100
i=4
n=2
adr = 10
n=2
Se face testarea n = 0; deoarece este condiia nu este ndeplinit, se intr pe ramura altfel.
Avem din nou apel: nainte de acesta se salveaz adresa de memorie
a instruciunii de la care se va continua execuia (incrementarea de la adresa 100):
adr = 100
i=2
n=1
adr = 100
i=4
n=2
adr = 10
n=2
Se intr n apelul Recursiv(0); se salveaz pe stiv n = 0, se rezerv spaiu de memorie pentru
i (pentru care nu se atribuie o valoare anume la nceput):
i
n=0
adr = 100
i=2
n=1
adr = 100
i=4
n=2

31

adr = 10
n=2
Se calculeaz i = 2 n = 0, valoare care se depune n locul rezervat pentru i la acest apel (n
cazul nostru, n vrful stivei):
i=0
n=0
adr = 100
i=2
n=1
adr = 100
i=4
n=2
adr = 10
n=2
Se face testarea n = 0; deoarece este condiia ndeplinit, se intr pe ramura atunci, care
reprezint ntoarcere la metoda Recursiv(1). La ntoarcerea din Recursiv(0) n Recursiv(1), se
scoate de pe stiv informaia care a fost depus datorit apelului Recursiv(0), reveninduse la
forma de dinainte de efectuarea acestui apel:
i=2
n=1
adr = 100
i=4
n=2
adr = 10
n=2
Instruciunea de la care se va continua execuia este de la adresa 100, care a fost scoas de pe
stiv.
Suntem n Recursiv(1). Execuia se continu de la instruciunea cu adresa 100 (conform
punctului precedent), deci i ia valoarea 2+1 (valoareal
2 din sum fiind datorat informaiei i = 2 de pe stiv), care se afieaz.
Datorit instruciunii de retur finale din subalgoritmul Recursiv, avem o ntoarcere de la
Recursiv(1) la Recursiv(2). Se cur stiva de informaiile i = 2, n = 1, adr = 100 i se continu
execuia de la instruciunea cu adresa 100:
i=4
n=2
adr = 10
n=2
Suntem n Recursiv(2). Execuia se continu de la instruciunea cu adresa 100, deci i ia
valoarea 4+1 (valoarea 4 din sum fiind datorat informaiei i = 4 de pe stiv), care se afieaz.
Datorit instruciunii de retur finale din subalgoritmul Recursiv, avem o
ntoarcere de la Recursiv(2) la Exemplu. Se cur stiva de informaiile i = 4, n = 2, adr = 10 i
se continuarea execuiei se va face de la instruciunea cu adresa 10. Stiva va fi:
n=2
Se execut instruciunea cu adresa 10: Scrie(Sfarit).
Se termin programul (algoritmul Exemplu), iar stiva devine vid. n cazul unei implementri,
controlul ar fi preluat de ctre sistemul de operare.

32

Algoritmul a afiat valorile 3, 5. Evident, acest lucru se putea face mult mai simplu printro
iteraie; dar exemplul de fa a dorit s ilustreze funcionarea unui apel recursiv.
Datorit operaiilor de curare a stivei (care se efectueaz printro simpl incrementare
sau decrementare a valorii dintrun registru al microprocesorului), precum i datorit
informaiilor repetate care se salveaz pe stiv (n, i, adr), timpul i spaiul de memorie necesar
sunt mai mari dect pentru variantele iterative. Totui, n destul de multe cazuri implementrile
recursive sunt mult mai uor de urmrit i de neles dect cele iterative.
O observaie important: programatorul este singurul rspunztor de faptul c
programul lui se termin. Altfel spus, trebuie mare atenie la impunerea unei condiii de
terminare a recursivitii (n cazul nostru, testarea pentru n = 0). n caz contrar, un astfel de
algoritm implementat va epuiza spaiul disponibil pentru stiv i se va opri fr a rezolva
problema (apel ecursiv la infinit).
Dac revenim la convenia ca transferul parametrilor s se fac prin adres, atunci pe
stiv, n cadul fiecrui subalgoritm apelat, n loc de valoarea lui n sar fi depus adresa de
memorie rezervat n cadrul algoritmului (la noi: adresa bazei stivei). n acest fel, orice
modificare care sar face asupra parametrului n din subalgoritmul Recursiv s-ar face de fapt
(via aceast adres pus pe stiv) asupra valorii lui n din algoritmul Exemplu. Majoritatea
limbajelor (Pascal, C/C++, C#, PL/SQL) au mecanisme prin care se poate stabili dac pe stiv
se depune o valoare (copia valorii parametrului efectiv) sau o adres de memorie (adresa la
care este stocat parametrul efectiv).
Exemplu

Calcularea lui n!
Pentru un n natural, definim valoarea n! n mod recursiv astfel:
n! = 1, pentru n = 0
i n! = n (n 1) !, pentru n > 0
Algoritmul recursiv este urmtorul:
Start FactRec
citete( n )
scrie( Fact(n) )
Stop
unde subalgoritmul Fact are forma:
Fact(n)
dac n = 0 atunci
Fact = 1
altfel
Fact = n Fact(n 1)
sfrit dac
Return
Scriei algoritmul de mai sus nerecursiv

33

Complexitatea algoritmului este dat de ecuaia recursiv T(n) = T(n 1) + c, c fiind o


constant datorat apelului recursiv (operaiei cu stiva) i nmulirii, pentru care soluia este
T(n) = (n). Varianta iterativ este de aceeai complexitate teoretic, dar mai rapid n
practic.

Exemplu
irul lui Fibonacci
irul lui Fibonacci se definete astfel:
f(n) = 0,pentru n= 0
f(n) = 1, pentru n= 1
f(n) = f(n 1) + f(n 2), pentru n 2
Vom implementa recursiv acest algoritm:
Start FiboRec
citete( n )
scrie( Fibo(n) )
Stop
unde algoritmul recursiv Fibo este:
Fibo(n)
dac n = 0 sau n = 1 atunci
Fibo = n
altfel
Fibo = Fibo(n 1) + Fibo(n 2)
sfrit dac
Return

Complexitatea teoretic este dat de ecuaia recursiv


T(n) = T(n 1) + T(n 2) + c,
care duce la T(n) = (n), unde = 1+5* 2 (tietura de aur). Complexitatea
exponenial mare este datorat faptului c se calculeaz de mai multe ori
aceleai valori: de exemplu, n
f(n) = f(n 1) + f(n 2),
f(n 2) este apelat att pentru calculul lui f(n 1), ct i pentru cel de al
doilea membru al prii drepte a ecuaiei anterioare. Redundana de calcul
devine tot mai accentuat pentru valori mici ale argumentului.
Calculai.
Exemplu
S vedem o implementare iterativ:
Start FiboIter
citete( n )
f(0) = 0
f(1) = 1
pentru i = 2, n
f(i) = f(i 1) + f(i 2)
34

sfrit pentru
scrie( f(n) )
Stop
Aplicai algoritmul pentru irul n=5

Complexitatea algoritmului anterior este evident (n). Acest fapt se datoreaz


memorrii valorilor lui f i evitrii calculului repetat (ca n algoritmul precedent).

Rezumat
Scrierea recursiv a algoritmilor este deseori mai uoar dect cea
iterativ. n esen, orice apel de subalgoritm (recursiv sau nu)
determin depunerea unor informaii pe o stiv gestionat automat.
Codul rezultat este mai scurt i mai uor de depanat; ns o
implementare recursiv poate duce la o complexitate crescut, datorit
rezolvrii n mod repetat a acelorai probleme, deci este necesar de
fiecare dat o analiz pertinent a complexitii (un exemplu relevant
este dat n cele dou implementri pentru calculul irului lui Fibonacci
de mai sus).
M1.U5.3. Test de autoevaluare a cunotinelor
1. S se calculeze recursiv maximul unui ir.
2. S se calculeze recursiv suma elementelor dintrun ir.
3. S se calculeze puterea a na a unui numr real a, cu o complexitate
mai bun dect (n).
4. S se dea un contraexemplu pentru care algoritmul de la 3 nu d
numrul optim de nmuliri.
5. S se scrie un algoritm care calculeaz termenul fibo(n) al irului Fibonacci
n timp mai bun de (n) (a se veda i problema 3).
6. S se dea algoritmi recursivi pentru parcurgerea n preordine, postordine,
inordine a unui arbore binar.

35

Modulul 2. Metode de elaborare a algoritmilor.


Cuprins
Introducere ........................................................................................................................... 36
U1. Metoda Backtracking. .................................................................................................... 36
U2. Generarea submulimilor................................................................................................ 41
U3. Metoda Divide et Impera .............................................................................................. 53
U4. Sortare i statistici de ordine ......................................................................................... 61
U5. Metoda Greedy ............................................................................................................. 71
U6. Metoda programrii dinamice ....................................................................................... 81

Introducere.
n timp s-au concretizat metode generale pentru elaborarea algoritmilor. Modulul acesta
introduce cele mai folosite dintre aceste metode.

M2.U1. Metoda Backtracking.


M2.U1.1. Introducere
Acest capitol introduce una din cele mai generale modaliti de rezolvare a
problemelor: metoda Backtracking. Ea duce la scrierea unor algoritmi care evit
generarea tuturor combinaiilor posibile i apoi la validarea rezultatelor.
Este prezentat schema general de algoritm backtracking i probleme clasice
pentru a cror rezolvare se folosete aceast abordare.
M2.U1.2. Obiectivele unitii de nvare
La sfritul acestei uniti de nvare studenii vor fi capabili s:
urmreasc parcurgerea incomplet a unui arbore de generare de soluii
s construiasc algoritmi iterativi prin metoda Backtracking.

Durata medie de parcurgere a acestei uniti de nvare este de dou ore.

Metoda Backtracking se poate aplica algoritmilor care trebuie s construiasc o soluie de


forma x = (x(1), x(2), . . . , x(n)) unde pentru orice i {1, 2, . . . , n}, x(i) Ai, Ai fiind o
mulime dat ca informaie de intrare i unde, n plus, componentele lui x trebuie s mai

36

satisfac nite condiii interne.


Exemple
S considerm: A1 = {A, B, E}, A2 = {A, C}, A3 = {U, S, E, C}
Cerine:
1. S se construiasc cuvintele care au prima liter din A1, a doua liter din A2,
a treia liter din A3;% i care respect condiiile interne de la 2;
2. n plus, se cere respectarea urmtoarelor condiii interne:
(a) Nu sunt dou litere consecutive egale;
(b) Nu sunt formate numai din consoane sau numai din vocale.
Dac lum n considerare numai cerina de la 1, obinem soluiile admisibile
ale problemei, de exemplu (AAU), (ACS), (EAU), (BAU), (BCC), . . . .
Dar (AAU), (BCC) nu respect condiiile interne 2a, iar (EAU) nu respect
condiia 2b.
O metod de rezolvare a acestei probleme ar fi s construim mai nti toate
soluiile admisibile i apoi s verificm care dintre ele respect condiiile
interne. Acest mod de a aborda problema duce, n exemplul nostru, la 324 =
24 de soluii din care evident cele carencep cu AA, de exemplu, nu sunt bune.
Dac mulimile A1,A2, . . . , An au respectiv k1, k2, . . . , kn elemente, atunci
am avea n total k1 k2 . . . kn soluii admisibile. Pentru k1 = k2 = = kn = m
avem mn soluii admisibile, deci un algoritm de tip brute force care ia n
considerare toate posibilitile ar avea o complexitate exponenial.
Studiai aceeai problem cu mulimile: A1 = {A, C, E}, A2 = {A, C}, A3 = {A,
S, E, C}
Algoritmul de tip backtracking urmrete micorarea numrului de operaii, dar trebuie
s spunem c rmne de complexitate exponenial. Acest algoritm urmrete s genereze cte
o component x(k) Ak ct timp mai are o component netestat din Ak; dac nu mai are
componente netestate, se revine la alegerea elementului de pe poziia anterioar x(k 1), altfel
se merge la componenta x(k + 1). In felul acesta s-ar genera toat mulimea soluiilor
admisibile; dar dac dup ce am gsit o component x(k) testm dac bucata de vector (x(1),
x(2), . . . , x(k)) generat pn atunci satisface condiiile de continuare derivate din condiiile
interne, atunci vom putea s nu permitem generarea inutil a unor soluii care oricum nu ar fi
bune (n exemplul nostru (AA )).
Trebuie s mai subliniem c simplul fapt c bucata (x(1), x(2), . . . x(k)) din vectorul x
satisface condiiile de continuare nu nseamn c o s gsim, obligatoriu, o soluie a problemei
(exist, totui, o metod care gsete soluia pas cu pas, fr revenire, numit Greedy; ea va fi
prezentat mai trziu).
Schema algoritmului general va fi:
Backtracking(A, n)

37

k=1
ct timp k > 0
dac k = n + 1 atunci
scrie( (x(i)), i = 1, n ){s-a generat o soluie}
k=k1
altfel
dac mai exist valori netestate a Ak atunci
x(k) = a
dac (x(1), x(2), . . . , x(k)) satisface condiiile de continuare
atunci
k=k+1
sfrit dac
altfel
k=k1
sfrit dac
sfrit dac
sfrit ct timp
Return
Exemplu
Vom utiliza acest algoritm prin problema aezrii damelor pe tabla de ah
astfel nct oricare dou dame s nu se atace (2 dame se atac dac sunt pe
aceei linie, coloan, diagonal). Vom considera mulimile (Ai)i=1,n ca fiind
poziiile (de la 1 la n) pe care le poate ocupa o dam pe linia i. Evident c de
data aceasta toate mulimile Ai sunt egale. Deci o configuraie pe o tabl de n =
5 precum mai jos va fi simbolizat de vectorul soluie x = (1, 3, 5, 2, 4) i se
poate vedea c n acest caz condiiile interne sunt respectate (oricare dou
dame nu se bat).

Algoritmul pentru n > 4 este:


Dame(n)
pentru i = 1, n
x(i) = 0{iniial pe nici o linie nu avem poziionat nici o regin}
sfrit pentru
k=1
ct timp k > 0

38

dac k = n + 1 atunci
scrie( (x(i)), i = 1, n )
k=k1
altfel
x(k) = x(k) + 1
dac x(k) n atunci
dac DispunereCorecta(x, k)=adevarat
atunci
k=k+1
sfrit dac
altfel
x(k) = 0{ Nu avem nici o regin pe
aceast linie} k = k 1{ Ne ntoarcem la
regina de pe poziia anterioar} sfrit
dac{x(k) n}
sfrit dac{ k = n + 1}
sfrit ct timp
Return
DispunereCorecta(x, k) testeaz dac condiiile de continuare pentru
bucata x(1), . . . , x(k) suntndeplinite; returnaz adevrat dac nu avem regine
care se atac, fals n caz contrar.
DispunereCorecta(x, k)
corect = adevarat
{ presupunem c nu avem nici un atac ntre regina k i cele dispuse anterior}
pentru i = 1, k 1
dac x(k) = x(i) sau |x(k) x(i)| = k i atunci
{ sunt pe aceeai coloan sau pe aceeai diagonal}
corect = fals
sfrit dac
sfrit pentru
DispunereCorecta = corect
Return
Aplicai algoritmul de mai sus pentru n=6.

Rezumat
Multe probleme cer generarea uneia, mai multora sau tuturor soluiilor care
respect anumit cerine. O abordare de tipul genereaz toate combinaiile,
apoi reinele pe cele valide (bruteforce) este cea mai ineficient. Metoda
backtracking urmrete depistarea ct mai devreme cu putin a soluiilor care
sunt invalide (pe msur ce componentele lor se genereaz). Dei algoritmii
39

rezultai sunt de complexitate exponenial, multe soluii invalide sunt evitate.


M2.U1.3. Test de autoevaluare a cunotinelor
1. Scriei un algoritm pentru parcurgerea tablei de ah cu un cal (calul sare pe
diagonal unui dreptunghi cu laturile de un ptrat i dou patrate). Fiecare
ptrat al tablei trebui s fie vizitat exact o singur dat.
2. Avnd 4 culori i o hart cu n ri (dat print-o matrice de adiacen: aij = 1
dac ara i este vecin cu ara j, 0 altfel), s se coloreze harta astfel ca dou ri
vecine s nu aibe aceeai culoare (dou ri se consider a fi vecine dac au o
frontier comun).
3. O organizaie are n componena sa n brbai i m femei. S se scrie un
algoritm care listeaz toate modalitile n care se poate alctui o delegaie care
s conin cel puin k femei, k <m.
4. Cum poate fi pltit o sum de x lei, n bancnote de valoare v(i), i = 1, n din
care avem cte b(i) buci? S se dea toate soluiile posibile.

40

M2.U2. Generarea submulimilor


M2.U2.1. Introducere
De multe ori soluiile care se cer pentru rezolvarea unei (sub)probleme sunt de o
form particular: produse carteziene, familia submulimilor, a combinaiilor,
aranjamentelor, permutrilor. Acest capitol conine rezolvri clasice pentru aceste
probleme, n mai multe variante
M2.U2.2. Obiectivele unitii de nvare
La sfritul acestei uniti de nvare studenii vor fi capabili s:
detecteze o problem de generare
formuleze modelul informatic al problemei
scrie algoritmul recursiv sau iterativ de rezolvare al problemei

Durata medie de parcurgere a acestei uniti de nvare este de trei ore.

Schema general de lucru


O aplicaie imediat a metodei Backtracking este generarea submulimilor.
Vom considera sumbulimi ale mulimii A = {a(1), a(2), . . . , a(n)} cu n elemente.
Evident c avem bijecie ntre mulimea indicilor {1, 2, . . . , n} i elementele mulimii A.
Exemple
{1, 3, 4, 5} {a(1), a(3), a(4), a(5)} Ca atare, vom presupune pentru
simplificarea algoritmilor c a(i) = i, i {1, . . . , n}.
Avem la dispoziie urmtoarele modaliti de reprezentare a unei submulimi X
cu i elemente ale lui A:
1. Printrun vector x cu i componente care conine elementele submulimii,
preciznduse valoarea lui i sau completnd componentele lui x cu o valoare
neutilizat (de exemplu 0);
2. Analog cu metoda 1, dar plasnd elemetele submulimii la sfritul
vectorului x;
3. Prin vectorul caracteristic al submulimii: un vector c cu n componente unde
c(i) = 1 arat c a(i) aparine submulimii, iar c(i) = 0 arat c a(i) nu aparine
submulimii.
Pentru A = {1, 2, 3, 4, 5, 6, 7}, submulimea X = {1, 3, 4, 5} poate fi
reprezentat astfel:
1. x = (1, 3, 4, 5, 0, 0, 0): elementele din submulimea X la nceput, restul pn
la n poziii fiind completate cu o valoare care nu se gsete n A, de exemplu 0
(care nu este indice valid);

41

2. x = (0, 0, 0, 1, 3, 4, 5): elementele din submulimea X la sfrit, primele


poziii fiind completate cu o valoare care nu se gsete n A;
3. c = (1, 0, 1, 1, 1, 0, 0): c este vectorul caracteristic: c(i) = 1, dac i X c(i)
= 0, dac i X
Toate generrile prezentate n continuare au un caracter iterativfiecare nou element
(i.e. submulime) al mulimii fiind gsit pe baza elementului anterior.
Algoritmul general pe care l vom prezenta n continuare folosete, pe lng un vector
de dimensiune n (n care apar succesiv elementele submulimii) i un indicator de generare.
Acest indicator de generare are iniial valoarea 0; apoi are valoarea 1 ct timp se mai poate
genera o submulime X i din nou valoarea 0 cnd s-a terminat generarea.
Generare(x, n, ig)
dac ig = 0 atunci
x = valoarea (submulimea) iniial
ig = 1
Return{iese n (sub)algoritmul apelant}
sfrit dac
dac exist un succesor al lui x atunci
x =succesorul lui x{ig rmne 1}
altfel
ig = 0{nu s-a mai putut genera submulimea X}
sfrit dac
Return
Un mod de utilizare a acestui algoritm pentru generri este urmtorul:
ig = 0
repet
cheam Generare(x, n, ig)
dac ig = 0 atunci
exit{Iei din ciclare}
altfel
cheam Prelucrare(x, n){prelucreaz elementul proaspt obinut al submulimii}
sfrit dac
pn cnd fals
n majoritatea algoritmilor pe care i vom prezenta, vectorii vor fi generai n ordine
lexicografic, ceea ce corespunde strategiei generale Backtracking.

42

S ne reamintim...
Mulimile sunt generate element cu element, dup generare acesta se
prelucreaz. Folosim un indicator de generare are iniial valoarea 0; apoi
are valoarea 1 ct timp se mai poate genera o submulime X i din nou
valoarea 0 cnd s-a terminat generarea.
Generarea elementelor unui produs cartezian
Fiind date mulimile A1,A2, . . . Am, unde Ai = {1, 2, . . . , n(i)}, dorim s generm
elementele produsului cartezian A1 A2 Am. Vom avea n total

n(i) elemente.

i 1

n algoritmul urmtor elementele produsului cartezian vor fi generate succesiv n


vectorul x cu m elemente, unde o configuraie x = (x(1), x(2), . . . , x(m)) va desemna elementul
(A1(x(1)),A2(x(2)), . . . , Am(x(m))) al produsului cartezian.
Vom porni deci cu un element iniial x = (1, 1, . . . , 1) care nseamn c din fiecare
mulime Ai lum primul element. n continuare vom determina cel mai mare indice i cu
proprietatea c x(i) < n(i) i vom alege ca succesor al lui x = (x(1), x(2), . . . , x(m)) elementul x
= (x(1), x(2), . . . , x(i1), x(i)+1, 1, . . . , 1).
Dac pentru fiecare i {1, . . . , m} avem x(i) = n(i) nseamn c aceasta a fost ultima
component a produsului cartezian i generarea s-a terminat.
Dou iruri x = (x(1), . . . , x(n)) i y = (y(1), . . . , y(n)) sunt n ordine lexicografic strict
cresctoare dac exist k 1, k < n astfel nct x(i) = y(i) pentru orice i k i x(k+1) < y(k+1).
Exemplu: (1, 1, 2) < (1, 2, 1) n sens lexicografic. Pentru cazul n care x i y au forma general
x = (x(1), . . . , x(m)) i y = (y(1), . . . , y(n)), dac exist k m, n astfel nct (x(1), . . . , x(k)) <
(y(1), . . . , y(k)) atunci x este mai mic (lexicografic) dect y. Dac un astfel de k nu exist
(adic x(1) = y(1), . . . , x(p) = y(p) unde p este min(m, n)), atunci cel mai scurt dintre vectori
este considerat cel mai mic. Exemplu: (1, 2) < (1, 3, 4), (1, 2) < (1, 2, 3).
Algoritmul pentru generarea produsului cartezian este:
ProdusCartezian(x, m, n, ig)
dac ig = 0 atunci
pentru i = 1,m,1
x(i) = 1
sfrit pentru
ig = 1
Return
sfrit dac
pentru i = m, 1,1
dac x(i) < n(i) atunci
x(i) = x(i) + 1
Return
altfel
x(i) = 1

43

sfrit dac
sfrit pentru
ig = 0
Return
Generarea tuturor submulimilor unei mulimi
O prim metod ar fi s generm elementele produsului cartezian al mulimilor A1=A2=
=An ={0, 1}. Vectorul x care se obine, interpretat ca vector caracteristic, va fi element al
mulimii prilor lui {1, 2, . . . , n}
O a doua metod ve genera submulimile unei mulimin ordinea cresctoare a
numrului de elemente utliznd metoda (2) de reprezentare a mulimilor. Specificm c
vectorii x vor fi generai i de aceast dat n ordine lexicografic strict cresctoare.
Fie x = (x(1), x(2), . . . , x(n)); atunci succesorul lui se va determina n modul urmtor:
se va determina indicele i cu proprietatea c x(i) < i i pentru n j > i avem x(j) = j. Este
evident c urmtorul element x n ordine lexicografic este cel n care x_(i) = x(i) + 1 iar
celelalte componente cresc cu cte 1, adic:
x(i + 1) = x(i) + 2, . . . , x(n) = x(i) + n i + 1.
Se ncepe cu x = (0, 0, . . . , 0) care reprezint mulimea vid, iar atunci cnd x = (1, 2, .
. . , n) nseamn c am generat ultimul element.
Algoritmul pentru generarea submulimilor unei mulimi este:
Submultimi(x, m, ig)
dac ig = 0 atunci
pentru i = 1, n
x(i) = 0
sfrit pentru
ig = 1
Return
sfrit dac
pentru i = n, 1,1
dac x(i) < i atunci
x(i) = x(i) + 1
pentru j = i + 1, n
x(j) = x(j 1) + 1
sfrit pentru
Return
sfrit dac{x(i) < i}
sfrit pentru
ig = 0
Return
Generarea mulimii combinrilor

44

Fie A = {1, 2, . . . n} i m n. Vom construi n continuare algoritmi de generare a


tuturor celor C nm combinri din mulimea A cu proprietatea c oricare dou elemente difer
ntre ele prin natura lor, nu i prin ordine.
O prim metod se bazeaz pe metoda de reprezentare 1. i de aceast dat vectorii vor
fi generai n ordine lexicograficncepnd cu x = (1, 2, . . . , m).
Fiind dat x = (x(1), x(2), . . . , x(m)), urmtorul element x va fi determinat astfel:
1. se determin indicele i cu proprietatea: x(i) < n m + i, x(i + 1) = n m + i + 1, , x(m) = n
2. se trece la x prin:
- x(j) = x(j) dac j < i
- x(i) = x(i) + 1
- x(j) = x(j 1) + 1 pentru j de la i + 1 la m
3. cnd nu gsim i cu proprietatea de mai sus nseamn c x = (nm+1, n m + 2, . . . , n) i
deci s-a generat ultimul element.
Algoritmul va fi urmtorul:
Combin1(x, n, m, ig)
dac ig = 0 atunci
pentru i = 1,m
x(i) = i
sfrit pentru
ig = 1
Return
sfrit dac
pentru i = m, 1,1
dac x(i) < n m + i atunci
x(i) = x(i) + 1
pentru j = i + 1,m
x(j) = x(j 1) + 1
sfrit pentru
Return
sfrit dac
sfrit pentru
ig = 0
Return
A doua metod folosete reprezentarea elementelor prin vectorul caracteristic, generat
n ordine lexicografic cresctoare. Primul element va fi: x = (0, 0, . . . , 0, 1, 1, . . . , 1) vectorul
cu ultimele m poziii egale cu 1 i 0 n rest.
Fie acum vectorul x cruia vrem s i determinm succesorul x; pentru aceasta vom pune n
x ..., 0, 1, ..., 1, 0, ..., 0
eviden ultima secven de cifre 1:
unde a este indicele
a
b
unde ncepe aceast secven iar b indicele ultimului element egal cu 1.

45

Trecerea la x depinde de faptul c ultima secven conine un singur element (a = b) sau mai
multe elemente (a < b).
Dac a = b atunci x(a 1) = 1, x(a) = 0 i n rest x(i) = x(i) va determina configuraia
urmtoare.
Dac a < b atunci rezult c x(a 1) = 0. S notm cu l1 lungimea secvenei de elemente 1,
deci l1 = b a + 1 i cu l0 lungimea secvenei de zerouri de la sfritul lui x, deci l0 = n b.
Rezult c l1 2 i l0 0.
Trecerea de la x la x se poate vedea uor n urmtoarea schem:

l1
x

..., 0,

l0

1, ..., 1, 0, ..., 0
a
b
l1 1

..., 1,

0, ...,
1,
0, ..., 0
a
n l1 2
Deci x(a 1) va deveni 1, urmeaz apoi un ir de l0 + 1 zerouri, urmat, n final de un ir de
l11 elemente egale cu 1. Algoritmul pentru generarea combinrilor este urmtorul:
Combinari2(x, n, m, ig, a, b)
dac ig = 0 atunci
pentru i = 1, n m
x(i) = 0
sfrit pentru
pentru i = n m + 1, n
x(i) = 1
sfrit pentru
ig = 1
a=nm+1
b=n
Return
sfrit dac
dac a = 1 atunci
ig = 0
Return
sfrit dac
dac a < b atunci
l1 = b a + 1
x(a 1) = 1
pentru i = a, n l1 + 1
x(i) = 0
sfrit pentru
pentru i = n l1 + 2, n
46

x(i) = 1
sfrit pentru
a = n l1 + 2
b=n
Return
altfel
x(a 1) = 1
x(a) = 0
b=a1
pentru i = a 1, 1,1
dac x(i) = 0 atunci
a=i+1
Return
sfrit dac
a=1
sfrit pentru
sfrit dac{a < b}
Return
Generarea mulimii permutrilor
Complexitatea oricrui algoritm care rezolv aceast problem este de (n!), deoarece
trebuie generai n! vectori. Vectorul p cu n elemente va conine permutarea mulimii A = {1, 2,
. . . , n}. Exist muli algoritmi pentru rezolvarea acestei probleme, dar noi prezentm aici
numai doi dintre ei.
Prima metod este: urmrete generarea vectorului x n ordine lexicografic
cresctoare. Pentru construcia lui p succesorul lui p vom pune n eviden ultima secven
descresctoare: p(i) < p(i + 1) > p(i + 2) > > p(n) i n consecin componenta care trebuie
modificat este p(i). n locul lui p(i) trebuie pus o component p(j) cu j > i, cu condiia ca p(j)
> p(i), dar pentru c p este succesorul lui p, trebuie ales j astfel nct p(j) s fie cel mai mic cu
proprietatea p(j) > p(i); fie acest indice k; atunci prin interschimbarea lui p(i) cu p(k) se ajunge
la: (p(1), . . . , p(i 1), p(k), p(i + 1), . . . , p(k 1), p(i), p(k + 1), . . . , p(n)) unde p(i+1) >
p(i+2) > > p(k1) . Dac p(i) nu ar fi mai mare dect p(k+1), atunci am avea (datorit
inegalitii p(k) > p(k+1)) c p(k) nu ar fi cel mai mic din secvena p(i+1), . . . , p(n) care este
mai mare dect p(i), contradicie cu alegerea lui k; deci avem c p(i) > p(k + 1), deci p(i) > p(k
+ 1) > > p(n). Uor se demonstreaz c p(k 1) > p(i).
Rezult c secvena de la p(i+1) la p(n) este descresctoare i pentru a obine p nu
avem dect s inversm aceast secven, interschimbnd termenii de la nceput cu cei de la
sfrit pn la mijlocul secvenei. Algoritmul este:
Perm1(p, n, ig)
dac ig = 0 atunci
pentru i = 1, n

47

p(i) = i
sfrit pentru
ig = 1
Return
sfrit dac
i=n1
ct timp p(i) > p(i + 1)
i=i1
dac i = 0 atunci
ig = 0
Return
sfrit dac
sfrit ct timp
k = n{ cutarea lui k}
ct timp p(i) > p(k)
k=k1
sfrit ct timp
{schimb p(i) cu p(k)}
p(i) p(k)3
{calculeaz mijlocul secvenei}
n i
m
2
{[x] este partea ntreag a lui x}
pentru j = 1,m
p(i + j) p(n + 1 j)
sfrit pentru
Return
Metoda pe care o prezentm n continuare se bazeaz pe generarea recursiv a
permutrilor. Dac avemSk mulimea permutrilor de k elemente de forma p = (p(1), . . . , p(k))
Sk, atunci Sk+1 va conine pe baza fiecrei permutri p cte k+1 permutri p: (p(1), . . . ,
p(k), k+1), (p(1), . . . , p(k1), k+1, p(k)),. . . , (p(1), k + 1, p(2), . . . , p(k)), (k + 1, p(1), . . . ,
p(k)).
Deci permutrile Sn vor fi frunzele unui arbore construit astfel:
- rdcina conine 1;
- descendenii unei permutri p = (p(1), . . . , p(k)) sunt cele k + 1 permutri descrise mai sus.
Putem ataa acestui arbore unul n care toate permutrile sunt de n elemente, prin completarea
permutrii de k elemente cu componentele (k +1, k + 2, . . . , n).
Notaia a b nseamn interschimb valoarea lui a cu valoarea lui b, care se efectueaz prin
urmtoarea secven: aux = a, a = b, b = aux.

48

Exemplu: pentru n = 3 se obine arborele din Figura 7.1 i arborele ataat din
figur:

Arborele ataat unei permutri de trei elemente

Arborele ataat are, evident, aceleai vrfuri terminale. Trecerea de la o permutare p la


urmtoarea permutare p nseamn o parcurgere a frunzelor acestui arbore.
Fie p = (p(1), . . . , p(n)); atunci p_ se construiete astfel: Dac n apare pe o poziie m > 1,
atunci se face interschimbarea p(m) p(m 1) i s-a obinut noua permutare. Dac p(1) = n,
atunci se permut circular spre stnga cele n elemente, obinnduse tatl permutrii p. n
continuare, dac n 1 apare pe o poziie m = 1, atunci se efectueaz interschimbarea p(m)
p(m 1) i sa obinut noua permutare; n caz contrar, se permut circular ultimele n1 poziii
i se ncearc deplasarea spre stnga a lui n 2, etc. Algoritmul este:
Perm2(p, n, ig)
dac ig = 0 atunci
pentru i = 1, n
p(i) = i
sfrit pentru
ig = 1
Return
sfrit dac
pentru i = n, 2,1
m=1
ct timp p(m) i
m=m+1
sfrit ct timp
49

dac m _= 1 atunci
p(m 1) p(m)
Return
sfrit dac
cheam PermCircStanga(p, n, i)
{se permut circular primele i componente ale lui P}
Return
sfrit pentru
ig = 0
Return
Unde PermCircStanga(p, n, i) este urmtorul algoritm:
PermCirc(p, n, i)
x = p(1)
pentru j = 1, i 1
p(j) = p(j + 1)
sfrit pentru
p(i) = x
Return
Generarea mulimii aranjamentelor
O prim metod const n generarea tuturor combinrilor de n luate cte m i pentru
fiecare din acestea realizarea tuturor permutrilor. Aceast idee duce la urmtorul algoritm:
Aranj1(x,n,m, ig, c, p, ig1)
dac ig = 0 atunci
pentru i = 1,m
x(i) = i
c(i) = i
p(i) = i
sfrit pentru
ig = 1
ig1 = 1
Return
sfrit dac
cheam Perm(p, m, ig1)
dac ig1 0 atunci
pentru i = 1,m
x(i) = c(p(i))
sfrit pentru
altfel
cheam Combin(c, m, ig)
dac ig = 1 atunci

50

pentru i = 1,m
p(i) = i
x(i) = c(i)
sfrit pentru
ig1 = 1
Return
sfrit dac
sfrit dac
Return
S observm c metoda nu genereaz aranjamentelen ordine lexicografic cresctoare.
Urmtoarea metod urmrete s pstreze aceast ordine, conform strategiei generale
Backtracking.
Fie x = (x(1), . . . , x(m)) un aranjament oarecare. Pentru determinarea lui x,
aranjamentul succesor, n ordine lexicografic, vom determina mai nti indicele i, cel mai
mare indice care poate fi mrit. Un x(i) nu poate fi mrit dac valorile x(i)+1, x(i)+2, . . . , n nu
sunt disponibile. Vom folosi un vector disp cu n componente care sunt 0 sau 1. disp(k) = 0
arat c valoarea k este disponibil iar disp(k) = 1 arat c valoarea x(k) nu este disponibil
(este deja folosit n x). Cnd se gsete valoarea x cu proprietatea menionat, x(i), x(i+1), ...,
x(m) vor primi cele mai mici valori disponibile, n ordine cresctoare. Rezult de aici c pn
la gsirea lui i toate valorile cercetate regresiv vor fi fcute disponibile. Obinem n acest fel
urmtorul algoritm:
Aranj2(x, n, m, ig, disp)
dac ig = 0 atunci
pentru i = 1,m
x(i) = i
disp(i) = 1
sfrit pentru
pentru i = m + 1, n
disp(i) = 0
sfrit pentru
ig = 1
Return
sfrit dac
pentru i = m, 1,1
disp(x(i)) = 0
pentru j = x(i) + 1, n
dac disp(j) = 0 atunci
x(i) = j
disp(j) = 1
k=0

51

pentru l = i + 1,m
repet
k=k+1
pn cnd disp(k) = 0
x(l) = k;
disp(k) = 1
sfrit pentru
Return
sfrit dac
sfrit pentru
sfrit pentru
ig = 0
Return
Rezumat
Pentru determinarea unor submulimi (de o natur particular) a unei mulimi
exist algoritmi specializai. Se pot da, evident, mai muli algoritmi pentru
generarea unei soluii. Aceste tipuri de probleme se ntlnesc adeseori sub o
form mascat n enunul altor probleme, sau ca etap intermediar ntrun caz
mai mare.
M2.U2.3. Test de autoevaluare a cunotinelor
1. S se scrie algoritmi recursivi pentru generarea tipurilor de submulimi din
acest capitol.

52

M2.U3. Metoda Divide et Impera


M2.U3.1. Introducere
Capitolul prezint metoda Divide et Impera, ce se poate utiliza atunci cnd
rezolvarea unei probleme se face prin spargerea ei n cazuri de dimensiune mai
mic (dar de aceeai natur cu problema iniial), iar apoi prin recombinarea
soluiilor obinute. Determinarea complexitii se reduce la rezolvarea unei
recurene, pentru care o unealt util este Teorema central. Este dat cel mai
eficient algoritm de sortare (Quicksort) i se demonstreaz o limit inferioar
pentru algoritmiide sortate bazai pe comparaii.

M2.U3.2. Obiectivele unitii de nvare


La sfritul acestei uniti de nvare studenii vor fi capabili s:
s folosii metoda Divide et Impera
s calculai complexitatea algoritmilor elaborai prin aceast metod

Durata medie de parcurgere a acestei uniti de nvare este de trei ore.

Aceast metod nu este ceva nou pentru cursul nostru. Am rezolvat cu ea problema sortrii
prin interclasare. Metoda const n urmtoarele etape:
1. Partiionarea problemei iniiale n mai multe probleme mai mici;
2. Rezolvarea (de obicei recursiv) a fiecrei subprobleme; dac o subproblem este de
dimensiune suficient de mic, atunci ea se poate rezolva printro metod ad-hoc, care poate
fi mai puin performant pentru cazuri de dimensiune mare;
3. Combinarea rezultatelor pariale pentru a obine rezultatul problemei iniiale. Dac o
problem a fost divizat n a subprobleme care au dimensiunea n/b i sunt rezolvate recursiv,
n
f n unde f(n) este costul
atunci complexitatea este dat de formula: T (n) aT
b
combinrii soluiilor subproblemelor.
Aflarea formei lui T(n) este o problem de algebr care se poate aborda prin inducie sau
folosind rezultatele teoremei centrale din [2]:

53

Teorema 1. (Teorema central.) Fie a 1 i b > 1 constante, fie f(n) o funcie i T(n) definit
n
n
f n unde interpretm pe
pe ntregii nenegativi prin recurena: T (n) aT
fie ca pe
b
b
n
n
, fie ac pe
. atunci T (n) poate fi delimitat asimptotic dup cum urmeaz:
b
b

1. Dac f (n)

O n logb a

2. Dac f (n)

O n logb a atunci T (n)

3. Dac f (n)

n log b a

pentru o anumit constant

n logb a

0 , atunci T (n)

n log b a log n

pentru o anumit constant

0 i dac af

n
b

cf n pentru o

f ( n)
anumit constant c 1 i toi n suficieni de mari atunci T (n)
Am folosit divide et impera la sortarea unui ir (sortarea prin interclasare, algoritmul
Mergesort); se vede c se ajunge la complexitatea: T(n) = (n log2 n) (care se deduce din cazul
2 al Teoremei centrale), complexitate mai bun dect cea de la sortarea prin inserie, care era
T(n) = (n2)
Metoda const n urmtoarele etape:
1. Partiionarea problemei iniiale n mai multe probleme mai mici;
2. Rezolvarea (de obicei recursiv) a fiecrei subprobleme; dac o subproblem este de
dimensiune suficient de mic, atunci ea se poate rezolva printro metod ad-hoc, care poate
fi mai puin performant pentru cazuri de dimensiune mare;
3. Combinarea rezultatelor pariale pentru a obine rezultatul problemei iniiale.
Am folosit divide et impera la sortarea unui ir (sortarea prin interclasare, algoritmul
Mergesort); se vede c se ajunge la complexitatea T(n) = (n log2 n) (care se deduce din cazul
2 al Teoremei centrale), complexitate mai bun dect cea de la sortarea prin inserie, care era
T(n) = (n2) unde f(n) este costul combinrii soluiilor subproblemelor.
Am folosit divide et impera la sortarea unui ir (sortarea prin interclasare, algoritmul
Mergesort); se vede c se ajunge la complexitatea T(n) = (n log2 n)(care se deduce din cazul
2 al Teoremei centrale), complexitate mai bun dect cea de la sortarea prin inserie, care era
T(n) = (n2)
Problema turnurilor din Hanoi.
Prima problem pe care o prezentm este Problema turnurilor din Hanoi, problem
care nu are nici o legtur cu Hanoi-ul, ci este un joc logic inventat n secolul trecut de
matematicianul francez Eduard Lucas. El a fost cel care a vndut pentru prima oar aceast
problem drept joc, cu opt discuri i trei baghete.
Dup o strveche legend hindus, zeul Brahma ar fi fixat pe masa templului su din
Benares trei bastonae verticale de diamant i ar fi nirat pe acesta 64 de discuri de mrimi
descresctoare (de la baz la vrf), formnd astfel un trunchi de con. A dispus apoi ca discurile
s fie mutate pe un al doilea, folosind i al treilea bastona; zi i noapte trebuia mutat cte unul
din discurile aflate n vrful unuia din bastonae n vrful altui bastona, respectnd n fiecare
moment ordinea descresctoare a mrimii discurilor aflate pe fiecare batona. Legenda spune

54

c, dnd aceast porunc, Brahma a susinut c atunci cnd se va termina aezarea celor 64 de
dicuri pe cel de-al doilea batona, atunci se va sfri i viaa pe Pmnt. O versiune destul de
optimist deoarece presupunnd c mutarea unui disc ar dura o secund, cele 64 de discuri vor
ajunge pe al doilea bastona n circa 584.942.416.729 de ani.
Enunul formalizat al problemei este: fie trei tije i n discuri perforate de diametre
diferite. Discurile sunt aezate iniial pe tija 1 n ordinea descresc toare a diametrelor acestora
considernd sensul de la baz la vrf. Problema const n a muta turnul de n discuri de pe tija
1 pe tija 2 ntr-un numr minim de mutri, innd cont de urmtoarele reguli:
1. La fiecare mutare se mut un singur disc
2. n permane, pe fiecare tij, deasupra unui disc pot fi mutate numai discuri de diametre mai
mici.
Calcularea numrului minim de pai: observm c pentru 2, 3, 4 i 5 discuri, problema
se rezolv efectund un numr de mutri egal cu 3, 7, 15 i respectiv 31, de unde putem
formula ipoteza c pentru problema cu n discuri vom efectua 2n 1 mutri.
S demonstrm valabilitatea acestui rezultat prin inducie matematic complet dup n.
Amartat c afirmaia este adevrat pentru n = 2, 3, 4, 5 discuri. Presupunem c ea este
adevrat pentru n discuri, deci sunt necesare 2n1 mutri. Vom demonstra c pentru n+1
discuri sunt necesare 2n+11 mutri.
Putem rezolva problema mutrii celor n + 1 discuri n trei etape:
1. Mutm primele n discuri pe tija a treia
2. Mutm al n + 1-lea disc pe a doua tij
3. Mutm cele n discuri de pe a treia tij pe cea de-a dou tij.
Folosind acest procedeu, sunt necesare:
1. 2n 1 mutri pentru realizarea primei etape
2. o mutare pentru realizarea celei de a doua etape
3. 2n 1 mutri pentru realizarea celei de a treia etape
n total sunt suficiente 2n+11 mutri.
Metoda 1
O mutare poate fi scris ca o pereche (i, j) cu i {1, 2, 3} i i j, semnificnd c se
mut discul cel mai mic de pe tija i pe tija j. Mai notm cu H(m, i, j) irul mutrilor necesare
pentru a muta primele m discuri (cele mai de sus) de pe tija i pe tija j. n aceste ipoteze,
problema se reduce la a determina irul de mutri H(m, i, j). Observm c putem scrie:
(i, j )
pentru m 1
, unde k = 6 i j este
H m, i, j
H m 1, i, k , (i, j ), H (m 1, k , j ) pentru m 1
tija diferit de tijele i i j.
Cu aceasta reducem rezolvarea problemei cu n discuri la rezolvarea a dou probleme cu
n1 discuri. Algoritmul se bazeaz pe ideea c n loc de H(m, i, j) putem scrie H(i, j),
memornd valoarea lui m separat. Se mai poate observa c H(i, j) se transform n H(i, k), (i, j),
H(k, j) cu k = 6 i j. Avnd n mijloc tot perechea (i, j), rezult c imediat ce o pereche este
generat, ea poate fi nscris direct pe locul ei. Algoritmul obine cele 2n 1 coloane, fiecare

55

coloan memornd o mutare. ncepem prin a nscrie perechea (1,2) pe coloana 2n1. Perechile
ce vor lua natere din ea vor fi nscrise pe coloanele 2n2 i 2n1 + 2n2. Se observ c acest
algoritm face parte din clasa algoritmilor Divide et Impera.
Mai general, pentru fiecare valoare a lui m {n, n 1, . . . , 2} se expandeaz perechile din
coloanele c de forma c =(2m)+2m1, perechile care rezult din cea din coloana c fiind nscrise
pe coloanele c 2m2, c + 2m2. ( p este notaie pentru multiplu de p)
Hanoi1(M, n,N){N = 2n 1}
k1 = N + 1
k2 = k1/2
k3 = k2/2
M(1, k2) = 1
M(2, k2) = 2
pentru m = n, 2,1
pentru l = k2,N, k1
i = M(1, l)
j = M(2, l)
k = 6 i j
M(1, l k3) = i
M(2, l k3) = k
M(1, l + k3) = k
M(2, l + k3) = j
sfrit pentru
k1 = k2
k2 = k3
k3 = k3/2
sfrit pentru
return
Algoritmul de sortare QuickSort.
Fie irul x = (x(1), x(2), . . . , x(n)). Dorim s sortm acest ir, adic i {1, ... , n1},
x(i) x(i+1). Dup cum tim, algoritmii sunt caracterizai de timpul de execuie i spaiul de
memorie ocupat. Sortarea unui ir cu metoda bulelor sau cea a inseriei s-a fcut cu algoritmi
in place (n afara irului sau meninut cel mult un numr constant de elemente din ir), iar
prin metoda interclasrii se ocup un alt ir (nu este un algoritm in place) i n plus timpul de
execuie ascunde o constant multiplicativ mare.
Ne dorim s realizm un algoritm de sortare prin metoda Divide et Impera, fr a
folosi un ir intermediar, deci in place. Profesorul C.A.R. Hoare de la Universitatea Oxford a
propus mpirea irului n pri nu neaprat egale avnd proprietatea c toate elemetele primei
pri s fie mai mici sau egale dect un x(k) fixat (determinat de algoritmul de partitionare care
va fi prezentat mai jos), iar componentele celei de a doua pri s fie mai mari dect x(k). Faza
de combinare lipsete (este o metod Divide et Impera chioap).

56

Algoritmul QuickSort (sortare rapid) este:


QuickSort(x, p, r)
dac p < r atunci
cheam Part(x, p, r, k)
cheam QuickSort(x, p, k)
cheam QuickSort(x, k + 1, r)
sfrit dac
Return
Algoritmul care realizeaz partiionarea este:
Part(x, p, r, k)
pivot = x(p)
i=p1
j=r+1
terminat = fals
repet
repet
j=j1
pn cnd x(j) pivot
repet
i=i+1
pn cnd x(i) pivot
dac i < j atunci
x(i) x(j)
altfel
k=j
terminat = adevarat
sfrit dac
pn cnd terminat = adevarat
Return
Exemplu
Fie irul

Avem pivot=6, p=1, r=8. Primul element al irului mai mic sau egal dect
pivotul este 2 (n cutarea de la dreapta la stnga) i primul element al irului
mai mare sau egal dect pivotul este 6 (n cutarea de la stnga la dreapta).
Dup gsirea lor le interschimbm, ajungnd la configuraia:

57

Apoi succesiv:

Deoarece j a devenit mai mic dect i, se iese din procedura de partiionare cu


valoarea lui k=5. n acest moment s-a terminat partiionarea cu pivot=6. n
continuare se face partiionarea irului din partea stng pentru pivot=2:

Aplicai algoritmul de mai sus pentru irul 10,9,3,4,5,2,7.

Deoarece avem j i, procedura se termin valoarea lui k = 2. Analog se procedeaz pentru


fiecare subcaz, pn cnd se ajunge la vectori de lungime 1, care sunt (evident) sortai.
Observaie: Cnd irul este deja ordonat cresctor, se face partiionare dup fiecare element.
Complexitatea partiionrii este dat de TP (n) = c n, c constant. Complexitatea QuickSort n
cazul irului iniial sortat este:

58

TQ (n)

TQ (1) TQ (n 1) TP (n)

TQ (n 1) TP (n) c

TQ (1) TQ (n 2) TP (n 1) TP (n)

TQ (n 2) TP (n 1) TP (n) 2c

TP (k ) (n 2)c

TP (k ) cn

k 2

... TQ (2)

k 2

ck

cn

TP (k ) (n 2)c

k 3

TQ (1) TQ (1) TQ (n)

(n 2 )

k 2

unde am considerat n mod convenabil c TQ(1) este mrginit de aceeai constant c care apare
n expresia lui TP (n). n cel mai bun caz, cnd irul se mparte n jumtate,
TQ(n)=2TQ(n/2)+(n) = (n log n) (conform teoremei centrale). Se poate demonstra c timpul
mediu al QuickSortului este (n log n). n practic se dovedete c QuickSort este cel mai
rapid algoritm de sortare.
Ne punem ntrebarea dac exist algoritmi de sortare cu complexitatea mai mic dect n
log n? Se poate demonstra c dac algoritmul se bazeaz doar pe comparaiintre elemente, nu
se poate cobor sub bariera de (n log n). Pentru a demonstra, alegem a1, a2, a3 elemente pe
care le comparm (Figura urmtoare ). Se obine un arbore binar care are drept frunze
permutrile indicilor elementelor iniiale, adic 3! frunze. n cazul a n valori, obinem un
arbore binar cu n! frunze. Prin nimea unui arbore nelegem lungimea drumului maxim de la
rdcin la orice frunz i se noteaz cu h. h reprezint numrul de comparaii n cazul cel mai
defavorabil. Este evident c numrul maxim de frunze pentru un arbore binar de adncime h
este 2h. Deci n cazul nostru pentru compararea a n elemente vom avea un arbore cu o
adncime h astfel nct n! 2h. Rezult c h log2(n!) i folosind aproximaia lui
n

n 2
(n log 2 n)
Stirling n!
obinem c h log 2 n! n log 2 n n log 2 e
2 n
e
Se obine c T(n) = (n log2 n). Am demonstrat deci c pentru algoritmii care se
bazeaz doar pe comparaii complexitatea nu poate s scad sub n log2 n.
n figura de mai jos se poate vedea arborele de decizie pentru trei elemente

59

Rezumat
Metoda Divide et Impera se folosete atunci cnd rezolvarea unei probleme se
poate face prin descompunerea ei n cazuri de dimensiune mai mic, rezolvarea
acestora dupa acelai principiu, apoi combinarea rezultatelor lor. Deseori
algoritmii de acest tip se scriu recursiv (datorit naturii lor), iar determinarea
complexitii se face prin rezolvarea unei ecuaii recursive, a crei soluii se
poate da de multe ori pe baza Teoremei centrale. Un exemplu important de
algoritm de tip Divide et Impera este Quicksort.
M2.U3.3. Test de autoevaluare a cunotinelor
1. Baznduv pe observaia c mutarea din mijloc se tie, scriei un algoritm
iterativ pentru problema turnurilor din Hanoi, punnd mereu mijloacele
intervalelor obinute prinnjumtire ntr-un ir cu numrul cunoscut de
mutri.
2. Avnd la intrare un ir ordonat cresctor, s se scrie un algoritm de tip
Divide et Impera pentru gsirea poziiei unui element de valoare dat x, dac
el exist. Se va folosi metoda njumtirii intervalului dat de numrul de
elemente al irului. Care este complexitatea cutrii?
3. Rezolvai aceeai problem prin cutare secvenial (testarea pe rnd a
elementelor). Care este n acest caz complexitatea?
4. Demonstrai c timpul de execuie al algoritmului QuickSort, n cazul unui
vector cu toate elementele egale, este (n log n).
5. Complexitatea algoritmului QuickSort este mare din cauz c n cazuri
defavorabile, partiionarea se face cu un singur element ntr-una din partiii.
Rescriei algoritmul de partiionare, lund ca element de separare nu primul
element, ci valoarea din mijloc ntre primul element, ultimul i cel aflat pe
poziia din mijloc.

60

M2.U4. Sortare i statistici de ordine


M2.U4.1. Introducere
n acest capitol ne propunem s rezolvm urmtoarele probleme, relative la un ir
cu n elemente:
1. s se gseasc a i-a component n ordine statistic a irului
2. s se gseasc mediana (componenta din mijloc) unui ir
3. s se gseasc cele mai mici dou elemente dintrun ir
4. determinarea simultan a minimului i maximului
Observaie: Vom considera c irul este cu elemente distincten rezolvarea tuturor
problemelor enunate, urmnd apoi s extindem rezultatele obinute i pentru
situaia n care elementele se repet. Vor fi considerai mai muli algoritmi care
rezolv aceast problem.
Unitatea de nvare conine i prezentri ale altor algoritmi de sortare (heapsort,
cu care ocazie se introduce i structura de date numit heap), sortri n timp liniar.

M2.U4.2. Obiectivele unitii de nvare


La sfritul acestei uniti de nvare studenii vor fi capabili s:
foloseasc diveri algoritmi de sortare
scrie algoritmi pentru probleme de statistic de ordine

Durata medie de parcurgere a acestei uniti de nvare este de trei ore.

Exemplu
Fie x = (5, 4, 2, 8, 9, 1, 7). Ce se nelege prin a i-a component n ordine
statistic? Avem n = 7 i n mod evident 1 i 7. Dac l consider pe i = 3,
vomnelege prin a i-a component n ordine statistic a irului x elementul al
treilea din irul x sortat n ordine cresctoare. x = (1, 2, 4, 5, 7, 8, 9), x(3) = 4
Aplicai exemplulde mai sus pentru irul 10, 9, 3, 4, 5, 2, 7.

Mediana depinde de paritatea numrului de elemente din irul dat. Dac n este impar
atunci mediana este unic i se gsete n irul sortat pe poziia i = (n + 1)/2. Altfel avem dea

61

face cu dou mediane, una dintre ele pe poziia i = n/2, cealalt pe poziia i = n/2 + 1. n
general, pentru un ir de lungime n vom considera median elementul de pe poziia (n+1)/2
din irul sortat. n cazul nostru mediana este 5.
Ce reprezint minimul i maximul din perspectiva statisticilor de ordine?
Minimul este prima component n ordine statistic a unui ir. Maximul este a n-a component
n ordine statistic.
Determinarea simultan a minimului i a maximului
MinMax(x, n)
dac n mod 2 = 0 atunci
dac x(1) < x(2)
atunci
min = x(1)
max = x(2)
altfel
min = x(2)
max = x(1)
sfrit dac
k=3
altfel
min = x(1)
max = x(1)
k=2
sfrit dac
pentru i = k, n 1, 2
dac x(i) < x(i + 1)
atunci
mic = x(i)
mare = x(i + 1)
altfel
mic = x(i + 1)
mare = x(i)
sfrit dac
dac mic < min
atunci
min = mic
sfrit dac
dac mare > max
atunci
max = mare
sfrit dac

62

sfrit pentru
scrie( minimul este , min )
scrie( maximul este , max )
Return
Numrul de comparaii ntre elemente necesare n determinarea independent a min i max este
2(n1) (prin dou parcurgeri ale irului), n schimb pentru determinarea simultan a min i max
avem nevoie de 3 n / 2 2 comparaii (demonstrai acest lucru).
Sugerm cititorului s execute algoritmul pentru intrarea x = (1, 2,3, 4, 5,
6,85, 2).
Gsirea celor mai mici dou elemente dintrun ir
Ideea algoritmului prezentat mai jos este urmtoarea: se compar elementele 1 cu 2, 3
cu 4, etc; pentru fiecare pereche comparat se determin cel mai mic. Aceste elemente
ctigtoare se vor compara la rndul lor pe perechi adiacente, ca mai sus. Cnd rmne un
singur element, acesta este minimul irului. Atam acestor operaii un arbore binar (posibil
nestrict) echilibrat, construit n felul urmtor: exist n noduri terminale (frunze), fiecare
coninnd drept informaie cte un element din ir; un nod neterminal are drept valoare
minimul din cei doi fii.

Evident c pentru fiecare nod neterminal, informaia sa coincide cu a unui descendent


al su. n cadrul acestui algoritm vom folosi termenul nvins n exprimarea x este nvins de
y dac x > y (deoarece se caut minimul). Pentru determinarea celui de al doilea minim
trebuie s determinm cel mai mic element din vectorul iniial nvins de minim: n exemplele
date este 2, nvins de 1. Pentru aceasta vom considera informaia din cei doi descendeni ai
rdcinii, n ambele exemple date fiind 4 i 1. 4 este cel mai mic element din jumtatea stng
a irului (este descendent stng al rdcinii) i va trebui s cutm n jumtatea dreapt a
irului cel mai mic numr, nvins de rdcin; acest lucru se face (n exemplele date) cobor nd
n subarborele drept spre frunze. Se compar toate elementele pe care informaia din rdcin
lea depit cu minimul din jumtatea stng (n cazul nostru 4); coborrea se termin atunci

63

cnd fie se ajunge la frunze (cazul (b)), fie este evident c nu mai are sens coborrea (cazul
(a)). n algoritmul de mai jos nu se va construi explicit arborele. Se vor folosi nite cozi pentru
meninerea pentru fiecare element din ir a elementelor nvinse de acesta.
MinMin(x, n, min1, min2)
pentru i = 1, n
C(i) = {C(i) este coad}
sfrit pentru
ct timp n > 1
pozitie = 1{pozitie este indicele din vector unde se vor pune elementele nvingtoare}
pentru i = 1, n 1, 2
dac a(i) < a(i + 1) atunci
imin = i
imax = i + 1
altfel
imin = i + 1
imax = i
sfrit dac
a(pozitie) = a(imin)
C(pozitie) = C(imin) {a(imax)} pozitie = pozitie + 1
dac n mod 2 _= 0 atunci
{a rmas un element care nu are cu cine se compara}
a(pozitie) = a(n)
C(pozitie) = C(n)
pozitie = pozitie + 1
sfrit dac
n = pozitie 1
sfrit pentru
sfrit ct timp
min1 = a(1)
min2 C(1)
ct timp C(1) _=
y C(1)
dac y < min2 atunci
min2 = y
sfrit dac
sfrit ct timp
Return

64

Prin C(i) = C(j) nelegem trecerea elementelor din coada C(j) n coadaC(i) (dac cozile
sunt implementate folosind liste nlnuite, atunci aceast operaie se poate face n timp (1)),
iar prin C(i) = C(j) {x} se nelege x C(j) urmat de C(i) = C(j).
Numrul de comparaii se determin dup cum urmeaz: n 1 pentru determinarea
minimului (terminarea ciclului ct timp), dup care numrul de comparaii pentru determinarea
minimului din coada C(1). Cum n aceast coad exist cel mult
log2 n elemente (nlimea arborelui de comparaii - demonstraia o lsm cititorului), avem c
se execut cel mult log2 n 1 comparaii pentru determinarea celui de al doilea minim. Deci n
total avem n + log2 n 2 comparaii n cazul cel mai defavorabil.
Selecie n timp mediu liniar
n general determinarea celei de a ia componente este o problem avnd ca date de
intrare un ir a cu n elemente distincte i un numr i cu proprietatea 1 i n, iar ca dat de
ieire un element rez al irului a care are proprietatea c exist exact i 1 elemente ale irului
mai mici dect rez.
Sel(A, p, r, i, rez)
dac p = r atunci
rez = a(p)
Return
sfrit dac
Part(A, p, r, k){Part este algoritmul de partiionare din QuickSort}
dac i k atunci
Sel(a, p, k, i, rez)
altfel
Sel(a, k + 1, r, i k, rez)
sfrit dac
Return
Algoritmul Sel aplicat unui ir deja sortat are (cel mai ru caz) complexitatea O(n2). Se
poate arta c pentru cazul mediu complexitatea este de O(n).
Heapsort
Fie vectorul:

Arborele ataat este cel din figura de mai jos. Acest arbore are adncimea de log2n.

65

O structur creia i se poate pune n coresponden un arbore binar echilibrat se


numete HeapMax, dac oricare nod are o valoare mai mare dect oricare din fiii si. Dac
orice nod are valoarea mai mic dect oricare dintre fii, atunci structura se numete HeapMin.
Algoritmul de heapificare a unui vector cu n elemente ncepnd de la a i-a component este:
Heapify(a, n, i)
imax = i
repet
i = imax
l = 2 i
r=l+1
dac l n i a(l) > a(i) atunci
imax = l
sfrit dac
dac r n i a(r) > a(imax) atunci
imax = r
sfrit dac
dac i _= imax atunci
a(i) a(imax)
sfrit dac
pn cnd i = imax
Return
Algoritmul pentru costrucia unui heap este urmtorul:
ConstruiesteHeap(a, n)
pentru i = [n/2], 1,1
Heapify(a, n, i)
sfrit pentru
Return
Exemplu
Fie irul

Urmrii, n continuare evoluia arborelui la heapificare.

66

Aplicai exemplulde mai sus pentru irul 10, 9, 3, 4, 5, 2, 7.

Complexitatea: pentru un nod de nlime h, complexitatea algoritmului Heapify este de


n
(h). Se poate arta c pentru un heap cu n noduri exist cel mult h 1 noduri de nlime h.
2
Deci timpul de execuie pentru ConstruiesteHeap este:
1
log 2 n
log 2 n h
log 2 n h
n
h
2
T ( n)
O
(
h
)
O
n
2
,
dar
h
h
h
2
h 0
h 0 2
h 0 2
h 02
2h 1
1 1
2
Deci timpul de execuie pentru algoritmul ConstruiesteHeap este O(n).
Folosim cei doi algoritmi de mai sus pentru sortare n modul urmtor:
HeapSort(a, n)
Cheama ConstruiesteHeap(a, n)
pentru i = n, 2,1
a(1) a(i)
Heapify(a, i 1, 1)
sfrit pentru
Return
Sortarea cu HeapSort se face cu complexitatea O(n) + nO(log2 n) =O(n log2 n).
Deoarece sortatea folosind comparaii are complexitatea inferioar (n log2 n) rezult c
algoritmul Heapsort are complexitatea (n log n).
Heapsort este un excelent algoritm de sortare, dar o implementare bun a QuickSort
ului duce la sortri mai rapide. Heapul este ns foarte util n inerea n eviden dinamic a

67

cozilor de prioriti unde o complexitate de n care sar putea obine printrun algoritm cu
inserie este nlocuit cu o complexitate de log n pstrnd coada ntrun heap.
Algoritmi de sortare n timp liniar
Exist algoritmi de sortare a cror complexitate este mai mic dect cea teoretic de
(n log2 n), dar pentru situaii speciale ale irului. Astfel, dac irul este constituit din numere
ntregi pozitive (sau mcar ntregi), sau dac ele sunt numere distribuite uniform ntrun
interval, sau lund n considerare cifrele numerelor din ir se pot da diferii algoritmi de
complexitate liniar pentru sortare.
Algoritmul sortrii prin numrare (CountSort).
Un astfel de algoritm este algoritmul de sortare prin numrare numit CountSort. Fie
(x(i))i=1,n un ir de numere naturale strict pozitive i k maximul acestui ir. Sortarea prin
numrare constn gsirea a cte elemente sunt mai mici dect x(i) (pentru fiecare i); n acest
fel vom ti pe ce poziie trebuie s se afle x(i) n irul sortat (dac sunt m elemente mai mici
dect x(i), atunci el ar trebui s se afle pe poziia m + 1 n irul sortat). Considerm irul iniial
x = (x(1), x(2), . . . , x(n)); y = (y(1), y(2), . . . , y(n)) va fi irul sortat, iar c = (c(1), c(2), . . .
c(k)) este un ir ajuttor.
CountSort(x, n, y, k)
pentru i = 1, k
c(i) = 0
sfrit pentru
pentru j = 1, n
c(x(j)) = c(x(j)) + 1
sfrit pentru
pentru i = 2, k
c(i) = c(i) + c(i 1)
sfrit pentru
pentru j = n, 1,1
y(c(x(j))) = x(j)
c(x(j)) = c(x(j)) 1
sfrit pentru
Return
Exemplu
x = (3, 6, 4, 1, 3, 4, 1, 4), k = 6.
- elementele lui c sunt iniializate cu 0: c = (0, 0, 0, 0, 0, 0)
- pentru i de la 1 la k, pe poziia i din c obinem numrul de elemente egale
cu i n irul x: c = (2, 0, 2, 3, 0, 1)
- pentru i = 2, k, pe poziia i a irului c se obine cte elemente mai mici sau
egale cu i sunt n irul x: c = (2, 2, 4, 7, 7, 8)
- pentru j = n, 1, cu pasul -1, se afl componentele irului y sortat: y = (1, 1,
3, 3, 4, 4, 4, 4, 6)

68

Aplicai exemplulde mai sus pentru irul 10,9,3,4,5,2,7.

Complexitatea algoritmului CountSort este (n+k). Dac k = O(n) (adic exist o


constant c astfel nct pentru orice n suficient de mare, s avem k < cn), atunci complexitatea
algoritmului de sortare prin numrare este (n).
Sortare pe baza cifrelor
S presupunem c avem un ir de numere naturale, avnd toate acelai numr d de cifre
(dac nu, atunci un numr se poate completa cu zerouri esemnificative pn cnd atinge
numrul maxim de cifre din ir). Se poate face ordonarea irului de numere n felul urmtor: se
pornete de la cifra cea mai puin semnificativ (ultima cifr) i se sorteaz elementele pe baza
acestei informaii, obinnduse o alt ordine (un alt ir). Se pornete de al acest ultim ir
generat i se sorteaz elementele dup urmtoarea cifr mai puin semnificativ (penultima
cifr), pentru care se face sortarea elementelor; i aa mai departe, n final fcnduse sortarea
dup cea mai semnificativ cifr (prima cifr). Esenial este ca dac sortarea se face dup cifra
i (numerotarea cifrelor o considerm de la stnga spre dreapta, de la cifra cea mai semnificativ
la cea cea mai puin semnificativ), atunci se pleac de la ordinea obinut anterior pentru
cifra i+1(1 i < d); n al doilea rnd, algoritmul care este folosit pentru sortarea cifrelor trebuie
s aibe urmtoarea proprietate: dacntrun ir x = (x(1), . . . , x(n)) avem c x(i) = x(j) i i < j,
prin sortare se ajunge ca x(i) s fie repartizat pe poziia i_, x(j) s fie repartizat pe poziia j_, iar
i_ < j_ (spunem c algoritmul de sortare este stabil ). n pseudocod, algoritmul este:
OrdonarePeBazaCifrelor(a, d)
pentru i = d, 1,1
sorteaz stabil tabloul a dup cifra i
sfrit pentru
Return
Analiza algoritmului depinde n mod evident de complexitatea algoritmului prin care se
face sortarea pe baza unei cifre. De exemplu, dac numerele sunt n baza b, se poate folosi
sortarea prin numrare din seciunea precedent (care este stabil). Pentru fiecare cifr dup care
se face sortarea avem timpul (n + b), iar pentru sortarea complet (ciclul pentru)
complexitatea este (d(n + b)). Dac d este constant i b = O(n), atunci complexitatea este
liniar.
Rezumat
Noiunea de statistic de ordine generalizeaz pe cele de minim, maxim,
median. Selectarea celei de a i-a statistici de ordine se pate realiza n timp
liniar. Pentru cazuri particulare ale vectorilor care se sorteaz, se pot folosi
algoritmi liniari (se depeste deci bariera de (n log n) demonstrat pentru
algoritmi care lucreaz exclusiv pe baza comparaiilor).

69

M2.U4.3. Test de autoevaluare a cunotinelor


1. Descriei un algoritm care, fiind dat o mulime S de n numere ntregi i
distincte i un ntreg pozitiv k n, determin cele k numere care sunt cele mai
apropiate de mediana lui S.
2. Fie a[1 . . . n], b[1 . . . n] dou iruri sortate. Scriei un algoritm performant
care gsete mediana celor 2n numere.
3. Fiind dat o mulime de n numere, dorim s gsim cele mai mari i numere n
ordinea sortat, folosind un algoritm bazat pe comparaii. S se scrie mai multe
variante i s se compare din punct de vedere al complexitii.
4. S se modifice algoritmul CountSort astfel nct s sorteze i iruri
coninnd numere ntregi negative.

70

M2.U5. Metoda Greedy


M2.U5.1. Introducere
Unitatea introduce o nou metod de rezolvare a problemelor. Ea va arta cum se
abordeaz problemele pentru care soluia se poate construi prin alegeri ale
componentelor, alegeri asupra crora nu se revine. Se dorete a se exemplifica
demonstrarea corectitudinii algoritmilor (fr de care acesta ar fi doar o euristic).
Probleme clasice, ale cror rezultate se pot folosi n cazuri mai generale sunt
enunate si rezolvate (sau sunt fcute trimiteri bibliografice).

M2.U3.2. Obiectivele unitii de nvare


La sfritul acestei uniti de nvare studenii vor fi capabili s:
abordeze probleme pentru care soluia se poate construi prin alegeri ale
componentelor, alegeri asupra crora nu se revine
nu scrue algoritmi Greedy fr demonstraia corectitudinii

Durata medie de parcurgere a acestei uniti de nvare este de dou ore.

Metoda Greedy este o metod de elaborare a algoritmilor ce determin x X ce


verific o condiie dat C. Exemplul semnificativ este cel al problemelor de optimizare n care
se determin x ce minimizeaz sau maximizeaz o funcie obiectiv. S presupunem c soluia
x are mai multe componente i c spaiul soluiilor X este cunoscut. De asemenea presupunem
c C ofer un criteriu de selecie prin care se pot alege valori pentru componentele soluiei.
Modul de rezolvare al problemelor dat de strategia Greedy este acela de construcie a soluiei
component cu component. Se pleac de la soluia vid i la fiecare pas al algoritmului se
selecteaz o valoare pentru o component; dac aceast valoare convine, atunci aceasta este
pus n soluie pe componenta respectiv. ntrun cuvnt, se ia tot ce este bun pentru
construcia soluiei. Aceast selecie se face n funcie de C, valoarea aleas asigurnd la pasul
respectiv cea mai bun continuare n spiritul lui C. Strategia descris anterior are un posibil
inconvenient dat de modul de selecie. Dac la fiecare pas alegem cea mai bun valoare n
spiritul lui C, nu este necesar ca n final s obinem soluia optim cutat. Este ceea ce se
ntmpl n problemele de optimizare: o succesiune de creteri poate s duc la un optim local
i nu la cel global cutat. Dac se reueste s se demonstreze c soluia gsit verific condiia
C atunci am obinut o soluie pentru problem. n caz contrar, dac soluia gsit este suficient

71

de aproape de soluia optim spunem c avem o soluie Greedy eurisitc. Cele prezentate
anterior pot fi descrise de procedura Greedy de mai jos:
Greedy(x)
x=
ct timp soluia nu este complet
selecteaz o valoare a
dac valoarea a convine
atunci
x = x {a}
sfrit dac
sfrit ct timp
Return
n concluzie o problem se rezolv prim metoda Greedy respectnd etapele:
1. n funcie de condiia C se gsete un criteriu de selecie a valorilor
2. Se elaboreaz algoritmul respectnd schema anterioar
3. Se demonstreaz corectitudinea alegerii fcute artnd c soluia x respect C. n caz
contrar se ncearc s se determine ct de departe este soluia gsit de algoritm fa de soluia
cutat.
Complexitate soluiei Greedy este dat de complexitatea seleciei valorii la fiecare pas
i de numrul de selecii fcut de ctre algoritm. n general, metodele de tip Greedy sunt
metode de complexitate polinomial mic, ceea ce este o justificare pentru utilitatea metodei.
Exemplifcm metoda anterioar prin cteva probleme clasice.
Submulimi de sum maxim
Enunul problemei: Dac x = (x(i), i = 1, n) reprezint o mulime de elemente atunci s
se determine submulimea y = (y(i), i = 1, k) de sum maxim. Date de intrare:
n - dimensiunea vectorului
x = (x(i), i = 1, n), mulimea de elemente;
Date de ieire:
- k numrul de elemente al submulimii y = (y(i), i = 1, k)
- submulimea de elemente.
Problema anterioar este o problem clasic de optimizare care se rezolv prin Greedy.
Submulimea cutat este o soluie a problemei de optimizare max

y|

y Y

Simplitatea problemei de optimizare ofer un criteriu de seleie foarte simplu bazat pe


observaiile:
1. dac n mulimea x sunt numere pozitive, adugarea unuia la submulimea y conduce la
creterea sumei; gsim astfel submulimea y format din toate elementele pozitive;
2. n cazul n care toate elementele sunt negative, submulimea y este format cu cel mai mare
element.

72

Ca atare, la fiecare pas alegem n y elementele pozitive. Dac nu sunt, atunci alegem n
y elementul maximal.
SumaMax(n, x, k, y)
k=0
pentru i = 1, n
dac x(i) 0
atunci
k=k+1
y(k) = x(i)
sfrit dac
sfrit pentru
dac k = 0
atunci
max = x(1)
pentru i = 2, n
dac x(i) > max
atunci
max = x(i)
sfrit dac
sfrit pentru
k=1
y(k) = max
sfrit dac
Return
Se observ c fiecare din cele n selecii are complexitate (1), gsind n final o complexitate
pentru algoritm de (n).
Teorema de Corectitudine. Submulimea y determinat de ctre algoritm are suma
elementelor maxim fa de toate submulimile lui x.
Demonstraie. Notm sum(x) suma elementelor din x. Fie z submulimea de sum maxim.
Vom arta c y = z tratnd cele dou cazuri.
1. n x nu sunt elemente pozitive. Atunci sum(z) z(1) max {x(i), i = 1, n} =
sum(y); din proprietatea lui z rezult sum(z) = sum(y) i deci y este submulime de sum
maxim.
2. n x sunt elemente pozitive. Atunci urmtoarele afirmaii conduc la egalitatea y = z:
(a) z nu conine elemente negative. Dac z conine elementul a negativ, atunci sum(z) =
sum(z {a}) + a < sum(z {a}) fals, pentru c z este de sum maximal;
(b) z conine toate elementele pozitive. Dac z nu conine elementul strict pozitiv a atunci
sum(z) < sum(z + {a}) fals.
Rmne c z nu conine elemente negative i conine toate pozitivele, deci y = z

73

Arborele Huffman
Enunul problemei: dac S = {s1, s2, . . . , sn} este o mulime de semnale i p = (p(i), i =
1, n) este vectorul de probabilitate asociat lui S, atunci s se determine codul de lungime
optim asociat lui S.
Date de intrare:
- n numrul de semnale,
- p = (p(i), i = 1, n) vectorul de probabiliti.
Date de ieire -codul optim reprezentat sub form arborescent.
Un exemplu de cod care simuleaz optimalitatea este alfabetul Morse. O caracteristic a
acestui cod este aceea c simbolurile cele mai probabile sunt codificate cu mai puine caractere,
astfel nct costul transmisiei s fie ct mai mic. nainte de a da rezolvarea problemei, este bine
s facem o prezentare a ctorva noiuni de teoria codurilor.
Un cod binar este o aplicaie injectiv : S {0, 1}+. Lungimea medie a codului n
n

funcie de sistemul de probabiliti este definit de L() =

i 1

( si ) p(i ) , unde (|a| este

numrul de caractere a lui a). Un cod este optim dac minimizeaz lungimea medie. Dac
:S{0, 1}+ este un cod instantaneu, atunci arborele m - ar asociat se noteaz cu T i
verific:
- are attea frunze cte semnale are S;
- drumul n arbore de la rdcin la frunz s S d codificarea (s) prin concatenarea
marcajelor muchiilor.
Lungimea medie a unui cod poate fi transportat ntro lungime medie a arborelui
asociat, dat de L( )

n
i 1

(si ) p(i)

i 1

nivT (si ) p(i)

L(T )

Numim arbore Huffman asociat sistemului de numere reale p = (p(i), i =1, n) arborele
ce minimizeaz L(T )

n
i 1

nivT (si ) p(i)

Vom lucra cu structuri arborescente reprezentate nlnuit n care n cmpul de


informaie avem probabilitatea sau frecvena simbolului. Pn n acest moment nu am spus
nimic despre modul de aplicare al stretegiei Greedy. Rmne afirmaia fcut iniial c
semnalele cele mai probabile sunt codificate cu ct mai puine caractere.
Algoritmul lui Huffman construiete codul optim prelucrnd o pduredup regulile:
- iniial pdurea este format din vrfuri izolate reprezentnd semnalele.
- se repet o execuie dat de:
1. se selecteaz arborii pdurii avnd informaia cea mai mic;
2. se conecteaz cei doi arbori alei ntrunul singur printro rdcin comun avnd
informaia suma informaiilor din arbori. Proceura Huffman determin arbore minim.
Huffman(n, s, p, rad)
pentru i = 1, n

74

s(i) LIBERE{LIBERE este zona din care se fac alocri de memorie}


LS(s(i)) =
LD(s(i)) =
INFO(s(i)) = p(i)
sfrit pentru
Cheama Sort(n, s)
pentru i = n, 2,1
x LIBERE
LS(x) = s(i)
LD(x) = s(i 1)
INFO(x) = p(i) + p(i 1)
j=i1
pentru j > 0 i INFO(x) > INFO(s(j))
s(j + 1) = s(j)
j=j1
sfrit pentru
s(j + 1) = x
sfrit pentru
rad = s(1)
Return
n algoritmul precedent procedura Sort determin o sortare a semnalelor s(i), i = 1, n
descresctor dup p(i).
Teorema de complexitate. Algoritmul Huffman are complexitatea (n2).
Demonstraie. Complexitatea algoritmului este dat de complexitatea sortrii i de
complexitatea seleiilor repetate. Complexitatea sortriiu este (n log n). Seleciile repetate ce
se fac utilizeaz principiul inserrii directe. Pentru a insera semnalul p n structura (s(j), j = 1,
n
n(n 1)
i1) facem cel mult i1 comparaii, gsind n final un numr de i 1
(n 2 )
i 1
2
comparaii.
Rezult complexitatea din enun.
Teorema de Corectitudine. Algoritmul Huffman genereaz un cod optim n raport cu
probabilitile p = (p(i), i = 1, n).
Demonstraie.
Vom folosi n demonstraie urmtoarele notaii:
H = H(p1, . . . , pn) - arborele dat de algoritmul lui Huffman
T = T(p1, . . . , pn) - arborele optim, unde p(i) = pi, i = 1, n.
Despre arborele optim se poate arta:
pi < pj rezult nivT (si) > nivT (sj)
exist un arbore optimn care cele mai puin probabile semnale sunt frai.

75

S presupunem c si i sj sunt semnalele cu probbabilitatea cea mai mic i c prin


unirea celor dou semnale se creeaz un nou semnal notat si,j .
Algoritmul Huffman construiete arborele binar dup regula recursiv dat de figur Avem
urmtorul calcul de lungime:

Crearea unui nod pe baza semnalelor cele mai puin probabile


n

L( H )
i 1

n 2

niv(si )

i 1

niv(si ) p(i) niv(s n 1 ) p(n 1) niv(s n ) p(n)

i deoarece niv(sn1) = niv(sn) rezult c:


n 2

L( H )
i 1

niv(si ) p(i) ( p(n 1)

p(n))niv( s n 1 ) 1 p(n 1)

p ( n)

n 2
i 1

niv(si ) p(i)

p(n 1, n)niv( s n,n 1 )

n final vom avea


L( H ( p(1),...,p(n)))

p(n 1)

L( H ( p(1),...,p(n 1)

p ( n)

p(n))) p(n 1)

p(n)

Analog se arat c i arborele optim, care i are ca frai pe s n-1 i sn verific


L(T ( p(1),...,p(n))) L(T ( p(1),...,p(n 1) p(n))) p(n 1) p(n)
Demonstraia optimalitii se va face prin inducie dup n. Pentru n = 2 algoritmul ne
ofer H(p(1), p(2)) care codific pe s1 cu 0 i pe s2 cu 1, ceea ce nseamn c este un cod
optim.
S presupunem c avem proprietatea adevrat pentru n semnale i anume codul
Huffman asociat semnalelor s1, . . . , sn este optim.
Fie p = (p(i), i = 1, n+1) un sistem cu n+1 probabiliti n care p(n1) i p(n 2) sunt cele mai
mici dou numere din irul p. Aplicnd formulele gsite anterior obinem:
L(H(p(1), . . . , p(n), p(n + 1))) = L(H(p(1), . . . , p(n) + p(n + 1))+p(n) + p(n + 1))
L(T(p(1), . . . , p(n), p(n + 1))) = L(T(p(1), . . . , p(n) + p(n + 1))+p(n) + p(n + 1))
Pentru c arborele Huffman codific n semnale optim vom gsi:
L(T(p(1), . . . , p(n) + p(n + 1))) L(H(p(1), . . . , p(n) + p(n + 1)))
Din relaiile de mai sus vom gsi:
L(T(p(1), . . . , p(n) + p(n + 1))) + p(n) + p(n + 1) L(H(p(1), . . . , p(n) +p(n + 1))) + p(n) +
p(n + 1)

76

i deci L(T(p(1), . . . , p(n), p(n + 1))) L(H(p(1), . . . , p(n), p(n + 1))) Din optimalitatea lui T
vom gsi: L(T(p(1), . . . , p(n), p(n + 1))) = L(H(p(1), . . . , p(n), p(n + 1))) deci H este un arbore
optim.
Interclasare optimal
Enunul problemei: Dnduse o mulime de vectori sortai cresctor s se elaboreze un
algoritm care determin interclasarea acestora cu numrul minim de comparaii.
Date de intrare:
n - numrul de vectori,
nr = (nr(i), i = 1, n) numrul de elemente din fiecare vector, x(i) = (x(i, j), j = 1, nr(i))
irul de vectori.
Date de ieire:
y = (y(i), i = 1,
nr(1) + + nr(n)), vectorul interclasat.
S presupunem c interclasarea a doi vectori se face prin algoritmul de interclasare
direct. Dac dimensiunile vectorilor sunt m, respectiv n atunci numrul de comparaii este cel
mult m + n 1. n general aceast interclasare nu este una optim dar facem presupunerea c
orice doi vectori se interclaseaz folosind interclasarea direct. n funcie de modul de
interclasare al vectorilor de interclasare se obin diferite soluii cu numr distinct de comparaii.
Este deci esenial s gsim o ordine care conduce ctre numrul minim de comparaii.
Vom nota x+y vectorul obinut prin interclasarea lui x cu y.
Exemplu
Dac avem 4 vectori x, y, z, t cu respectiv 10, 20, 30 i 40 de elemente atunci:
interclasarea ((x+y)+z)+t se face cu (10+20-1)+(30+30-1)+(60+40- 1)=187
comparaii,
inteclasarea ((z +t)+y)+x se face cu (30+40-1)+(70+20-1)+(90+10-1)=257
comparaii,
deci prima strategie este mai bun dect a doua (de fapt, este chiar cea optim).
Din acest exemplu se observ c maxim 40 se regsete n toate interclasrile
ce se efectueaz conducnd al un numr mare de comparaii.
Aplicai exemplulde mai sus pentru vectorii cu 15, 20, 4 ,5, 7, 30 de
componente.
Idea ar fi ca vectorii cu dimensiuni mici s se se interclaseze prima dat astfel nct
aceste valori mici s apar n sumele de interclasare. Deci la fiecare pas vom selecta pentru
interclasare vectorii de dimensiune minim. Pentru ca selecia s se fac ct mai uor pstrm
vectorii n matricea x astfel nct vectorul nr s fie sortat descresctor. n acest caz se
selecteaz ultimii 2 vectori din matricea x. Dup realizarea interclasrii noul vector se
insereaz nmatricea x folosind diferite tehnici: inseria direct sau inseria binar. Se repet
procesul pn cnd rmne un singur vector.

77

InterOpt(n, nr, x, z)
Cheama Sortare(n, nr, x)
pentru i = n 1, 1,1
Inter(nr(i + 1), x(i + 1), nr(i), x(i), y)
n1 = nr(i)
n2 = nr(i + 1)
j=i1
ct timp j > 0 i n(j) > n1 + n2
pentru k = 1, nr(j)
x(j + 1, k) = x(j, k)
sfrit pentru
nr(j + 1) = nr(j)
j=j1
sfrit ct timp
pentru k = 1, n1 + n2
x(j + 1, k) = y(k)
sfrit pentru
nr(j + 1) = n1 + n2
sfrit pentru
Return
n procedura anterioar procedura Sortare determin rearanjarea elementelor (liniilor)
matricii x descresctor n funcie de vectorul nr iar Inter face interclasare direct a doi vectori.
Pentru a analiza corectitudinea algoritmului definim printro formul recursiv arborele asociat
unei strategii de interclasare:
1. Arborele asociat unui vector este format dintrun nod ce are ca informaie numrul de
elemente ale vectorului;
2. arborele asociat interclasrii a doi vectori x(1) i x(2) este reprezentat grafic n figur
3. Dac T1 este arborele asociat irului de vectori x(i1), . . . , x(ik) iar T2 este arborele asociat
irului de vectori x(ik+1), . . . , x(in) atunci T din figur este arborele asociat prin strategie
irului de vectori x(i1), . . . , x(in).

Arborele construit pe baza a doi vectori

78

Arborele construit pe baza a dou subsecvene de vectori.


Arborele asociat unui ir de vectori verific:
- are attea frunze ci vectori sunt n irul de vectori;
- informaia ataat unui nod este suma dimensiunilor vectorilor din subarborii stng i drepi
ai nodului;
- dac un vector apare pe nivelul p atunci termenii irului apar n exact p interclasri;
- numrul total de comparaii dat de strategie este L(T )

nr (i) niv(i) n 1, unde niv(i)

i 1

este nivelul n T al vectorului x(i).


Teorema de Corectitudine. Algoritmul InterOpt determin interclasarea vectorilor x(i), i = 1,
n cu numr minim de comparaii realizate din interclas rile directe.
Demonstraie. Se observ c arborele asociat unei strategii de interclasare ete arborele
Huffman asociat numerelor nr(1), . . . , , nr(n). Algoritmul anterior repet modul de construcie
al arborelui Huffman deci suma

nr (i ) *niv(i) este minim i implicit L(T) este minim.

i 1

Rezult c strategia de interclasare este optim.


Teorema de Complexitate. Dac L este numrul de comparaii rezultat din interclasrile
directe, atunci algoritmul InterOpt are complexitatea (L+ n2).
Demonstraie. Complexitatea algoritmului este dat de numrul de comparaii fcut de
interclasrile directe i cele date de pstrarea structurii x. n primul caz numrul este n. Pentru
cel de al doilea caz se observ c inserarea direct folosete cel mult i-1 comparaii pentru
punerea irului y n structura x. Gsim un numr total de

(i 1)

(n 2 ) comparaii.

i 2

Dac se utilizeaz o inserare binar n structura x atunci complexitatea algoritmului


devine (L + n log n).
Rezumat
Metoda Greedy nseamn construirea pas cu pas a soluiei, prin alegerea
componentelor pe baza unei strategii deduse din enunul problemei. Spre
deosebire de metoda Backtracking, nu se revine asupra unei decizii. Ea poate fi
folosit i pentru elaborarea unor algoritmi euristici (care nu determin
optimul, sau pentru care o demonstraie de corectitudine nu este dat). Pentru
fiecare rezolvare greedy trebuie s se ncerce justificarea prin metode
matematice a optimalitii soluiei.
79

M2.U5.3. Test de autoevaluare a cunotinelor


1. Dac x = (x(i), i = 1,m), y = (y(i), i = 1, n) reprezint dou mulimi de
elemente, atunci s se determine mulimea intersecie z = (z(i), i =1, k).
2. Dnduse n obiecte de cost c = (c(i), i = 1, n) cu greutatea g =(g(i), i = 1, n)
i un rucsac de capacitate maxim G, s se elaboreze un algoritm de umplere a
rucsacului de cost maxim. Un obiect poate fi pus n rucsac ntrun anumit
procent (problema continu a rucsacului).
3. ntro sal, ntro zi trebuie planificate n spectacole. Pentru fiecare spectacol
se cunoate intervalul n care se desfoar: [st, sf). Se cere s se planifice un
numr maxim de spectacole astfel nct s nu se suprapun..
4. Se dau n numere ntregi nenule b1,..., bn i m numere ntregi nenule a1,... am,
n m. S se determine o submulime a mulimii B = {b1, , bn} care s
maximizeze valoarea expresiei: E = a1*x1 + a2*x2 +...+ am*xm unde xi B..
5. O staie de servire trebuie s satisfac cererile a n clieni. Timpul de servire
pentru fiecare client este cunoscut: pentru clientul i timpul este ti. S se
minimizeze timpul total de ateptare. T

n
i 1

clientul i)

80

(timpul de ateptare pentru

M2.U6. Metoda programrii dinamice.


M2.U6.1. Introducere
Programarea dinamic, asemenea metodelor Divide et Impera, rezolv
problemele combinnd soluiile unor subprobleme. Programarea n acest context
se refer la o metod tabelar i nu la scrierea unui cod pentru calculator.

M2.U6.2. Obiectivele unitii de nvare


La sfritul acestei uniti de nvare studenii vor fi capabili s:
fie familiarizat cu trsturile generale ale unei probleme pentru care se
apeleaz la programare dinamic
i nsueasc strategia general de rezolvare a unei probleme folosind
aceast metod.

Durata medie de parcurgere a acestei uniti de nvare este de trei ore.

Am vzut, n capitolul referitor la divide et impera, c algoritmii construii cu aceast


metod partiioneaz problema n subprobleme indepenedente pe care le rezolv n general
recursiv, dup care combin soluiile lor pentru a obine soluia problemei iniiale. Spre
deosebire de aceast abordare, programarea dinamic este aplicabil atunci cnd aceste
subprobleme nu sunt independente, adic au n comun subprobleme1.
n general, programarea dinamic se aplic problemelor de optimizare.
Fiecare soluie are o valoare i se dorete determinarea soluiei optime (minim sau maxim). O
asemenea soluie numete soluie optim a problemei, prin contrast cu valoarea optim,
deoarece pot exista mai multe soluii care s realizeze valoarea optim.
Dezvoltarea unui algoritm bazat pe metoda programrii dinamice poate fi mprit ntro
secven de patru pai:
1. Caracterizarea structurii unei soluii optime
2. Definirea recursiv a valorii unei soluii optime
3. Calculul valorii unei soluii optimentro manier bottomup (plecnd de la subprobleme
de dimensiune mic i ajungnd la unele de dimensiuni din ce n ce mai mari)
4. Construirea unei soluii optime din informaia calculat.
Paii 13 sunt baza unei abordri de tip programare dinamic. Pasul 4 poate fi omis dac se
dorete doar calculul valorii optime. n vederea realizrii pasului 4, deseori se pstreaz

81

informaie suplimentar de la execuia pasului 3, pentru a uura construcia unei soluii


optimale.
nmulirea unui ir de matrici
Primul nostru exemplu de programare dinamic este un algoritm care rezolv problema
nmulirii unui ir de matrici. Se d un ir A1,A2, . . . , An de n matrice care trebuie nmulite.
Acest produs se poate evalua folosind algoritmul clasic de nmulire a unei perechi de matrici
ca subalgoritm, o dat ce produsul A1 An este parantezat (aceast restricie este impus
de faptul c nmulire matricilor este operaie binar). Un produs de matrici este complet
parantezat dac este format dintro sigur matrice sau dac este format din factori complei
parantezai.
Exemplu.
produsul A1 A2 A3 A4 poate fi complet parantezat n 5 moduri
distincte, astfel:
(A1 (A2 (A3 A4)))
(A1 ((A2 A3) A4))
((A1 A2) (A3 A4))
((A1 (A2 A3)) A4)
(((A1 A2) A3) A4)

Aplicai exemplul de mai sus pentru 4 matrici.

Modul n care parantezm un ir de matrici poate avea un impact dramatic asupra


costului evalurii produsului. S considerm mai nti costul nmulirii a dou matrici.
Algoritmul standard este dat de urmtoarea procedur, descris n pseudocod. Prin linii i
coloane nelegem numrul de linii i coloane ale matricilor implicate.
Inmultire(A, n, m,B, p, q,C)
dac m _= p atunci
scrie( dimensiuni incompatibile )
altfel
pentru i = 1, n
pentru j = 1, q
C(i, j) = 0
pentru k = 1,m
C(i, j) = C(i, j) + A(i, k) B(k, j)
sfrit pentru
sfrit pentru
sfrit pentru
sfrit dac

82

Return
Dou matrici se pot nmuli numai dac numrul de coloane ale primei matrici este egal cu
numrul de linii ale celei de a doua matrici, adic dac avem A(n,m) i B(m, q); n acest caz C =
AB va avea n linii i q coloane. Timpul necesar pentru calculul matricei C este dat de numrul
de nmuliri scalare, n m q.
Exemplu
Pentru a ilustra modul n care apar costuri diferite la parantezri diferite ale
produsului de matrici, s considerm problema irului A1,A2,A3. S
presupunem c dimensiunile matricilor sunt 10 100, 100 5, 5 50. Dac
efectumnmulirile pentru ((A1A2)A3), atunci vom avea 101005 = 5000
nmuliri scalare pentru a efectua (A1 A2), care va fi o matrice de dimensiune
105, plus alte 10550 = 2500 nmuliri pentru a efectua (A1A2)A3, deci n
total rezult 7500 nmuliri scalare. Pentru parantezarea (A1 (A2 A3)) vom
avea 100 5 50 = 25000 nmuliri scalare pentru a efectua A2 A3, care va fi
o matrice de dimensiune 10050, plus alte 1010050 = 50000 nmuliri
scalare pentru a efectua A1 (A2 A3). Deci rezult un numr de 75000
nmuliri scalare.
S considerm problema irului A1,A2,A3,A4. S presupunem c dimensiunile
matricilor sunt 10 100, 100 5, 5 50,502. Efeectuai calculele ca mai
sus.
Enunul formalizat al problemei este: dnduse un ir A1,A2, . . . , An de n matrici, unde
pentru i = 1, n matricea Ai are dimensiunle pi1 pi, s parantezeze complet produsul A1 A2
An astfel nct s se minimizeze numrul de nmuliri scalare. nainte de a rezolva
problema nmulirii irului de matrici prin programare dinamic, trebuie s ne asigurm c
verificarea complet a tuturor parantezrilor nu duce la un algoritm eficient. Fie P(n) numrul
de parantezri distincte ale unei secvene de n matrice. O secven de n matrice o putem diviza
ntre matricele k i k + 1 pentru orice k = 1, . . . , n 1i apoi putem descompunen paranteze,
n mod independent, fiecare dintre secvene.
n acest fel vom obine urmtoarea recuren:

P ( n)

1............................daca..n 1
n 1
P(k ) P(n k ).....daca..n 2
k 1

Numerele astfel definte se numesc numerele lui Catalan i se arat c P(n)=C(n-1) unde
1 2n
C (n)
(4 n / n 3 / 2 )
n 1 n
Numrul de soluii este exponenial, deci ar fi o strategie slab. Primul pas n schema
general a metodei programrii dinamice este dat de caracterizarea unei soluii optimale.
Pentru problema noastr descrierea este fcut n continuare. Vom face urmtoarea convenie

83

de notare: matricea obinut n urma procesului de evaluare a produsului Ai Ai+1 . . . Aj se


va nota cu Ai...j . O parantezare optim a produsului A1. . .An mparte produsul ntre Ak i
Ak+1 pentru un anumit k din intervalul 1 . . . n. Aceasta nseamn c pentru o valoare a lui k,
mainti calculm matricele A1...k i Ak+1...n i apoi le nmulim pentru a obine rezultatul
final A1...n. Costul acestei parantezri optime este dat de suma costurilor calculului pentru
matricele A1...k, Ak+1...n i A1...n. Observaia care trebuie fcut este c: parantezarea
subirului prefix A1 . . . Ak n cadrul parantezrii optime a produslui A1 . . . An este o
parantezare optim pentru A1 . . . Ak, pentru c altfel, dac am presupune c mai exist o
metod de parantezare mai puin costisitoare a lui A1 . . .Ak ar contrazice faptul c
parantezarea pentru A1 . . . An este optim. Aceast observaie este valabil i pentru
parantezara lui Ak+1 . . . An. Prin urmare, o soluie optim a unei instane a unei probleme
conine soluii optime pentru instane ale subproblemelor. Al doilea pas n aplicarea metodei
programrii dinamice este dat de definirea valorii unei soluii optime n mod recursiv, n
funcie de soluiile optime ale subproblemelor. n cazul problemei nmulirii irului de matrice,
o subproblem const n determinarea costului minim al unei parantezri a irului Ai Ai+1
Aj , pentru 1 i j n. Fie m(i, j) numrul minim de nmuliri scalare necesare pentru a
calcula matricea Ai...j; costul modalitilor optime de calcul al lui A1...n va fi m(1, n). Definiia
recursiv a lui m(i, j) este dat de formula
0.........................................................daca...i j
m[i, j ]
...daca..i j
min m[i, k ] m[k 1, j ] pi 1 p k p j
Valorile m(i, j) exprim costul soluiilor optime ale subproblemelor. Pentru a putea
urmri modul de construcie a slouiei optime, s definim s(i, j) care va conine valoarea k
pentru care mprirea produsului Ai . . . Aj produce o parantezare optim. Aceasta nseamn
c s(i, j) este egal cu valoarea lui k pentru care m(i, j) = m(i, k) + m(k + 1, j) + pi1pkpj .
n acest moment este uor s scriem un algoritm recursiv dat de recurena de mai sus care s
calculeze costul minim m(1, n) pentru produsul A1 . . . An. Dar acest algoritm necesit timp
exponenial - nu mai bun dect cutarea complet a parantezrilor. Observaia care se
impunen acest moment se refer la faptul c avem relativ puine subprobleme: o problem
n
pentru fiecare alegere a lui i i j ce satisfac 1 i j n, adic un total de
n
(n 2 ) .
2
Un algoritm recursiv poate ntlni fiecare subproblem de mai multe ori pe ramuri diferite ale
arborelui su de recuren. Aceast proprietate de suprapunere a subproblemelor este a doua
caracteristic a programrii dinamice. n loc s calculm recursiv soluia recurenei vom aplica
pasul al treilea din schema programrii dinamice i vom calcula costul optimal cu o abordare
bottomup. Algoritmul urmtor presupune c matricele Ai au dimensiunile pi1 pi pentru
orice i = 1, . . . , n. Intrarea este secvena (p0, . . . , pn) de n+1 elemente. Procedura foloeste un
tablou auxiliar m(1 . . . n, 1 . . . n) pentru costurile m(i, j) i un tablou auxiliar s(1 . . . n, 1 . . . n)
carenregistreaz acea valoare a lui k pentru care sa obinut costul optimn calculul lui m(i, j)
InmultireMatrici(p, m, s, n)

84

pentru i = 1, n
m(i, i) = 0
sfrit pentru
pentru l = 2, n
pentru i = 1, n l
j=i+l1
m(i, j) =
pentru k = i, j 1
q = m(i, k) + m(k + 1, j) + pi1pkpj
dac q <m(i, j)
atunci
m(i, j) = q
s(i, j) = k
sfrit dac
sfrit pentru
sfrit pentru
sfrit pentru
Return
Algoritmul completeaz tabloul m ntrun mod ce corespunde rezolvrii problemei
parantezrii unor iruri de matrici din ce n ce mai mari. Relaia arat c m(i, j), costul de calcul
al sirului de j i + 1 matrici depinde doar de costuril e calculrii produselor irurilor de mai
puin de j i + 1 matrici. Aceasta nsemna c, pentru k = i, i + 1, . . . , j 1, matricea Ai...k
este un produs de k i + 1 < j i + 1 matrice, iar matricea Ak+1...j este un produs de j k < j
i + 1 matrici. n ciclul pentru i = 1, n algoritmul iniializeaz m(i, i) = 0 (costul minim al
irurilor de lungime 1). La prima execuie a ciclului pentru l = 2.n se calculeaz, cu formula
m(i, i + 1) pentru i = 1, 2, . . . , n 1 (costul minim al irurilor de lungime 2). La a doua trecere
prin ciclul pentru l = 2, n se calculeaz m(i, i + 2), pentru i = 1, 2 . . . , n 2 (costul minim al
irurilor de lungime 3), etc. La fiecare pas, costul m(i, j) calculat in ciclul pentru k = i, j 1
depinde doar de intrrile m(i, k) i m(k+1, j) ale tabloului, deja calculate.
Exemplu
n tabelul de mai jos este descris funcionarea algoritmului pentru un ir de n
= 6 matrici avnd dimensiunile date mai jos.
Matrice Dimensiune
A1
30 35
A2
35 15
A3
15 5
A4
5 10
A5
10 20
A6
20 25
Tabelul cu Dimensiunile matricelor care se nmulesc

85

1
0
0
0
0
0
0

2
15750
0
0
0
0
0

3
7875
2625
0
0
0
0

4
9375
4375
750
0
0
0

5
11875
7125
2500
1000
0
0

6
15125
1050
5375
3500
5000
0

1
0
0
0
0
0
0

2
1
0
0
0
0
0

3
1
2
0
0
0
0

4
3
3
3
0
0
0

5
3
3
3
4
0
0

6
3
3
3
5
5
0

Evoluia algoritmului pentru matricile de mai sus. n partea stng apare


matricea m, n partea dreapt matricea s. n tablourile m, s sunt utilizate doar
elementele de desupra diagonalei principale. Numrul minim de nmuliri
scalare necesare nmulirii acestor ase matrici este m(1, 6) = 15125. Pentru a
calcula cu formula q = m(i, k)+m(k + 1, j) + pi1pkpj pe m(2, 5), procedm ca
mai jos:
m(2, 2) + m(3, 5) + p1p2p5 = 13000
m(2, 3) + m(4, 5) + p1p3p5 = 7125
m(2, 4) + m(5, 5) + p1p4p5 = 11375
rezult m(2,5)= 7125
O simpl examinare a algoritmului conduce la constatarea c timpul de execuie este
(n ). Dei acest algoritm determin numrul optim de nmuliri scalare necesare pentru
calculul produsului irului de matrici, acesta nu prezint n mod direct modul n care se face
nmulirea. Pasul 4 al schemei generale a metodei programrii dinamice urmrete construirea
unei soluii optime din informaia disponibil. n acest caz particular, vom folosi tabloul s
pentru a determina modul optim de nmulire a matricilor. Fiecare element s(i, j) conine
valoarea lui k pentru care parantezarea optim a produsului Ai . . . Aj mparte produsul ntre
Ak i Ak+1. Atunci tim c n produsul final de calcul al matricel A1...n optimul este A1...s(1,n)
As(1,n)+1...n. nmulirile anterioare pot fi determinate recursiv, deoarece s(1, s(1, n))
determin ultima nmulire matriceal din calculul lui A1...s(1,n); analog, s(s(1, n)+1, n)
determin ultima nmulire din produsul As(1,n)+1,n.
Cel mai lung subir cresctor
Problema se enun n felul urmtor: se d un ir de numere a = (a(i), i =1, n). S se
determine cel mai lung subir cresctor. Prin subir se nelege un ir a(i1), a(i2), . . . , a(ik),
unde 1 i1 < i2 < < ik n (elementele subirului nu sunt neaprat adiacente n irul iniial).
Dac am considera toate subirurile de elemente, atunci acestea ar fi n numr de 2n 1. Chiar
i dac am genera prin backatracking subirurile cresctoare ale lui a, tot la complexitate
exponenial sar ajunge. Ideea este de a calcula, pentru fiecare element al irului a cel mai
lung subir cresctor care ncepe cu elementul respectiv. n final, se alege cel mai mare subir
3

86

cresctor din cele determinate pentru fiecare element. S presupunem c cel mai lung subir
cresctor care conine elementul a(p) este a(i1), . . . , a(p), . . . , a(ik) (1). Facem afirmaia c
subirul a(p), . . . , a(ik) este cel mai lung subir cresctor care se formeaz ncepnd cu a(p).
Dac prin absurd nu ar fi aa, atunci ar nsemna c am avea un alt subir cresctor care s
nceap cu a(p): a(p), . . . a(ik)(2). Ori, dac am considera subirul a(i1), . . . , a(p), . . . a(i k)
(partea final a lui (1) estenlocuit cu subirul (2)), atunci am avea un subir cresctor care s
l conin pe a(p) mai lung dect n cazul (1), ceea ce contrazice faptul c am presupus (1)
maximal. Deci optimul total implic optimul local, o caracteristic a problemelor de
programare dinamic. Afirmaiile de mai sus reprezint suportul pentru strategia de rezolvare
aleas.
Vom considera un ir auxiliar L = (L(i), i = 1, n), care va avea pe poziia i lungimea
celui mai lung subir cresctor care ncepe cu valoarea a(i). Relaia pe care o respect
elementele acestui ir este:
1..............................daca..i n
L(i)
1 max L( j ) | j i, a( j ) a(i), daca..i n
unde pentru cazul mulimii vide de la a doua variant maximul se ia 0.
Valorile lui L se vor calcula iterativ (implementarea recursiv ar fi extrem de ineficient,
datorit calculului repetat care sar face pentru aceleai valori), n ordinea L(n), L(n1), . . . ,
L(1). Pentru a determina mai uor subirul de lungime maxim, se construiete un ir sol =
(sol(i), i = 1, n) care va conine pentru valoarea i acel j care d minimul pentru (2.6.1) sau
chiar valoarea i dac un asemenea j nu exist. Determinarea lungimii maxime a unui subir se
face apoi calculnd max( L(i )) iar determinarea efectiv a acestui ir se face pe baza informaiei
i 1, n

din sol.
Exemplu
a = (3, 2, 8, 6, 9, 7), n = 6. Atunci:
L(6) = 1, sol(6) = 6
L(5) = 1 + max = 1, sol(5) = 5
L(4) = 1 + max{L(5), L(6)} = 2, sol(4) = 5
L(3) = 1 + max{L(5)} = 2, sol(3) = 5
L(2) = 1 + max{L(3), L(4), L(5), L(6)} = 3, sol(2) = 3
L(1) = 1 + max{L(3), L(4), L(5), L(6)} = 3, sol(1) = 3
Valoarea maxim din vectorul L este 3, obinut pentru L(1) (i altele). Pe baza
vectorului sol obinem i irul cresctor maximal: 2, urmat de elementul de
indice sol(1) = 3, adic a(3) = 8, care este urmat de elementul de indice sol(3)
= 5, adic a(5) = 9, pentru care avem sol(5) = 5, adic nu are succesor.
Aplicai algoritmul pentru irul a=( 5,3,6,1,3,4,7)

87

Exemplu:
Start SubsirCrescator
citete( n, (a(i), i = 1, n) )
pentru i = n, 1,1
L(i) = 1
sol(i) = i
pentru j = i + 1, n
{Acest ciclu nu se execut niciodat pentru i = n}
dac a(j) a(i) i 1 + L(j) > L(i) atunci
L(i) = 1+L(j)
sol(i) = j
sfrit dac
sfrit pentru
sfrit pentru
LMax = L(1)
pozLMax = 1
pentru i = 2, n
dac L(i) > LMax atunci
LMax = L(i)
pozLMax = i
sfrit dac
sfrit pentru
scrie( Lungimea maxim a unui subir cresctor este:, LMax )
scrie( a(LMax) )
i = pozLMax
ct timp sol(i) _= i
i = sol(i)
scrie( a(i) )
sfrit ct timp
Stop
Complexitatea acestui algoritm este (n2), datorit celor dou cicluri pentru imbricate.
Meninm c nu este absolut necesar vectorul sol, dar determinarea soluiei se face mai uor pe
baza lui.
Rezumat
Am vzut, n capitolul referitor la divide et impera, c algoritmii construii cu
aceast metod partiioneaz problema n subprobleme indepenedente pe care
le rezolv n general recursiv, dup care combin soluiile lor pentru a obine
soluia problemei iniiale. Spre deosebire de aceast abordare, programarea
dinamic este aplicabil atunci cnd aceste subprobleme nu sunt independente,

88

adic au n comun subprobleme.


n general, programarea dinamic se aplic problemelor de optimizare.
Fiecare soluie are o valoare i se dorete determinarea soluiei optime (minim
sau maxim). O asemenea soluie numete soluie optim a problemei, prin
contrast cu valoarea optim, deoarece pot exista mai multe soluii care s
realizeze valoarea optim.
Programarea dinamic este o metod care se folosete n cazurile n care
optimul general implic optimul parial; n astfel de cazuri, demonstraia se
face de cele mai multe ori prin reducere la absurd. Pentru evitarea calculrii
acelorai rezultate, se apeleaz la tabelare rezultatele pentru cazurile luate n
considerare sunt memorate ntro structur de tip matricial. Pe baza acestor
informaii, se poate determina soluia (soluiile, chiar) pentru care se atinge
optimul.
M2.U6.3. Test de autoevaluare a cunotinelor
1. Gsii o parantezare optim a produsului unui ir de matrice al crui ir de
dimensiuni este (5, 10, 3, 12, 5, 50, 6) (adic 5 10, 10 3, etc).
2. Artai c o parantezare complet a unei expresii cu n elemente are exact n
1 perechi de paranteze.
3. Se d o matrice subdiagonal. Se consider toate sumele n care se adun
elementele de pe fiecare linie aflate sub elementul adunat anterior sau sub-ila-dreapta. Se cere un algoritm care gsete suma maxim.
Exemplu:
5
23
812
5934
Suma maxim este S = 5+2+8+9.
4. Folosind rezultatele lui InmultireMatrici s se descrie un algoritm care
realizeaz nmulirea matricilor n ordine optim.

89

Rspunsuri la teste

M1.U2.3. Test de autoevaluare a cunotinelor


1) Se definete produsul scalar a doi vectori A i B, de aceeai dimensiune n cu formula:
n

PS

A(i ) B (i ) .
i 1

S se scrie algoritmul pentru calculul produsului scalar.


2). Se d n, vectorul A(i), i=1, n i un numr x. S se scrie un algoritm care gsete indicele j
pentru care A(j)=x (problema cutrii).
3. Cum se modific algoritmul de mai sus dac tim c vectorul este sortat cresctor?
4. Se dau n i vectorul A(i), i=1, n. S se gseasc cel mai mic element.
5. Reluai problema anterioar n situaia n care trebuie s aflai cte elemente minime sunt.
ncercai s rezolvai problema folosind o singur parcurgere a irului.
6. Se dau n i vectorul A(i), i=1, n. Ce numr apare cel mai des i de cte ori?
7. Cum se modific algoritmul precedent dac vectorul este deja sortat ?
Rspunsuri:
1. Start ProdusScalar
Citete (n, (a(i), i = 1, n), (b(i), i = 1, n) )
ps = 0 {0 este element neutru pentru adunare}
pentru i = 1, n
ps = ps + a(i) b(i)
sfrit pentru
Scrie (Produsul scalar este: , ps )
Stop
Complexitatea este (n).
2. Ideea este de a parcurge irul i a compara fiecare element cu x; dac acest element este
gsit, atunci este oprit cutarea, altfel se merge la urmtorul element. La sfrit se raporteaz
rezultatul. Pentru a semnala faptul c x a fost gsit, vom folosi o variabil boolean gsit care
va primi valoarea adevrat dac x a fost gsit n ir, fals n caz contrar.
Start CutareLiniar
Citete (n, (a(i), i = 1, n), x )
gsit = fals{deocamdat nu am gsit pe x n a}
i = 1{i este indicele elementului curent din ir}
repet
dac a(i) = x atunci
gsit = adevrat
poziie = i

90

altfel
i=i+1
sfrit dac
pn cnd (i > n) sau (gsit = adevrat)
dac gsit = adevrat atunci
Scrie (Am gsit , x, pe poziia , poziie )
altfel
Scrie (x, nu a fost gsit )
sfrit dac
Stop
Complexitatea este (n), cazul cel mai defavorabil fiind cnd x nu se afl n ir.
Observaie: Deoarece execuia instruciunilor din interiorul ciclului trebuie s se fac cel puin
o dat, este corect utilizarea unui ciclu repet.
3. Vom folosi un algoritm asemntor cu cel de la problema precedent, folosind informaia c
irul este ordonat cresctor. Vom da dou variante, cu analiz de complexitate.
(a) Prima variant: ncepem cutarea, dar vom impune o condiie suplimentar: trebuie ca
permanent x a(i). Dac avem x >a(i), atunci este clar c x nu mai poate fi gsit la dreapta
indicelui i.
Start CutareLiniarCresctor
Citete (n, (a(i), i = 1, n), x )
gsit = fals{deocamdat nu am gsit pe x n a}
i = 1{i este indicele elementului curent din ir}
repet
dac a(i) = x atunci
gsit = adevrat
poziie = i
altfel
i=i+1
sfrit dac
pn cnd (i > n) sau (gsit = adevrat) sau (x > a(i))
dac gsit = adevrat atunci
Scrie (Am gsit , x, pe poziia , poziie )
altfel
Scrie (x, nu a fost gsit )
sfrit dac
Stop
Complexitatea este aceeai ca la algoritmul precedent: (n).
Observaie: Este esenial ca testarea i > n s se fac nainte de testarea a(i) x; dac spre
exemplu locul lor ar fi fost inversat, atunci era posibil ca s avem i = n + 1, iar condiia a(i) x
s fie testat, dei a(n + 1) nu exist.

91

(b) A doua variant: speculeaz mai inteligent faptul c irul este gata sortat. Putem mpri
irul n dou subiruri de dimensiuni ct mai apropiate. Se testeaz dac elementul de la mijloc
este egal cu x. Dac da, atunci cutarea se oprete cu succes. Dac nu, i x < elementul din
mijloc, atunci cutarea va continua s se fac n prima jumtate a irului (e clar c n a doua
jumtate nu poate fi gsit); dac x este mai mare dect elementul de la jumtate, atunci
cutarea va continua n a doua jumtate. Cutarea continu pn cnd fie se gsete x n ir, fie
nu mai avem nici un element de cercetat, caz care nseamn cutare fr succes. Acest algoritm
se numete algoritmul cutrii prin njumtire.
Start Cutarenjumtire
Citete (n, (a(i), i = 1, n), x )
gsit = fals{ ca n algoritmul anterior}
stnga = 1
dreapta = n {stnga i dreapta reprezint capetele ntre care se face cutarea lui x}
{Iniial, cutarea se face n tot vectorul}
ct timp (gsit = fals) i (stnga dreapta)
{nu l-am gsit pe x i mai avem unde cuta}
mijloc = _stnga+dreapta
{m poziionez la jumtatea irului}
dac a(mijloc) = x atunci
gsit = adevrat
altfel
dac x < a(mijloc) atunci
dreapta = mijloc 1
altfel
stnga = mijloc + 1
sfrit dac
{caut doar n jumtatea corespunztoare}
sfrit dac
sfrit ct timp
dac gsit = adevrat atunci
Scrie (Am gsit , x, pe poziia , poziie )
altfel
Scrie (x, nu a fost gsit )
sfrit dac
Stop
Complexitate: Timpul T(n) pentru rezolvarea problemei de dimensiune n satisface ecuaia:
T(n) = T(n/2)+c, c fiind o constat necesar testrilor i atribuirilor (operaii elementare, al
cror cost n parte nu depinde de n). Prin inducie complet se poate demonstra c:
T(n) = (log2(n)), adic mult mai performant dect n varianta 3.

92

4. Presupunem c minimul este primul element din ir. Lum apoi fiecare din celelalte
elemente ale vectorului a i le comparm cu minimul curent cunoscut: dac acest element este
mai mic dect minimul, atunci e clar c am descoperit un minim mai bun, deci noul minim
va fi elementul curent. Procedeul continu pn cnd se epuizeaz elementele din ir. La sfrit
minimul gsit va fi de fapt minimul absolut al irului, ceea ce trebuia determinat.
Start Minim
Citete (n, (a(i), i = 1, n) )
minim = a(1)
pentru i=2,n
dac a(i) < minim atunci
minim = a(i)
sfrit dac
sfrit pentru
Scrie (minimul este , minim )
Stop
Numrul de comparaii este n 1. Nu se poate cobor sub acesta (demonstraia se face prin
inducie).
Observaie. Exist varianta de a iniializa minim cu o valoare foarte mare, considerat +
pentru tipul de date care este folosit. De multe ori limbajele de programare pun la dispoziie
constante reprezentnd valorile cele mai mari (mici) reprezentabile pe un anumit tip de date
numeric (Pascal, C/C++, Java).
5. Vom folosi un contor (o variabil) pentru a urmri de cte ori apare minimul.
(a) Vom rezolva problema n doi pai: vom determina minimul printr-o parcurgere a irului, ca
mai sus, iar apoi vom parcurge irul nc o dat pentru a numra de cte ori a aprut acesta.
Start ContorMinim1
Citete (n, (a(i), i = 1, n) )
minim = a(1)
pentru i=2,n
dac a(i) < minim atunci
minim = a(i)
sfrit dac
sfrit pentru
contor = 0
pentru i = 2, n
dac minim = a(i) atunci
contor = contor + 1
sfrit dac
sfrit pentru
Scrie (minimul este , minim )
Scrie (apare de , contor, ori )
Stop
93

Complexitate: (n), deoarece irul se parcurge de dou ori, pentru fiecare element efectundu
se operaii elementare ce nu depind de n (dimensiunea intrrii).
(b) Vom rezolva problema printro singur parcurgere a irului; complexitatea teoretic
rmne aceeai.
Start ContorMinim2
Citete (n, (a(i), i = 1, n) )
minim = a(1)
contor = 1
pentru i = 2, n
dac minim > a(i) atunci
minim = a(i)
{am gsit un minim mai bun}
contor = 1{resetm contorul la 1}
altfel
dac minim = a(i) atunci
contor = contor + 1
{am mai gsit o valoare egal cu minimul curent}
sfrit dac
sfrit dac
sfrit pentru
Scrie (minimul este , minim )
Scrie (apare de , contor, ori )
Stop
6. Vom construi doi vectori: unul reine numerele distincte din a, iar cellalt contorizeaz
numrul de apariii pentru fiecare numr. Vom scrie un subalgoritm de tip funcie de cutare
liniar a unui element x ntr-un vector v care conine un anumit numr de elemente. Aceast
funcie returneaz poziia pe care se gsete elementul x n v sau 0 n cazul n care x nu este
cuprins n v:
CautLiniar (x, v, l)
poziie = 0
gsit = fals
i=1
repet
dac x = v(i) atunci
gsit = adevrat
poziie = i
altfel
i=i+1
sfrit dac
pn cnd (i > l) sau (gsit = adevrat)
CautLiniar = poziie

94

Return
Start CelMaiDes
Citete (n, (a(i), i = 1, n) )
k=0
{k este numrul de elemente distincte din a}
pentru i = 1, n
PozInDif =CautLiniar (a(i), diferite, k)
dac PozInDif = 0 atunci
k=k+1
{a(i) nu a mai fost gsit nainte}
{punem a(i) n vectorul diferite}
diferite(k) = a(i)
contor(k) = 1
altfel
{a(i) a mai aprut o dat nainte n a}
contor(PozInDif) = contor(PozInDif)+1
{l-am mai gsit o dat}
sfrit dac
sfrit pentru
{acum cutm n vectorul contor s vedem care numr a aprut cel mai des}
maximApariii = contor(1)
pozMax = 1
pentru i = 2, k
dac contor(i) > maximApariii atunci
maximApariii = contor(i)
pozMax = i
sfrit dac
sfrit pentru
Scrie (Numrul cel mai frecvent:,diferite(pozMax))
Scrie (Apare de , maximApariii, ori )
Stop
Complexitate: cazul cel mai defavorabil este atunci cnd avem doar elemente distincte n irul
a. n acest caz, un apel CautLiniar(a(i), diferite, k) are costul de forma a k + b, a i b
n

constante. Ciclul din programul principal duce la un timp T(n) =

(a k
k 1

Exemplu:
pentru n = 4, a = (10, 20, 30, 40) vom avea:
diferit = (10, 20, 30, 40)
contor = (1, 1, 1, 1)

95

b)

(n 2 )

k=4
cel mai frecvent: 10
frecvena: 1
7. Vom profita de faptul c vectorul dat este sortat cresctor. Vom cuta o secven de numere
constante, de lungime maxim.
Start CelMaiDes
StartSir = 1
EndSir = 1
lungimeSecventaMaxima = 0
suntInSir = adevrat
ct timp suntInSir = adevrat
ct timp (EndSir) n i (a(StartSir) = a(EndSir))
EndSir = EndSir + 1
sfrit ct timp
dac EndSirStartSir>LungSecvMax atunci
apareMaxim = a(StartSir)
LungSecvMax= EndSir StartSir
sfrit dac
StartSir = EndSir
dac StartSir > n atunci
suntInSir = fals
{am parcurs tot irul} sfrit dac
sfrit ct timp
Scrie (Numrul cel mai frecvent:,apareMaxim)
Scrie (Apare de,LungSecvMax, ori )
Stop
Complexitate: se observ c valoarea EndSir ia pe rnd valorile 1, 2, . . . , n+1; de fiecare dat
sunt efectuate operaii elementare, deci avem T(n) = (n + 1) = (n).
Observaie: Pentru a rezolva problema de la punctul anterior, am putea face o sortare a irului,
dup care s aplicm algoritmul de mai sus.
Complexitatea total ar fi: sortarea implic un timp (n log n), algoritmul anterior (n), deci
per total (n log n), sensibil mai bine dect (n2) care fusese obinut anterior.

M1.U4.3. Test de autoevaluare a cunotinelor


1. Scriei algoritmii de intrare/ieire din coada circular.
2. Scriei algoritmii iterativi pentru parcurgerea arborilor binari n inordine i postordine.
3. Se d o stiv alocat nlnuit i o valoare x. S se extrag din stiv, dac exist, elementul
cu INFO = x.

96

4. Se d o stiv i o valoare x. S se introduc n stiv dup elementul cu INFO = x un element


cu INFO = y.
5. S se implementeze o coad folosind dou stive. S se analizeze complexitatea.
6. S se implementeze o stiv prin dou cozi. S se analizeze complexitatea.
Rspunsuri:
1. Vom folosi pentru memorarea elementelor din coad un vector v de n elemente. Vom
presupune c indicii vectorului sunt ntre 0 i n 1. Deoarece coada este circular, dup locaia
de indice n 1 urmeaz locaia de indice 0. Iniial, coada este vid, fapt semnalat prin inceput
= sfarsit = 0.
x Coada
sfarsit = (sfarsit + 1)mod n
dac sfarsit = inceput atunci
Coada este plin
altfel
v(sfarsit) = x
sfrit dac
Return
x Coada
dac inceput = sfarsit atunci
Coada este vid
altfel
inceput = (inceput + 1)mod n
x = v(inceput)
sfrit dac
Return
Complexitatea fiecrui subalgoritm este (1). Iniial avem inceput =sfarsit = 0.
Observaie: Se folosesc efectiv doar n 1 locaii ale vectorului; dar n acest mod se poate
distinge ntre situaiile de coad plin i coad vid.
2. (a) Parcurgerea n inordine, iterativ:
InordineIterativ(rad)
i = rad
S = { S este o stiv}
repet
ct timp LS(i)
iS
i = LS(i)
sfrit ct timp
scrie( INFO(i) )
ct timp LD(i) =

97

dac S = atunci
Return{ se iese din subalgoritm, deci i din ciclul infinit}
sfrit dac
iS
scrie( INFO(i) )
sfrit ct timp
i = LD(i)
pn cnd fals{ciclu infinit, din care se iese datorit lui Return}
(b) Parcurgerea n postordine, iterativ:
PostordineIterativ( rad )
i = rad
S = { S este o stiv}
repet
ct timp LS(i)
iS
i = LS(i)
sfrit ct timp
ct timp LD(i) =
repet
scrie( INFO(i) )
dac S = atunci
Return{se iese din subalgoritm, deci i din ciclul infinit}
sfrit dac
j=i
iS
pn cnd j = LS(i)
sfrit ct timp
iS
i = LD(i)
pn cnd fals{ciclu infinit, din care se iese datorit lui Return}
Apelul subalgoritmilor de mai sus se face avnd drept parametru de apel rad, reprezentnd
adresa rdcinii arborelui.
Complexitatea fiecreia din proceduri este de (n), unde n este numrul de vrfuri ale
arborelui.
3. Vom extrage succesiv vrful stivei S i l vom depune ntr-o alt stiv T. Extragerile
nceteaz fie cnd S devine vid, fie cnd x este gsit. Elementele din T vor fi depuse napoi n S.
Extrage( S, x )
T=
98

gasitx = fals
ct timp S _= i gasitx = fals
a S{ scoatem vrful stivei}
dac a _= x atunci
a T{ l depunem n T}
altfel
gasitx = adevarat
sfrit dac
sfrit ct timp
ct timp T _=
aT
aS
sfrit ct timp
Return
Complexitate: cazul cel mai defavorabil este acela n care x nu se gsete n S. n acest caz, tot
coninutul lui S este trecut n T i apoi repus n S. Complexitatea este deci (|S|), unde |S|
reprezint numrul de elemente aflate iniial n S.
Observaie: Primul ciclu ct timp poate fi scris mai adecvat ca un ciclu repet, avnd n vedere
faptul c execuia ciclui se face cel puin o dat.
4. Rezolvarea este asemnatoare cu cea de la punctul precedent.
Insereaza( S, x, y )
T=g
gasitx=fals
ct timp S _= i gasitx = fals
a S{ scoatem vrful stivei}
dac a _= x atunci
a T{ l depunem n T}
altfel
gasitx = adevarat
aS
sfrit dac
sfrit ct timp
dac gasitx = adevarat atunci
yS
sfrit dac
ct timp T _=
aT

99

aS
sfrit ct timp
Return
Complexitatea este (|S| + 1) = (|S|), motivaia fiind asemntoare
cu cea de la punctul precedent.
5. Vom folosi dou stive: Stiva1, Stiva2.
x Coada
dac Stiva1 = atunci
Goleste(Stiva2, Stiva1){Golete elementele din Stiva2 n Stiva1}
sfrit dac
x Stiva1
Return
x Coada
dac Stiva2 = atunci
Goleste(Stiva1, Stiva2){Golete elementele din Stiva1 n Stiva2}
sfrit dac
dac Stiva2 = atunci
Coada este vida
altfel
x Stiva2
sfrit dac
Return
Subalgoritmul Goleste(A,B), unde A i B sunt stive, va trece toate elementele din A n B, prin
extrageri repetate.
Goleste(A,B)
ct timp A _=
xA
xB
sfrit ct timp
Return
Numrul de operaii de inserare i extragere n fiecare caz zunt:
(a) Subalgoritmul Goleste(A,B) efectueaz 2|A| extrageri (|A| este numrul de elemente din
stiva A);
(b) Subalgoritmul xCoada efectueaz 2|Stiva2|+1=2|Coada|+1operaii de inserare/extragere;
(c) Subalgoritmul xCoada efectueaz 2|Stiva1|+1=2|Coada|+1 operaii de inserare/extragere;
6. Vom folosi dou cozi: Coada1, Coada2.
x Stiva

100

x Coada1
Return
x Stiva
dac Coada1 = atunci
eroare: stiva vid
altfel
ct timp Coada1
x Coada1
dac Coada1 atunci {x nu este ultimul element din Coada1}
x Coada2
sfrit dac
sfrit ct timp{Golete Coada2 n Coada1}
ct timp Coada2
y Coada2
y Coada1
sfrit ct timp
sfrit dac
Return
Numrul de operaii de inserare/extragere pentru fiecare din cei doi subalgoritmi sunt:
(a) Subalgoritmul x Stiva efectueaz o inserare
(b) Subalgoritmul x Stiva are numrul de operaii cerut egal cu 2(|Coada1| 1) + 1 (primul
ciclu) adunat cu 2(|Coada1| 1), n total 4|Coada1| 3 = 4|Stiva| 3.
Sugerm cititorului s gseasc i alte solucii pentru aceast problem.
7. Algoritmuii recursivi pentru parcurgeri sunt urmtorii:
(a) Parcurgerea n preordine se efectueaz astfel: se prelucreaz informaia rdcinii, apoi se
prelucreaz recursiv subarborele stng, apoi subarborele drept.
Preordine(Rad)
dac Rad atunci
scrie( Info(Rad) ){Sau orice alt subalgoritm de prelucrare a informaiei
din Rad}
Preordine(LS(Rad))
Preordine(LD(Rad))
sfrit dac
Return
Apelul acestui subalgoritm se face cu Preordine(Rad), unde Rad reprezint rdcina
subarborelui.
(b) Parcurgerea recursiv n postordine se efectueaz astfel: se parcurge recursiv subarborele
stng, apoi subarborele drept, apoi se prelucreaz informaia din rdcin.

101

Postordine(Rad)
dac Rad atunci
Postordine(LS(Rad))
Postordine(LD(Rad))
scrie( Info(Rad) ){Sau orice alt subalgoritm de prelucrare a informaiei din Rad}
sfrit dac
Return
Apelul se face ce n cazul anterior.
(c) Parcurgerea recursiv n inordine se efectueaz astfel: se parcurge recursiv subarborele
stng, apoi se prelucreaz informaia din rdcin, apoi se parcurge recursiv subarborele drept.
Inordine(Rad)
dac Rad atunci
Inordine(LS(Rad))
scrie( Info(Rad) ){Sau orice alt subalgoritm de prelucrare a informaiei din Rad}
Inordine(LD(Rad))
sfrit dac
Return
Apelul se face ce n cazul anterior

M1.U5.3. Test de autoevaluare a cunotinelor


1. S se calculeze recursiv maximul unui ir.
2. S se calculeze recursiv suma elementelor dintrun ir.
3. S se calculeze puterea a na a unui numr real a, cu o complexitate
mai bun dect (n).
4. S se dea un contraexemplu pentru care algoritmul de la 3 nu d
numrul optim de nmuliri.
5. S se scrie un algoritm care calculeaz termenul fibo(n) al irului Fibonacci n timp mai bun
de (n) (a se veda i problema 3).
6. S se dea algoritmi recursivi pentru parcurgerea n preordine, postordine, inordine a unui
arbore binar.
Problema 1. Definim maximul unui ir x = (x(1), x(2), . . . , x(n)) sub form recursiv astfel:
maxim(x(1 . . . n)) = maxim(x(1 . . . n)) = _ x(1), dac n=1
maxim(x(1 . . . n)) = max{x(n), x(1 . . . n 1)}, dac n > 1
unde max{a, b} este maximul dintre a i b. Algoritmul este dat mai jos:
Start MaximRec
citete( n, (x(i), i = 1, n) )
M = Maxim(x, n)
scrie( M )
Stop
unde subalgoritmul recursiv Maxim este:
Maxim(x, n)
dac n = 1 atunci
MaximRec = x(1)
102

altfel
MaximRec = max{x(n),Maxim(x, n 1)} sfrit dac
Return
Complexitatea este dat de ecuaia T(n) = T(n1)+c care are soluia
T(n) = (n).
2. Problema 2. Definim suma elementelor unu ir x = (x(1), . . . , x(n)) sub form recursiv
astfel:
suma(x(1 . . . n)) = _ 0, dac n=0
suma(x(1 . . . n)) = x(n) + suma(x(1 . . . n 1)), dac n > 0
Start SumaRec
citete( n, (x(i), i = 1, n) )
S = Suma(x, n)
scrie( S )
Stop
unde subalgoritmul recursiv Suma este:
Suma(x, n)
dac n = 0 atunci
150
Suma = 0
altfel
Suma = x(n) + Suma(x, n 1)
sfrit dac
Return
Complexitatea este dat de ecuaia T(n) = T(n1)+c care are soluia
T(n) = (n).
3. Problema 3. Vom da o formul recursiv de calcul a puterii naturale a unui numr.
an =1, dac n = 0
an =a, dac n = 1
an =(an/2)2 , dac n > 1, n par
an =a*(an/2)2 , dac n > 1, n impar
Subalgoritmul recursiv este:
Putere(a, n)
dac n = 0 atunci
Putere = 1
Return
sfrit dac
dac n = 1 atunci
Putere = a
Return
sfrit dac
temp = Putere(a, n/2)
dac n mod 2 = 0 atunci {n este par}
Putere = temp
altfel
Putere = a*temp
sfrit dac

103

Return
Complexitatea este dat de ecuaia T(n) = T(n/2)+c, de unde T(n) = (log n)
Problema 4. Pentru a15, conform algoritmului precedent, avem:
a15 = (an/2)**a=a7*a=(a3)2*a=((a)2*a)2*a
deci n total 4 nmuliri.
Altfel, evident avem 6 nmuliri.
Problema 5.
Se va aplica o metod de tabelare a rezultatelor.
6. Problema 6. Algoritmuii recusrivi pentru parcurgeri sunt urmtorii:
(a) Parcurgerean preordine se efectueaz astfel: se prelucreaz informaia rdcinii, apoi se
prelucreaz recursiv subarborele stng, apoi subarborele drept.
Preordine(Rad)
dac Rad _= atunci
scrie( Info(Rad) ){Sau orice alt subalgoritm de prelucrarea a informaiei din Rad}
Preordine(LS(Rad))
Preordine(LD(Rad))
sfrit dac
Return
Apelul acestui subalgoritm se face cu Preordine(Rad), unde Rad
reprezint rdcina subarborelui.
(b) Parcurgerea recursiv n postordine se efectueaz astfel: se parcurge recursiv subarborele
stng, apoi subarborele drept, apoi se prelucreaz informaia din rdcin.
Postordine(Rad)
dac Rad _= atunci
Postordine(LS(Rad))
Postordine(LD(Rad))
scrie( Info(Rad) ){Sau orice alt subalgoritm de prelucrarea a informaiei din Rad
sfrit dac
Return
Apelul se face ce n cazul anterior.
(c) Parcurgerea recursiv n inordine se efectueaz astfel: se parcurge recursiv subarborele
stng, apoi se prelucreaz informaia din rdcin, apoi se parcurge recursiv subarborele drept.
Inordine(Rad)
dac Rad _= atunci
Inordine(LS(Rad))
scrie( Info(Rad) ){Sau orice alt subalgoritm de prelucrarea a informaiei din Rad}
Inordine(LD(Rad))
sfrit dac
Return
Apelul se face ce n cazul anterior.

M2.U1.3. Test de autoevaluare a cunotinelor


104

1. Scriei un algoritm pentru parcurgerea tablei de ah cu un cal (calul sare pe diagonal unui
dreptunghi cu laturile de un ptrat i dou patrate). Fiecare ptrat al tablei trebui s fie vizitat
exact o singur dat.
2. Avnd 4 culori i o hart cu n ri (dat print-o matrice de adiacen:
aij = 1 dac ara i este vecin cu ara j, 0 altfel), s se coloreze harta astfel ca dou ri vecine
s nu aibe aceeai culoare (dou ri se consider a fi vecine dac au o frontier comun).
3. O organizaie are n componena sa n brbai i m femei. S se scrie un algoritm care listeaz
toate modalitile n care se poate alctui o delegaie care s conin cel puin k femei, k <m.
4. Cum poate fi pltit o sum de x lei, n bancnote de valoare v(i), i = 1, n din care avem cte
b(i) buci? S se dea toate soluiile posibile.
Rspunsuri:
1. Vom da dou variante de rezolvare: iterativ i recursiv. Ambele variante vor folosi doi
vectori de srituri: sarituraI i sarituraJ cu cte 8 elemente, reprezentnd cele 8 deplasri de la
poziia curent: de exemplu, dac poziia curent este pe linia i i coloana j, atunci prima
sritur va duce calul la coordonatele (i+sarituraI(1), j+sarituraJ(1) (cu condiia s nu se ias
din perimetrul tablei).
(a) Varianta iterativ: pentru refacerea pasului napoi vom folosi o matrice ptratic deLa de
ordinul n, care va reine pentru fiecare celul de coordonate (i, j) indicele sriturii care a
determinat atingerea poziiei respective (vom putea ti astfel de unde sa srit n poziia
curent). Soluia se d sub forma unei matrice ptratice de ordinul n coninnd n fiecare celul
de coordonate (i, j) indicele sriturii ce se face n continuare (cu excepia ultimei celule n care
se sare).
Start CalSah
citete( n )
sarituraI(1) = +1, sarituraJ(1) = +2
sarituraI(2) = 1, sarituraJ(2) = +2
sarituraI(3) = +1, sarituraJ(3) = 2
sarituraI(4) = 1, sarituraJ(4) = 2
sarituraI(5) = +2, sarituraJ(5) = +1
sarituraI(6) = 2, sarituraJ(6) = +1
sarituraI(7) = +2, sarituraJ(7) = 1
sarituraI(8) = 2, sarituraJ(8) = 1
pentru i = 1, n
pentru j = 1, n
deLa(i, j) = 0
tabla(i, j) = 0
sfrit pentru
sfrit pentru
nrSaritura = 1
i=j=1

105

ct timp nrSaritura > 0


dac nrSaritura = n2 atunci
ScrieSolutie(tabla, n)
indiceSarituraInapoi = deLa(i, j)
tabla(i, j) = 0
i = i sarituraI(indiceSarituraInapoi)
j = j sarituraJ(indiceSarituraInapoi)
nrSaritura = nrSaritura 1
sfrit dac
dac tabla(i, j) < 8 atunci
tabla(i, j) = tabla(i, j) + 1
iInainte = i + sarituraI(tabla(i, j))
jInainte = j + sarituraJ(tabla(i, j))
dac PoateSari(iInainte, jInainte, tabla, n)=adevarat atunci
deLa(iInainte, jInainte) = tabla(i, j)
i = iInainte
j = jInainte
nrSaritura = nrSaritura + 1
sfrit dac
altfel
dac nrSaritura > 1 atunci
iInapoi = i sarituraI(deLa(i, j))
jInapoi = j sarituraJ(deLa(i, j))
tabla(i, j) = 0
i = iInapoi
j = jInapoi
sfrit dac
nrSaritura = nrSaritura 1
sfrit dac
sfrit ct timp
Stop
ScrieSolutie(tabla, n)
pentru i = 1, n
pentru j = 1, n
scrie( tabla(i, j) )
sfrit pentru
sfrit pentru
Return
PoateSari(linie, coloana, tabla, n)

106

dac linie > 0 i linie < n + 1 i coloana > 0 i coloana < n + 1 i


tabla(linie, coloana) = 0 atunci
rezultat = adevarat
altfel
rezultat = fals
sfrit dac
PoateSari = rezultat
Return
(b) Varianta recursiv
Cal(i, j, n, nrSaritura, tabla)
dac nrSaritura = n n atunci
ScrieSolutie(tabla, n)
altfel
pentru indiceSaritura = 1, 8
iInainte = i + sarituraI[indiceSaritura]
jInainte = j + sarituraJ[indiceSaritura]
dac PoateSari(iUrmator, jUrmator, tabla, n)=adev atunci
tabla[iInainte, jInainte] = nrSaritura + 1
Cal(iInainte, jInainte, n, nrSaritura + 1, tabla)
tabla[iInainte, jInainte] = 0
sfrit dac
sfrit pentru
sfrit dac
Return
Subalgoritmii ScrieSolutie i PoateSari sunt identici cu cei de la varianta iterativ.
Start CalSah
citete( n )
sarituraI(1) = +1, sarituraJ(2) = +2
sarituraI(2) = 1, sarituraJ(2) = +2
sarituraI(3) = +1, sarituraJ(3) = 2
sarituraI(4) = 1, sarituraJ(4) = 2
sarituraI(5) = +2, sarituraJ(5) = +1
sarituraI(6) = 2, sarituraJ(2) = +1
sarituraI(7) = +2, sarituraJ(2) = 1
sarituraI(1) = 2, sarituraJ(2) = 1
pentru i = 1, n
pentru j = 1, n
tabla(i, j) = 0

107

sfrit pentru
sfrit pentru
tabla(1, 1) = 1
Cal(1, 1, n, 1, tabla)
Stop
2. Sa demonstrat cu ajutorul calculatorului c 4 culori sunt suficiente pentru a colora cu
restriciile date orice hart. Vom da dou variante, iterativ i recursiv. n ambele variante
vectorul culoare de n elemente va conine culorile rilor.
(a) Varianta iterativ.
Start ColorareHarti
citete( n )
citete( ((a(i, j), j = 1, n), i = 1, n) )
pentru i = 1, n
culoare(i) = 0
sfrit pentru
numarTara = 1
ct timp numarTara > 0
dac numarTara = n + 1 atunci
scrie( (culoare(i), i = 1, n) )
numarTara = numarTara 1
sfrit dac
dac culoare(numarTara) < 4 atunci
culoare(numarTara) = culoare(numarTara) + 1
dac BineColorat(a, numarTara, culoare) = adev atunci
numarTara = numarTara + 1
sfrit dac
altfel
culoare(numarTara) = 0
numarTara = numarTara 1
sfrit dac
sfrit ct timp
Stop
Subalgoritmul BineColorat(vecin, numarTara, culoare) returneaz adevrat dac culoarea rii
curente (numarTara)satisface restriciile cerute fa de rile anterior colorate.
BineColorat(a, numarTara, culoare)
corect = adevarat
i=1
ct timp corect = adevarat i i < numarTara
dac a(i, numarTara) = 1 i culoare(i) = culoare(numarTara) atunci
corect = fals

108

altfel
i=i+1
sfrit dac
sfrit ct timp
BineColorat = corect
Return
(b) Varianta recursiv.
Coloreaza(a, n, culoare, numarTara)
dac numarTara = n + 1 atunci
scrie( (culoare(i), i = 1, n) )
altfel
pentru k = 1, 4
culoare(numarTara) = k
dac BineColorat(a, numarTara, culori)= adev atunci
Cheama Coloreaza(a, n, culoare, numarTara + 1)
sfrit dac
sfrit pentru
sfrit dac
Return
Subalgoritmul BineColorat este identic cu cel de la varianta iterativ.
Utilizarea subalgoritmului recursiv se face astfel:
Start ColorareHarti
citete( n )
citete( ((vecin(i, j), j = 1, n), i = 1, n) )
Cheama Coloreaza(vecin, n, culoare, 1)
Stop
3. Vom nota brbaii cu b1, . . . , bn iar femeile cu f1, . . . , fm. Vom genera delegaiile folosind
doi vectori, unul pentru indicii de brbai, cellalt pentru indicii de femei (vectorii vb, respectiv
vf). Fiecare vector are o componennt de indice 0 care va avea permanent valoarea 0. n
vectorul vf vom avea generate p componente (k p m), iar n vectorul vb vom avea q
elemente (0 q n); varianta q = 0 exist deoarece se pot forma delegaii n care s nu existe
nici un brbat.
Varianta dat mai jos este recursiv. Pentru subalgoritmul GenerareDelegatii, parametrii fc i
bc reprezint indicele de vector vf, respectiv vb pentru care se efectueaz alegerea unei femei,
respectiv brbat.
GenerareDelegatii(p, fc,m, q, bc,n, vf, vb)
dac fc = p + 1 atunci {am generat toi indiciii pentru femei}
dac bc = q + 1 atunci {am generat toi indiciii pentru brbai}
scrie( (vf(i), i = 1, p) )
scrie( (vb(i), i = 1, q) )

109

altfel
pentru i = vb(bc 1) + 1, n q + bc
vb(bc) = i
Cheama GenerareDelegatii(p, fc,m, q, bc + 1, n, vf, vb)
sfrit pentru
sfrit dac
altfel
pentru i = vf(fc 1) + 1,m p + fc
vf(fc) = i
Cheama GenerareDelegatii(p, fc + 1,m, q, bc,n, vf, vb)
sfrit pentru
sfrit dac
Return
Start Delegatii
citete( m, n, k )
vf(0) = vb(0) = 0
pentru p = k,m
pentru q = 0, n
Cheama GenerareDelegatii(p, 1,m, q, 1, n, vf, vb)
sfrit pentru
sfrit pentru
Stop
4. Vom da mai jos o variant recursiv. Considerm rest ca fiind restul care mai este de pltit,
solutie un vector cu n elemente, 0 solutie(i) b(i), bc tipul bancnotei curente.
Plata(rest, n, solutie, bc)
dac rest = 0 atunci
scrie( (solutie(i), i = 1, bc 1) )
altfel
dac rest > 0 i bc n atunci
pentru k = 0, b(bc)
solutie(bc) = k
Cheama Plata(rest k v(bc), solutie, bc + 1)
sfrit pentru
sfrit dac
sfrit dac
Return
Start PlataSuma
citete( n, S )
citete( (b(i), i = 1, n) )
citete( (v(i), i = 1, n) )

110

Cheama Plata(S, n, solutie, 1)


Stop
Observaie Orice problem cu datele ca mai sus poate fi transformat ntruna n care vectorul
b conine doar valoarea 1, prin crearea unui vector v_ care va conine valoarea v(i) de b(i) ori.

M2.U2.3. Test de autoevaluare a cunotinelor


1. S se scrie algoritmi recursivi pentru generarea tipurilor de submulimi
din acest capitol.
Rspunsuri:
1. (a) Produs cartezian:
CartezianRecursiv(m, n, k, x)
dac k = n + 1 atunci
Cheama Afisare(x,m){sau orice subalgoritm care prelucreaz informaia obinut}
altfel
pentru p = 1, n(k)
x(k) = p
Cheama CartezianRecursiv(m, n, k + 1, x)
sfrit pentru
sfrit dac
Return
Subalgoritmul Afisare este:
Afisare(a, l)
pentru i = 1, l
scrie( a(i) )
sfrit pentru
Return
Programul principal este:
Start Cartezian
citete( m, (n(i), i = 1,m) )
Cheama CartezianRecrsiv(m, n, 1, x)
Stop
Subalgoritmul recursiv genereaz soluiile n ordine lexicografic.
(b) Submulimi:
Vom folosi un subalgoritm recursiv cu forma: SubmultimiRecursiv(n, k, cate, x) unde n este
dat de intrare reprezentnd numrul elementelor mulimii iniiale, k este indicele curent pentru
care se ncearc generarea, cate reprezint numrul de elemente
ale submulimii care se genereaz (1 k cate, pentru cate > 0), iar x este vectorul soluie.
Vom considera c x are un elemente de indice 0 care va avea permanent valoarea 0.

111

SumbultimiRecursiv(n, k, cate, x)
dac k > cate atunci
Cheama ScrieSolutie(x, cate){sau orice subalgoritm care prelucreaz
informaia dat}
altfel
pentru p = x(k 1), n cate + k
x(k) = p
Cheama SubmultimiRecursiv(n, k + 1, cate, x)
sfrit pentru
sfrit dac
Return
Subalgoritmul ScrieSolutie este o variatie a celui anterior, care n plus ia n considerare cazul
ncare s-a generat mulimea vid:
ScrieSolutie(a, l)
dac l = 0 atunci
scrie( multimea vida )
altfel
pentru i = 1, l
scrie( a(i) )
sfrit pentru
sfrit dac
Return
Subalgoritmul principal este:
Start Submultimi
citete( n )
x(0) = 0
pentru cate = 0, n
Cheama SubmultimiRecursiv(n, 1, cate, x)
sfrit pentru
Stop
Subalgoritmul recursiv genereaz submulimile n ordine lexicografic.
(c) Combinri:
Subalgoritmul este acelai cu cel de la generarea recursiv a submulimilor, cu acceeai
semnificaie a parametrilor de apel. Programul principal este:
Start Combinari
citete( n,m )
x(0) = 0
Cheama CombinariRecursiv(n, 1,m, x)
Stop
Subalgorimul recursiv genereaz combinrilen ordine lexicografic.

112

(d) Permutri:
PermutariRecursiv(k, x, n)
dac k = 0 atunci
Cheama ScrieSolutie(v, n)
altfel
pentru i = 1, k
v(k) v(i)
Cheama PermutariRecursiv(k 1, x, n)
v(k) v(i)
sfrit pentru
sfrit dac
Return
Subalgoritmul de ScrieSolutie este cel de la produs cartezian.
Programul principal ar fi:
Start Permutari
citete( n )
pentru i = 1, n
v(i) = i
sfrit pentru
Cheama PermutariRecursiv(n, x, n)
Stop
Subalgoritmul recursiv nu genereaz permutrile n ordine lexicografic. Sugerm cititorului
scrierea unei variante bazate pe backtracking recursiv, care s genereze permutrile n ordine
lexicografic

M2.U3.3. Test de autoevaluare a cunotinelor


1. Baznduv pe observaia c mutarea din mijloc se tie, scriei un algoritm iterativ pentru
problema turnurilor din Hanoi, punnd mereu mijloacele intervalelor obinute prinnjumtire
ntr-un ir cu numrul cunoscut de mutri.
2. Avnd la intrare un ir ordonat cresctor, s se scrie un algoritm de tip Divide et Impera
pentru gsirea poziiei unui element de valoare dat x, dac el exist. Se va folosi metoda
njumtirii intervalului dat de numrul de elemente al irului. Care este complexitatea
cutrii?
3. Rezolvai aceeai problem prin cutare secvenial (testarea pe rnd a elementelor). Care
este n acest caz complexitatea?
4. Demonstrai c timpul de execuie al algoritmului QuickSort, n cazul unui vector cu toate
elementele egale, este (n log n).

113

5. Complexitatea algoritmului QuickSort este mare din cauz c n cazuri defavorabile,


partiionarea se face cu un singur element ntr-una din partiii. Rescriei algoritmul de
partiionare, lund ca element de separare nu primul element, ci valoarea din mijloc ntre
primul element, ultimul i cel aflat pe poziia din mijloc.
Rspunsuri:
1. Vom proceda n felul urmtor: dac trebuie s umplem un vector al crui capt stng este st
i cel drept este dr, atunci la mijloc se pune perechea (i, j), dup care se completeaz jumtatea
stng a vectorului, urmat de cea dreapt. Varianta de mai jos este iterativ, folosind o stiv
pentru simularea recursivitii. Soluiile vor fi memorate ntro matrice cu 2n 1 linii i 2
coloane.
Start Hanoi
citete( n )
i=1
j=2
st = 1
dr = 2n 1
S = {S este stiv}
(i, j, st, dr) S
ct timp S _=
(i, j, st, dr) S
sol( st+dr2 , 1) = i
sol( st+dr2 , 2) = j
dac st st+dr2 1 atunci
(i, 6 i j, st, st+dr2 1) S
sfrit dac
dac st+dr2 + 1, dr atunci
(6 i j, j, st+dr2 + 1, dr) S
sfrit dac
sfrit ct timp
scrie( (sol(i, 1), sol(1, 2), i = 1, 2n 1) )
Stop
Adncimea stivei S este (log2(2n1)) = (n), iar complexitatea este evident (2n).
2. Complexitatea obinut este T(n) = (log n).
4. La fiecare pas din ciclul repet al algoritmului de pariionare i crete cu o unitate, iar j scade
cu o unitate. Pariionarea se oprete atunci cnd j este la jumtatea irului, deci mprirea este
echilibrat. Ca atare, complexitatea este Dat de T(n) = T(n/2) + c n, de unde T(n) = (n
logn).
5. Vom crea o funcie care determin dac un numr este sau nu situat ntre alte dou numere:
Intre(x, y, z){returneaz adevarat dac x este ntre y i z, fals altfel}

114

dac (y x i x z) sau (z x i x y) atunci


Intre = adevarat
altfel
Return fals
sfrit dac
Algoritmul de partiionare modificat este:
Partitie(a, p, q)
r = p+q
dac Intre(a(p, a(q), a(r))) atunci
indiceMediana = p
sfrit dac
dac Intre(a(q), a(p), a(r)) atunci
indiceMediana = q
sfrit dac
dac Intre(a(r), a(p), a(q) atunci
indiceMediana = r
sfrit dac
dac p indiceMediana atunci
a(p) a(indiceMediana)
sfrit dac
pivot = a(p)
i=p1
j=r+1
terminat = fals
repet
repet
j=j1
pn cnd a(j) pivot
repet
i=i+1
pn cnd a(i) pivot
dac i < j atunci
a(i) a(j)
altfel
k=j
terminat = adevarat
sfrit dac
pn cnd terminat = adevarat
Return

115

M2.U4.3. Test de autoevaluare a cunotinelor


1. Descriei un algoritm care, fiind dat o mulime S de n numere ntregi i distincte i un ntreg
pozitiv k n, determin cele k numere care sunt cele mai apropiate de mediana lui S.
2. Fie a[1 . . . n], b[1 . . . n] dou iruri sortate. Scriei un algoritm performant care gsete
mediana celor 2n numere.
3. Fiind dat o mulime de n numere, dorim s gsim cele mai mari i numere n ordinea sortat,
folosind un algoritm bazat pe comparaii. S se scrie mai multe variante i s se compare din
punct de vedere al complexitii.
4. S se modifice algoritmul CountSort astfel nct s sorteze i iruri coninnd numere ntregi
negative.
Rspunsuri
1. Rezolvarea problemei se face n doi pai:
(a) Se determin mediana irului iniial x = (x(1), . . . , x(n)); fie ea medx
(b) Se calculeaz irul auxiliar y = (y(i), i = 1, n) unde y(i) = |x(i) medx|
(c) Se determin a k-a statistic de ordine a irului y; fie ea medy
(d) Se determin k elemente mai mici sau egale cu medy; indicii acestor
elemente sunt indicii elementelor cutate din irul x.
Complexitatea fiecrui pas de mai sus este (n), deci complexitatea
algoritumului schiat este (n). Subalgoritmul n pseudocod este:
Start Apropiate
citete( n, k, (x(i), i = 1, n) )
Cheama Selectie(x, 1, n, k,medx)
Cheama ObtineY(x, n, y, medx)
Cheama Selectie(x, 1, n, k,marginey)
Cheama Vecini(x, y, n, marginey, z)
Stop
Subalgoritmul Selectie este cel care determin a k-a statistic de ordine a unui ir (cu
complexitate liniar pentru cazul cel mai defavorabil); subalgoritmul ObtineY este:
ObtineY(x, n, y, medx)
pentru i = 1, n
y(i) = |x(i) medx|
sfrit pentru
Return
Subalgoritmul Vecini va determina cei mai apropiai k vecini ai irului x fa de mediana sa:
Vecini(x, y, n, marginey, z)
lungZ = 0

116

pentru i = 1, n
dac y(i) marginey atunci
lungZ = lungZ + 1
z(lungZ) = x(i)
sfrit dac
sfrit pentru
Return
2. Vom folosi faptul c cele dou iruri sunt sortate i au aceeai lungime. De asemenea ne
folosim de observaia evident c mediana unui ir sortat se determin n timp constant: dac i
este indicele primului element din ir, iar j este indicele ultimului element al irului, atunci
mediana este pe poziia k = (i+j)/2
Pentru dou iruri A(iA . . . jA) i B(iB . . . jB) cu acelai numr de elemente
(jAiA = jBiB) procedm n felul urmtor: dac lungimile lor sunt mai mici sau egale cu 2,
atunci le putem interclasa i obinem mediana lor (timp (1)). Dac numrul de elemente este
mai mare dect 2, atunci fie kA respectiv kB indicii mijloacelor vectorilor A(iA . . . jA) i B(iB . . .
jB). Dac A(kA) < B(kB) atunci vom ignora elementele din A(iA . . . jA) aflate la stnga indicelui
kA i elementele din B(iB . . . jB) aflate la dreapta indicelui kB i vom considera doar vectorii
A(kA . . . jA) i B(kB . . . jB). Analog se procedeaz dac A(kA) B(kB). Procesul se reia pentru
vectorii njumtii.
Justificm faptul c prin eliminarea unei jumti din fiecare vector nu se pierde mediana
cutat. Fie de exemplu A(kA) < B(kB). Fie n = jAiA = jBiB. Fie i astfel nct iA i < kA.
Atunci A(i) A(kA) A(p) p = kA, . . . jA, A(i) A(kA) < B(kB) B(q) q = kB, . . . jB.
Obinem c A(i) este mai mic sau egal dect 2(n

n 1
2

1 Dar 2(n

n 1
2

1 n+1, deci

A(i) este mai mic sau egal dect cel puin n + 1 elemente. Ca atare, ignorndule, nu pierdem
mediana. Analog se demonstreaz c eliminnd jumtatea dreapt a lui B nu se pierde mediana.
Demonstraia este asemntoare pentru cazul n care A(kA) B(kB).
Algoritmul este dat mai jos:
Start MedianaSiruri
citete( n, (A(i),B(i), i = 1, n) )
iA = iB = 1
jA = jB = n
ct timp iA < iB
kA = (iA+jA)/2
kB =( iB+jB)/2
n = n/2
dac A(kA) < B(kB) atunci
iA = kA
jB = kB
altfel

117

jA = kA
iB = kB
sfrit dac
sfrit ct timp
scrie( max(A(iA),B(iB)) )
Stop
unde max returneaz maximul a dou numere. Complexitatea este:
T(n) = T(n/2) + (1) = (log n)
3. Se poate proceda n mai multe moduri:
(a) Sortm numerele i alegem cele mai mari i numere
(b) Construim un (max)heap i extragem din el de i ori maximul
(c) Prin utilizarea unui algoritm de statistic de ordine
Lsm n seama cititorului scrierea algoritmulor pentru cazurile 3a i 3b. Pentru situaia 3c
determinm a n i + 1a statistic de ordine (fie ea x) i apoi printro parcurgere a irului
numerele mai mari sau egale cu x. Complexitatea este de (n + i log i). Sugerm cititorului
compararea acestei complexiti cu cea obinut la punctele 3a i 3b.
4. Ideea este de a deplasa elementele dea lungul axei astfel nct s devin toate pozitive
(caz pentru care algoritmul Countsort funcioneaz). Aceast deplasare se obine adunnd o
cantitate potrivit, care poate fi luat ca min x(i) . Dup ce se efectueaz sortarea, se
i 1, n

revine la valorile originale prin scderea cantitii adunate anterior.


Start CountSortModificat
citete( n, (x(i), i = 1, n) )
min = MinimSir(x, n){Functie care determina minimul unui sir}
pentru i = 1, n
x(i) = x(i) + (min)
sfrit pentru
Cheama CountSort
pentru i = 1, n
y(i) = y(i) (min)
sfrit pentru
Stop

M2.U5.3. Test de autoevaluare a cunotinelor


1. Dac x = (x(i), i = 1,m), y = (y(i), i = 1, n) reprezint dou mulimi de elemente, atunci s se
determine mulimea intersecie z = (z(i), i =1, k).
2. Dnduse n obiecte de cost c = (c(i), i = 1, n) cu greutatea g =(g(i), i = 1, n) i un rucsac de
capacitate maxim G, s se elaboreze un algoritm de umplere a rucsacului de cost maxim. Un
obiect poate fi pus n rucsac ntrun anumit procent (problema continu a rucsacului).
118

3. ntro sal, ntro zi trebuie planificate n spectacole. Pentru fiecare spectacol se cunoate
intervalul n care se desfoar: [st, sf). Se cere s se planifice un numr maxim de spectacole
astfel nct s nu se suprapun..
4. Se dau n numere ntregi nenule b1, . . . , bn i m numere ntregi nenule a1, ..., am, n m. S
se determine o submulime a mulimii B = {b1, ..., bn} care s maximizeze valoarea expresiei:
E = a1*x1 + a2*x2 + + am*xm unde xi B..
5. O staie de servire trebuie s satisfac cererile a n clieni. Timpul de servire pentru fiecare
client este cunoscut: pentru clientul i timpul este ti. S se minimizeze timpul total de ateptare
n

(timpul de ateptare pentru clientul i)

i 1

Rspunsuri.
1. Pentru fiecare element x(i) se determin dac apare i n irul y. n caz afirmativ, x(i) va face
parte i din mulimea intersecie. Faptul c n acest fel se obine mulimea intersecie este
(sperm) evident. Complexitatea algoritmului este dependent de modul n care se face
cutarea lui x(i) n y.
Dac se face cutare liniar atunci complexitatea este (mn)
Dac se cauta binar, atunci:
y trebuie sortat n prealabil, deci avem o complexitate (mlog2m)
x(i) este cutat binar n y, pentru i = 1, n, deci se mai adaug complexitatea (n log2m)
n total se obine o complexitate de ((m + n) logm). Este mai avantajos ca s se fac sortarea
i cutarea n irul cel mai scurt. Lsm cititorul s conceap algotritmul pentru rezolvarea
acestei probleme. n ceea ce privete funcia care se optimizeaz, este vorba de: max f(x, y) =
|{a : a n x i a n y}| (|A| reprezint numrul de elemente ale mulimii A), care se regeste i
n algoritmul prezentat (cu toate c nu este evident).
2. Ideea algoritmului este de a calcula pentru fiecare obiect
valoarea lui unitar, i.e. ct se ctig din transportarea unei uniti din obiectul respectiv.
Suntem mai interesai n a considera obiectele profitabile (pre mare, greutate mic) naintea
celor pentru care raportul pre/greutate este mic. Algoritmul dat mai jos sintetizeaz aceast
strategie.
Start RucsacContinuu
citete( n, G, (c(i), g(i), i = 1, n) )
pentru i = 1, n
v(i) = c(i)/g(i)
sfrit pentru
Cheama Sortare(c, g, v, n)
gc = 0{gc reprezint greutatea curent pus n rucsac}
k = 1{k este numrul obiectului curent care se ncearc a fi pux n rucsac}
S = 0{S este suma realizat prin luarea obiectelor}
ct timp k n i gc < G
dac gc + g(k) G atunci

119

f(k) = 1{f(k) reprezint fraciunea care se ia din obiectul k}


gc = gc + g(k)
S = S + c(k)
k=k+1
altfel
f(k) = (Ggc)/g(k)
gc = G
S = S + f(k)g(k)
sfrit dac
sfrit ct timp
dac k > n atunci
k = n{n cazul n care G este mai mare sau egal cu greutatea tuturor obiectelor}
sfrit dac
pentru i = 1, k
scrie( f(i) )
sfrit pentru
Stop
Subalgoritmul Sortare(c, g, v, n) rearanjeaz elementele vectorilor c, g, v
astfel nct v(1) v(2) v(n). Corectitudinea algoritmului de
mai sus este dat de propoziia de mai jos:
Propozitia 1. Algoritmul precedent determin ctigul maxim n restriciile impuse problemei.
Demonstraie. Dac G >

g (i) , atunci n mod evident soluia dat de vectorul f va avea forma

i 1

f = (1, 1, . . . , 1) (se iau toate obiectele), ceea ce evident nseamn optim. Vom considera deci
cazul n care
n

G<

g (i) . Este uor de vzut c soluia dat de vectorul f are forma:

i 1

f = (1, 1, . . . , 1, , 0, 0, . . . 0)
(B.1)
unde 0 < 1 i apare pe poziia k (k fiind variabila din algoritm).
Avem c:S =

(B.2)

f (i )c(i )

i 1

i
G=

(B.3)

f (i )c(i )

i 1

Fie S_ ctigul maxim care se poate realiza n condiiile problemei i fie


F = (f(1), . . . , f(n))
(B.4)
fraciunile din obiecte care se iau, pentru a realiza S:
S=

f ' (i)c(i)

(B.5)

i 1

Avem:

120

G =

(B.6)

f ' (i)c(i)
i 1

unde G este greutatea total care se transport pentru cigul S.


Putem avea urmtoarele situaii:
(a) Dac (prin absurd) S < S, atunci se contrazice optimalitatea lui S; deci aceast situaie nu
poate s apar.
(b) Dac S = S, atunci S este optimal i nu mai avem ce demonstra.
(c) Dac (prin absurd) S > S, atunci procedm dup cum urmeaz.
Fie p cel mai mic indice pentru care f(p) 1 (n ipoteza n care un asemenea p exist). Vom
transforma f(p) n f(p) = f(p)+ = 1, obinnd un surplus de greutate de g(p) (depim
greutatea maxim G). Acest surplus de greutate va fi sczut dintrun alt obiect de indice t, t >
p (sau din mai multe obiecte
de indici mai mari dect p, dar tratarea este analoag cu cea pentru un singur obiect). Vom
schimba deci f(t) f(t) = f(t) 0, astfel nct
g(t) = g(p)
(B.7)
Pentru i p, t vom avea f(i) = f(i). Atunci:
f ' (i)c(i) ( f ' ( p)
)c( p)) ( f ' (t )
)c(t )) S ' c( p)
c(t ) (B.8)
S = f ' ' (i)c(i)
i

i p ,t

Dar: c(p) c(t) = v(p)g(p) v(t)g(t) i conform relaiei (B.7) obinem c:


S = S + g(p)(v(p) v(t))
(B.9)
Dac v(p) v(t) = 0, atunci S = S i procedeul se continu cu urmtorul p. Dac S < S se
contrazice optimalitatea lui S.
Dac nu (mai) exist nici un p cu proprietatea f(p) < f(p) =1, mai avem de studiat relaia dintre
f(k) = i f(k). Dac f(k) < atunci printrun procedeu analog de mrire a lui f(k) la (i
scderea unui f(t), t > k) vom obine fie o contradicie,
fie S = S (ca mai sus). Dac f(k) >, atunci se contrazice modul de alegere al lui f(k) = din
algoritm. n sftrit, dac f(k) = atunci avem c f poate fi adus la forma f fr a pierde din
ctig. Deci am demonstrat c S dat de f (conform algoritmului) nu poate fi mai mic dect S,
de unde rezult propoziia.
3. Algoritmul este:
(a) Se sorteaz spectacolele dup ora terminrii lor.
(b) Primul spectacol programat este cel care ncepe cel mai devreme.
(c) Se alege primul spectacol din cele ce urmeaz n ir ultimului spectacol programat care
ndeplinete conmdiia c ncepe dup ce sa terminat ultimul spectacol programat.
(d) dac tentativa de mai sus a euat (nu am gsit un astfel de spectacol), algoritmul se termin,
altfel se programeaz spectacolul gsit i se reia pasul 3c.
Algoritmul este:
Start Spectacole
citete( n, (st(i), sf(i), i = 1, n) )
Cheama Sortare(st, sf, n)
scrie( Spectacol care ncepe la , st(1), i se sfrete la, sf(1) )
121

k=1
pentru i = 2, n
dac st(i) sf(k) atunci
scrie( Spectacol care ncepe la , st(i), i se sfrete la, sf(i) )
k=i
sfrit dac
sfrit pentru
Stop
Algoritmul Sortare(st, sf, n) interschimb elementele din st, respectiv sf
astfel nct sf(1) sf(2) sf(n).
Complexitate: avem o sortare care se poate face n timp (n log n) i
o parcurgere a irului st, deci n total (n log n).
4. Dei problema are un caracter teoretic pronunat, rezultatul obinut poate fi folosit n
numeroase cazuri. Strategia pentru aceast problem este:
(a) Se sorteaz elementele celor dou mulimi
(b) Dac A = {a1, . . . , am} are k elemente negative i p elemente pozitive, se aleg primele k
elemente ale lui B i ultimele p elemente ale lui B.
Algoritmul este:
Start Maxim
citete( m, n, (a(i), i = 1,m), (b(i), i = 1, n) )
Cheama Sortare(a,m)
Cheama Sortare(b, n)
k=1
ct timp k m i a(k) < 0
k=k+1
sfrit ct timp
k=k1
suma = 0
pentru i = 1, k
suma = suma + a(i) b(i)
sfrit pentru
pentru i = k + 1,m
suma = suma + a(i) b(n m + i)
sfrit pentru
Stop
5. Timpul de ateptare pentru un client este compus din timpii de servire ai clienilor care sunt
procesai naintea lui adunat cu timpul de servire pentru el nsui. De aceea timpul total de
ateptare nu este indiferent fa de ordinea de servire. De exemplu, dac avem doi clieni, cu t1
= 1 i t2 = 2, atunci servirea n ordinea 1, 2 duce la un timp de ateptare de 1 + (1 + 2) = 4, pe
cdac ar fi procesai invers, sar ajunge la timpul 2 + (2 + 1) = 5. Intuitiv, este mai bine dac

122

clienii sunt procesai n ordinea vresctoare a timpilor de servire (n acest fel un timp de
servire mare are un impact mai trziu). Fie p o permutare a indicilor clienilor (corespunznd
unei ordini de intrare); clienii sunt preluai n ordinea p1, p2, . . . , pn. Pentru aceas
ordine, timpul total de servire este:
T(p) = tp1 +(tp1 +tp2)+ +(tp1 +tp2 + +tpn) =

n
i 1

(n i 1)t pk

(B.10)

Fie p permutarea care determin T din (B.10) minim. Fie p permutarea care se obine din p
printro inversiune: p_(i) = p(j), p_(j) =p(i) (i < j), pk) = p(k), k _= i, j. Atunci avem c
T(p) T(p) ceea ce este echivalent cu (j i)(tpi tpj ) 0, ceea ce conduce la tpi < tpj , ceea ce
confirm valabilitatea strategiei.
Algoritmul este:
Start Procesare
citete( n, (t(i), i = 1, n) )
pentru i = 1, n
p(i) = i
sfrit pentru
Cheama Sortare(t, p, n)
pentru i = 1, n
scrie( p(i) )
sfrit pentru
Stop
Algoritmul Sortare(t, p, n) sorteaz elementele lui t fcnd, n paralel, aceleai interschimbri
n vectorul de permutare p.

M2.U6.3. Test de autoevaluare a cunotinelor


1. Gsii o parantezare optim a produsului unui ir de matrice al crui ir de dimensiuni este
(5, 10, 3, 12, 5, 50, 6) (adic 5 10, 10 3, etc).
2. Artai c o parantezare complet a unei expresii cu n elemente are exact n 1 perechi de
paranteze.
3. Se d o matrice subdiagonal. Se consider toate sumele n care se adun elementele de pe
fiecare linie aflate sub elementul adunat anterior sau sub-i-la-dreapta. Se cere un algoritm
care gsete suma maxim.
Exemplu:
5
23
812
5934

123

Suma maxim este S = 5+2+8+9.


4. Folosind rezultatele lui InmultireMatrici s se descrie un algoritm care realizeaz nmulirea
matricilor n ordine optim.
Rspunsuri
1. Tablourile m i s, analoage celor din unitatea de nvare 2.6
1
2
3
4
5
6
1
2
3
4
5
6
0
150 330 405 165 201 0
1
2
2
4
2
5
0
0
0
360 330 243 195 0
0
2
2
2
2
0
0
0
0
0
180 930 177 0
0
0
3
4
4
0
0
0
0
0
300 186 0
0
0
0
4
4
0
0
0
0
0
0
0
150 0
0
0
0
0
5
0
0
0
0
0
0
0
0
0
0
0
0
0
De aici tragem concluzia c numrul optim de nmuliri scalare este m(1, 6) = 2010. Pentru
determinarea moduluin care se facenmulirea, folosim informaia din matricea s: s(1, 6) = 2,
deci vom avea parantezarea (A1 A2) (A3 . . . A6). Pentru a determina cum se face
parantezarea pentru produsul A36, folosim s(3, 6) = 4, de unde deducem c grupm A3...6 = (A3
A4) (A5 A6). n final, obinem parantezarea complet: ((A1 A2) ((A3 A4) (A5 A6)))
2. Demonstraia se face prin inducie matematic dup numrul n de matrici. Fie P(n)
propoziia parantezarea complet a unui ir cu n matrici are exact n 1 paranteze.
Verificarea:
(a) P(1): o matrice reprezint o expresie complet parantezat
(b) P(2): (A1 A2) este singura expresie complet parantezat asociat produsului a dou matrici,
deci avem o pereche de paranteze.
Demonstraie: presupunem propoziia adevrat pn la n 1 i demonstrm c i P(n) este
adevrat. Fie A1...n = (A1...p Ap+1...n), 1 p < n. Atunci: conform propoziiei P(p) avem c
numrul de perechi de paranteze pentru A1...p este p 1, iar pentru Ap...n este n (p + 1). Pentru
A1...n avem nevoie de (p 1) + (n p 1) + 1 perechi de paranteze (ultimul termen din sum
se datoreaz perechii de paranteze exterioare). Deci P(n) este adevrat, de unde concluzia din
enunul problemei.
3. Relaia dup care se calculeaz optimul este:
m(i, j) = min m(i, k ) m(k 1, j ) pi 1 pk p j
(B.11)
i k j

Fie . m(, ) este accesat n cadrul (B.11) fie ca m(i, k), fie ca m(k + 1, j). Fie t(, )
numrul de apeluri ce se face pentru m(, ) n primul caz i T(, ) numrul de apeluri ce
rezult din al doilea caz.

124

Pentru primul caz, avem i = , k = . Putem avea j {+1, n}, deci T(, ) = n . Pentru
U(,
)
se
poate
rta
analog
c
U(,
)
=
n

.
Obinem: R( , )

T( , )

(n

1)

n3

3
4. Fie a = (a(i, j)) matricea triunghiular dat la intrare (1 i n, 1 j i). Observm c avem
2n1 posibiliti de a forma drumuri de la a(1, 1) la ultima linie (demonstraia se face prin
inducie matematic dup n). Ca atare, o metod de abordare de tip brute force este de la
nceput sortit eecului. De asemenea se observ c o strategie de tipul coboar la numrul cel
mai mare aflat pe linia imediat urmtoare nu duce la soluie optim. Vom da un algoritm bazat
pe programare dinamic. Fie D drumul de la a(1, 1) la ultima linie care d suma maixim. Fie
S(i, j) un element de pe acest drum, aflat pe linia i i coloana j. Atunci S(i, j) este valoarea
maxim care se poate obine plecnd din (i, j) pn la ultima linie. Dac nu ar fi aa, atunci
nseamn c ar exista un alt drum de la (i, j) la ultima linie care d valoare mai mare, iar prin
modificarea lui D astfel nct s includ acest ultim drum am obine o valoare mai mare,
contradicie cu alegerea lui D. Ca atare, putem deduce c:
S (n, j )........................................................daca...i n
S(i, j) =
(B.12)
a(i, j ) max S (i 1, j ), S (i 1, j 1) ..........daca..i n
1

pentru 1 i n, 1 j i; ceea ce ne duce la algoritm. Calculul lui S(i, j) nu se face recursiv, ci


folosind o matrice triunghiular. Pentru reconstituirea soluiei se folosete o matrice
triunghilar sol.
Start Triunghi
citete( n, (a(i, j), i = 1, n, j = 1, i) )
pentru j = 1, n
s(n, j) = a(n, j)
sfrit pentru
pentru i = n 1, 1,1
pentru j = 1, i
dac s(i + 1, j) > s(i + 1, j + 1) atunci
s(i, j) = a(i, j) + s(i + 1, j)
sol(i, j) = j
altfel
s(i, j) = a(i, j) + s(i + 1, j)
sol(i, j) = j + 1
sfrit dac
sfrit pentru
sfrit pentru
scrie( Suma maxim este, s(1, 1) )
scrie( Ea se obine astfel: )
i=j=1
scrie( a(1, 1) )
125

pentru k = 1, n 1
j = sol(i, j)
i=i+1
scrie( a(i, j) )
sfrit pentru
Stop
5. Sugerm cititorului s urmreasc rezolvarea problemei 1. Rezolvarea se bazeaz pe un
algoritm de tip Divide et Impera din cauz c pentru un produs de tipul Ai...j vom determina
indicele k pentru care facem descompunerea Ai...j = (Ai...k) (Ak+1...j), fiecare din cele dou
matrici fiind la rndul lor parantezate independent, pn cnd se ajunge la cte o singur
matrice.
Descompunere(s.i, j)
dac i = j atunci
scrie( i )
Return
sfrit dac
dac j i = 1 atunci
scrie( (, i,*, j, ) )
Return
sfrit dac
k = s(i, j)
scrie( ( )
Cheama Inmultiri(s, i, k)
scrie( )*( )
Cheama Inmultiri(s, k + 1, j)
scrie( ) )
Return
Propunem cititorului interesat crearea unui algoritm care s determine toate modalitile de
nmulire a matricelor astfel nct s se ajung la un numr de nmuliri scalare minim.
(Indicaie: nu se mai folosete informaiua din s, ci doar cea din m)

126

Bibliografie
[1] Andonie, Rzvan, and Grbacea, Ilie. Algoritmi fundamentali. O perspectiv C++. Editura
Libris, 1995. Download de la: http://vega.unitbv.ro/andonie.
[2] Cormen, Thomas H, Leiserson, E Charles, and Rivest, Ronald R. Introducere
n algoritmi. Computer Libris Agora, 2000.
[3] Livovschi, Leon and Georgescu, Horia. Sinteza i analiza algoritmilor. Editura Stiinific
i Enciclopedic, 1986.
[4] Tudor, Sorin. Tehnici de programare (Manual pentru clasa a X-a). L&S Infomat, 1996.

127

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