Algoritmic
CURS PENTRU NVMNT LA DISTAN
AUTORI
Conf. Dr. Paul Iacob
ANUL I
2012-2013
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
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.
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.
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
cu formula: PS =
A(i) B(i) .
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
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
c5
ti
i =2
n
(t i 1)
A(j + 1) = A(j)
c6
i =2
j=j1
c7
(t i 1)
i =2
sfrit ct timp
8
A(j + 1) = k
c8
n-1
c9
sfrit pentru
9
scrie( A(i), i = 1, n )
Stop
n
i =2
i =2
i=2
T (n) = c1 (n + 1) + c 2 (n 1) + c3 (n 1) + c 4 (n 1) + c5 t i + (c6 + c7 ) (t i 1) + (c 6 + c7 ) (t i 1) +
+ 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 (i 2) =
, deci T (n) = an 2 + bn + c este o funcie
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
15
16
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
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
, dac.n = 1
a
n
elemente va fi T (n/2), deci: T (n) =
2 T + c n , dac.n > 1
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
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
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:
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
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 3 se va reprezenta astfel:
c
**
25
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
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).
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..
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
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
sfrit pentru
scrie( f(n) )
Stop
Aplicai algoritmul pentru irul n=5
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
Introducere.
n timp s-au concretizat metode generale pentru elaborarea algoritmilor. Modulul acesta
introduce cele mai folosite dintre aceste metode.
36
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).
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
40
41
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.
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
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:
x = (..., 0,
x = (..., 1,
l1
l0
1, ..., 1, 0, ..., 0 )
a
b
l1 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:
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
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
atunci complexitatea este dat de formula: T (n) = aT + f (n ) unde f(n) este costul
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
fie ca pe
pe ntregii nenegativi prin recurena: T (n) = aT + f (n ) unde interpretm pe
b
b
n
n
b , fie ac pe b . atunci T (n) poate fi delimitat asimptotic dup cum urmeaz:
(
f (n) = O (n
f (n) = (n
1. Dac f (n) = O n logb a pentru o anumit constant > 0 , atunci T (n) = n logb a
2. Dac
3. Dac
logb a
log b a +
anumit constant c < 1 i toi n suficieni de mari atunci T (n) = ( f (n) )
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:
n
1. 2 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:
pentru m = 1
(i, j )
H (m, i, j ) =
, unde k = 6 i j este
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
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:
58
k =2
k =2
k =2
+ TP (k ) + (n 2)c = TP (k ) + cn = ck + cn = (n 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
n2
n
Stirling n!= 2n obinem c h log 2 n!> n log 2 n n log 2 e = (n log 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
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 3n / 2 2 comparaii (demonstrai acest lucru).
Sugerm cititorului s execute algoritmul pentru intrarea x = (1, 2,3, 4, 5,
6,85, 2).
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
66
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
69
70
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.
yY
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
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
n
i =1
i =1
74
75
n2
i =1
i =1
n2
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).
78
- numrul total de comparaii dat de strategie este L(T ) = nr (i ) niv (i ) n + 1 , unde niv(i)
i =1
i =1
clientul i)
80
81
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:
1............................daca..n = 1
P(n) = 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
= (4 n / n 3 / 2 )
C (n) =
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
...daca..i < j
m[i, j ] =
min{m[i, k ] + m[k + 1, j ] + p i 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
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
89
Rspunsuri la teste
A(i) B(i) .
i =1
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
Exemplu:
pentru n = 4, a = (10, 20, 30, 40) vom avea:
diferit = (10, 20, 30, 40)
contor = (1, 1, 1, 1)
95
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.
96
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
din Rad}
scrie( Info(Rad) ){Sau orice alt subalgoritm de prelucrare a informaiei
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
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.
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
106
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
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
113
114
115
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.
n 1
n 1
Obinem c A(i) este mai mic sau egal dect 2(n
+ 1 Dar 2(n
+ 1 n+1, deci
2
2
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
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
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
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
(B.1)
f = (1, 1, . . . , 1, , 0, 0, . . . 0)
unde 0 < 1 i apare pe poziia k (k fiind variabila din algoritm).
n
(B.2)
i =1
i
k
G = f (i )c(i )
(B.3)
i =1
S= f ' (i )c(i )
(B.5)
i =1
Avem:
120
G = f ' (i )c(i )
(B.6)
i =1
i p ,t
121
cdac ar fi procesai invers, sar ajunge la timpul 2 + (2 + 1) = 5. Intuitiv, este mai bine dac
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:
n
(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.
123
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.
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:
(B.11)
m(i, j) = min{m(i, k ) + m(k + 1, j ) + pi 1 p k p j }
ik < 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
.
n3 n
=1 =
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
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) )
n
Obinem: R ( , ) = T ( , ) = (n + 1) = L =
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