Académique Documents
Professionnel Documents
Culture Documents
Carte Algoritmica
Carte Algoritmica
ALGORITMICA
Paul Iacob
Cuprins
Prefat
a
Cuv
ant nainte
1 Introducere
11
Scopul cartii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Generalitati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2 Limbajul pseudocod
15
Obiective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Limbajul pseudocod . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Rezumat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3 Analiza algoritmilor
Obiective . . . . . . . . . . . . . . . .
Analiza algoritmilor . . . . . . . . . .
Algoritmi parametrizati (subalgoritmi)
Notatia asimptotica . . . . . . . . . . .
Rezumat . . . . . . . . . . . . . . . . .
Exercitii . . . . . . . . . . . . . . . . .
4 Structuri elementare de date
Obiective . . . . . . . . . . . . .
Structuri liniare . . . . . . . . . .
Stiva . . . . . . . . . . . . . . . .
Alocarea secventiala a stivei
Alocarea nlantuita a stivei .
Coada . . . . . . . . . . . . . . .
Alocarea secventiala a cozii
Alocarea nlantuita a cozii .
Arbori binari . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
21
21
25
28
30
30
.
.
.
.
.
.
.
.
.
33
33
33
34
35
35
36
37
38
39
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
39
40
40
42
42
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
45
45
45
49
49
49
50
51
.
.
.
.
.
53
53
53
55
56
57
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7 Generarea submultimilor
Obiective . . . . . . . . . . . . . . . . . . . .
Schema generala de lucru . . . . . . . . . . .
Generarea elementelor unui produs cartezian .
Generarea tuturor submultimilor unei multimi
Generarea multimii combinarilor . . . . . . .
Generarea multimii permutarilor . . . . . . .
Generarea multimii aranjamentelor . . . . . .
Submultimi de suma data . . . . . . . . . . .
Rezumat . . . . . . . . . . . . . . . . . . . . .
Exercitii . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
59
59
59
61
62
63
66
69
71
73
73
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
75
75
75
76
78
79
79
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
87
87
87
88
89
91
91
92
92
95
95
96
97
98
98
10 Metoda Greedy
Obiective . . . . . . . . . . . . . . . . . .
Prezentare generala . . . . . . . . . . . . .
Problema submultimilor de suma maxima
Arborele Huffman . . . . . . . . . . . . . .
Interclasare optimala . . . . . . . . . . . .
Rezumat . . . . . . . . . . . . . . . . . . .
Exercitii . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
101
. 101
. 101
. 102
. 104
. 107
. 110
. 110
11 Programare dinamic
a
Obiective . . . . . . . . . . .
Prezentare generala . . . . . .
Inmultirea unui sir de matrici
Cel mai lung subsir crescator
Rezumat . . . . . . . . . . . .
Exercitii . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
113
113
113
114
120
122
122
12 Algoritmi euristici
125
Obiective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Prezentare generala . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
O problema de taiere cu doua criterii de optimizare. . . . . . . . . . 125
5
Rezumat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Exercitii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
13 Algoritmi n algebra liniar
a.
Obiective . . . . . . . . . . . . .
Algoritmul lui Strassen. . . . . .
Metoda LUP . . . . . . . . . . .
Rezumat . . . . . . . . . . . . . .
Exercitii . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
131
. 131
. 131
. 133
. 140
. 140
14 Teste de autoevaluare
143
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
149
. 149
. 150
. 150
. 151
. 152
. 152
. 153
. 154
B Rezolv
ari
Rezolvari pentru
Rezolvari pentru
Rezolvari pentru
Rezolvari pentru
Rezolvari pentru
Rezolvari pentru
Rezolvari pentru
Rezolvari pentru
Rezolvari pentru
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
capitolul
capitolul
capitolul
capitolul
capitolul
capitolul
capitolul
capitolul
capitolul
3 .
4 .
5 .
6 .
7 .
8 .
9 .
10
11
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
157
157
167
172
175
182
184
186
190
196
Prefat
a
In ultimii ani asistam la o dezvoltare n ritm sustinut a utilizarii calculatoarelor electronice, spectrul aplicatiilor creste continuu, numarul utilizatorilor se mareste simtitor. A aparut astfel necesitatea elaborarii unei
metodologii a programarii.
In acest context, ne permitem sa apreciem ca fiind gresita opinia (din
pacate nca raspandita) ca a sti sa programezi echivaleaza cu a cunoaste
unul sau mai multe limbaje de programare. Adevarul este altul: a sti sa programezi nseamna a stapani tehnici de elaborare a algoritmilor si metode de
elaborare a programelor, unele dintre ele fiind prezentate n aceasta lucrare.
Cea mai mare parte a materialului prezentat se refera la subiecte ce figureaza n mai toate manualele si tratatele actuale si anume: analiza algoritmilor, structuri elementare de date, recursivitate, metode de elborare a
algoritmilor, algoritmi euristici, algoritmi n algebra liniara. Se au n vedere
atat aspectele teoretice ale problemelor, cat si cele practice, majoritatea capitolelor continand probleme rezolvate si probleme propuse.
Pentru redactarea algoritmilor se foloseste un limbaj algoritmic (pseudocod
n limba romana), foarte naural si accesibil. Acest limbaj are avantajul ca
permite elaboraream mai usoara a algoritmilor si evita, n mare parte, efortul
de memorare a nenumaratelor detalii ale unui limbaj de programare.
Iata de ce aceasta lucare este bine venita; ea ne face sa gandim. Ne face
sa gandim algoritmic si despre algoritmi.
Structurarea lucrarii si modul de prezentare a notiunilor sunt ingenios
realizate, acestea fiind principalele repere care o deosebesc de alte lucrari din
domeniu.
Lucrarea se adreseaza n primul rand studentilor din nvatamantul universitar de informatica, dar si din nvatamantul universitar tehnic, matematic
si economic.
Problemele constitue un fond reprezentativ atat pentru nvatarea notiunilor
din carte cat si pentru pregatirea unor concursuri, olimpiade scolare, la diverse nivele. De asemenea, ea vine n sprijinul profesorilor de liceu pentru
pregatirea orelor de informatica n concordanta cu cerintele programelor de
7
specialitate actuale.
Prof. Dr.
Ciurea Eleonor
Cuv
ant nainte
Se cuvine sa spunem cate ceva despre stradania depusa pentru aceasta
carte.
Materialul sa strans pe parcursul a 14 ani de predare a materiei algoritmica la Facultatea de Matematica si Informatica a universitatii Transilvania din Brasov. Redactarea lui s a facut n mai multe etape cu jutorul
nepretuit al sotiei mele, Mirela si al domnului Sasu Lucian, studentul si
apoi asistentul nostru mai multi ani. Trebuie sa mai spun ca majoritatea
solutiilor problemelor propuse au fost scrise tot de Sasu Lucian, tot el a avut
ideea de introduce, n apendix, metode utile n calculul complexitatii si tot
la initiativa lui a fost introdus capitolul de recursivitate. La capitolul despre metoda Greedy sia adus o conrtributie importanta doamna Tabarca
Tatiana.
Multumesc, pe aceasta cale, profesorului Ciurea Eleonor pentru prefata,
pe care probabil ati citito deja, si pentru desele discutii anterioare care au
contribuit la completarea materialului.
Autorul
10
Capitolul 1
Introducere
Scopul c
artii
Lucrarea de fata si propune sa fie o introducere n teoria algoritmilor. Se
vor introduce notiunile de algoritm, subalgoritm, complexitatea algoritmilor,
diferite metode de elaborare a algoritmilor, probleme clasice. Cartea este
detinata tuturor celor ce vor sa scrie un program si nu stiu cu ce sa nceapa.
Generalit
ati
Pentru rezolvarea unei probleme pe calculator, se parcurg urmatorii pasi:
1. se detecteaza problema;
2. se adauga restrictiile impuse de domeniul care a creat problema;
3. se creeaza un model matematic;
4. se concepe un algoritm (un mod) de rezolvare a problemei;
5. se scrie un program;
6. se interpreteza rezultatele de la calculator n termeni specifici domeniului problemei.
Pasii 1, 2, 3 sunt n general reuniti sub numele de analiz
a. Pasul 4
constituie programarea. Pasul 6 face parte din implementare.
Deci algoritmul reprezinta un pas intermediar ntre modelul matematic
si programul realizat ntr-un limbaj conceput pentru calculator. Originea
cuvantului algoritm este legata de numele Abu Jafar Mohammed Ibn M
usa
11
cu dimensiunea intrarii). O alta problema este cea legata de dimensiunea memoriei necesare executiei programului realizat dupa un anumit
algoritm.
13
14
Capitolul 2
Limbajul pseudocod si structuri
fundamentale
Obiective
In acest capitol se va prezenta lexicul limbajului pseudocod, care va
fi folosit n restul lucrarii pentru reprezentarea algoritmilor. Se introduc
instructiunile de atribuire, decizie, ciclare, citire, scriere. Notiunile prezentate sunt nsotite de exemple simple care sa ajute la nsusirea temeinica a
limbajului pseudocod.
Limbajul pseudocod
Limbajul n care este descris algoritmul trebuie sa fie un intermediar ntre
limbajul matematic si cel de programare.
Limbajul schemei logice are avantaje de explicatie grafica, dar si dezavantaje legate de structurare si translatare, asa ca vom folosi pseudocodul. Acest
limbaj foloseste cuvinte cheie (subliniate), cuvinte care definesc variabile si
constante si text explicativ (ntre acolade).
Putem distinge urmatoarea structura generala:
Start Numele programului
citeste( date de intrare )
prelucreaza datele
scrie( rezultate )
Stop
Putem specifica:
15
Se calculeaza rezultatul operatiei de adunare b + c, acesta depunanduse ntr-o zona intermediara. Rezultatul adunarii se copiaza apoi n zona de
memorie alocata variabilei a. Chiar si pentru o atribuire de genul:
a=a+1
rezultatul adunarii lui a cu 1 se depune ntr-o zona intermediara, dupa care
se copiaza n locatia lui a, analog cu figura 2.2.
Atunci cand pasul urmator depinde de o anumita situatie cucerita pana
atunci, el este rezultatul unei decizii care va fi scris astfel:
daca conditie logica atunci
(grup de pasi 1)
altfel
(grup de pasi 2)
sfarsit daca
Exemplul 1.
Start Algoritm 1
citeste( i, a )
16
daca i 1 atunci
a=a+i
altfel
a=ai
sf
arsit daca
scrie( a=, a )
Stop
Acest algoritm scrie a = 10 daca intrarea este 0, 10, iar pentru intrarea 2, 10
scrie a = 8.
Exemplul 2. Urmatorul algoritm gaseste cel mai mare dintre trei numere:
Start Algoritm 2
citeste( a, b, c )
max = a
daca max < b atunci
max = b
sf
arsit daca
daca max < c atunci
max = c
sf
arsit daca
scrie( Maximul este , max )
Stop
Urmariti algoritmul pentru valorile de intrare (10, 1, 3), (2, 2, 5), (1, 12, 2).
Exista grupuri de instructiuni care se executa n mod repetat. Aceste
structuri se numesc cicluri si apar des n elaborarea algoritmilor.
Prima forma de ciclare se numeste ciclu cu numarator si se foloseste atunci
cand se stie de cate ori se executa un grup de instructiuni. Are forma:
pentru ind = vi, vf, pas
grup de pasi
sf
arsit pentru
Daca pas = 1, atunci el se poate omite. Grupul de pasi din interiorul ciclului
poate sa nu se execute niciodata, daca vi > vf (pentru pas > 0), respectiv
daca vi < vf (pentru pas < 0). De exemplu, ciclul de mai jos nu se executa
daca n < 1:
pentru ind = 1, n
grup de pasi
sf
arsit pentru
17
Exemplul 5. Algoritmul urmator (algoritmul lui Euclid) serveste la determinarea celui mai mare divizor comun a doua numere:
Start Euclid
citeste( a, b )
m=a
n=b
c
at timp n 6= 0
r = rest(m/n)
m=n
n=r
sf
arsit cat timp
scrie( CMMDC dintre , a, si , b, este , m )
Stop
Urmariti algoritmul pentru intrarile (10, 3) si (258, 12).
Ciclul cu test posterior are forma:
repeta
grup de pasi
p
an
a cand conditie logica
Grupul de pasi se executa cel putin o data.
Rezumat
Un algoritm are o structura bine definita. Instructiunile prezentate, mpreuna cu notiunea de subalgoritm din capitolul 3 formeaza un vocabular
suficient pentru scrierea algoritmilor. Sau prezentat: forma generala a
unui algoritm, citirea si scrierea datelor, atribuirea, instructiunea de decizie, cele trei forme de ciclare.
20
Capitolul 3
Analiza algoritmilor.
Subalgoritmi. Notatia
asimptotic
a
Obiective
Acest capitol prezinta modalitatea de comparare a performantei algoritmilor, folosind notiunea de complexitate. Se introduc cele trei notatii asimptotice mpreuna cu relatiile dintre ele, formule de calcul general, proprietati.
Exemplificarea se face prin compararea a doi algoritmi care rezolva aceeasi
problema: sortarea unui vector de numere (problema de larg interes, care nu
are o rezolvare triviala).
Se introduce de asemenea notiunea de subalgoritm, absolut necesara n
scrierea algoritmilor recursivi sau ca forma de abstractizare. Capitolul se
ncheie cu o suita de probleme propuse spre rezolvare, a caror solutionare
este data la sfarsitul lucrarii.
Analiza algoritmilor
Putem usor sa ne dam seama ca pentru a rezolva o anumita problema
pot exista mai multi algoritmi. De exemplu, se stie deja ca pentru aflarea
celui mai mare divizor comun a doua numere se poate folosi fie algoritmul
lui Euclid, fie un algoritm bazat pe descompunerea numerelelor n produse
de factori primi.
Se pune ntrebarea: care din acesti algoritmi este mai bun? si n general:
cum putem compara doi algoritmi care rezolva aceeasi problema?
Cu raspunsul la aceasta ntrebare se ocupa analiza algoritmilor.
21
Start Sortins
citeste( n, (A(i), i = 1, n) )
22
2
3
4
5
6
7
8
pentru i = 2, n
k = A(i)
{insereaza A(i) n sirul sortat A(1), . . . , A(i 1)}
j =i1
cat timp j > 0 si A(j) > k
A(j + 1) = A(j)
j =j1
sfarsit cat timp
A(j + 1) = k
{a terminat inserarea}
sfarsit pentru
scrie( A(i), i = 1, n )
Stop
c5
c6
j =j1
c7
Numar de operatii
n+1
n1
n1
n1
n
P
ti
i=2
n
P
i=2
n
P
(ti 1)
(ti 1)
i=2
8
9
c8
n1
c9
T (n) = c1 (n + 1) + c2 (n 1) + c3 (n 1) + c4 (n 1) +
n
n
n
X
X
X
+ c5
ti + (c6 + c7 )
(ti 1) + (c6 + c7 )
(ti 1) +
i=2
i=2
i=2
+ c8 (n 1) + c9 n
Cum putem sa-l aflam pe ti ? O sa ne punem, de fapt, problema intervalului n care se plimba, studiind cele doua cazuri care corespund limitelor
intervalului.
Cel mai rapid caz se obtine cand sirul este gata sortat si 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 functie liniara de n.
Cel mai rau caz se obtine cand sirul dat este sortat descrescator, ceea ce
nseamna ca pe linia 5 vom avea A(j) > k pentru orice j de la i 1 la 1, deci
n
n
P
P
s
i
(i 2) = (n2)(n1)
, deci
ti = i 1 ; atunci
(i 1) = n(n1)
2
2
i=2
i=2
citeste( n, (A(i), i = 1, n) )
citeste( m, (B(j), j = 1, m) )
k=0
i=1
j=1
repeta
k =k+1
daca A(i) < B(j) atunci
C(k) = A(i)
i=i+1
altfel
C(k) = B(j)
j =j+1
sfarsit daca
p
ana cand i > n sau j > m
{Ciclul de mai jos poate sa nu se execute nici o data}
pentru il = i, n
k =k+1
C(k) = A(il)
sf
arsit pentru
{Ciclul de mai jos poate sa nu se execute nici o data}
pentru jl = j, m
k =k+1
C(k) = B(jl)
sf
arsit pentru
{Din cele doua cicluri pentru de mai sus, se executa exact unul singur}
scrie( C(kl), kl = 1, m + n )
Stop
Se observa ca timpul total de executie pentru algoritmul de interclasare
este T (n, m) = c (n + m) unde c este timpul maxim necesar executiei
instructiunilor din interiorul ciclurilor repet
a si pentru (c nu depinde de m, n).
Pentru algoritmul de sortare prin interclasare prezentam notiunea de subalgoritm.
26
j =j+1
sf
arsit daca
p
an
a cand i > q sau j > r
{Ciclul de mai jos poate sa nu se execute nici o data}
pentru il = i, q
k =k+1
C(k) = A(il)
sf
arsit pentru
{Ciclul de mai jos poate sa nu se execute nici o data}
pentru jl = j, r
k =k+1
C(k) = A(jl)
sf
arsit pentru
{Din cele doua cicluri pentru de mai sus, se executa exact unul singur}
{Aici se reface bucata din A de la A(p) la A(r)}
k=0
pentru i = p, r
k =k+1
A(i) = C(k)
sf
arsit pentru
Return
Asa cum am precizat la nceputul acestui capitol algoritmul sortarii prin
interclasare Intersort se bazeaza pe metoda divide et impera, ai carei pasi
au fost deja prezentati. Evidentiem n continuare pasii acestui algoritm:
1. Imparte sirul initial n doua siruri
2. Rezolva (recursiv) fiecare din cele doua subprobleme
3. Combina (interclaseaza) cele doua subsiruri sortate pentru a obtine
sirul sortat
Exemplu: fie sirul: 10 2 7 3
1. mpartire: 10 2
7 3
2. sortare (recursiv)
(a) mpatire: 10
(a) mpatire: 7
Notatia asimptotic
a
Pentru a vedea cum se comporta timpul de executie al unui algoritm
pentru intrari mari vom defini functiile , , O.
Spunem ca T (n) = (f (n)) daca N, c1 , c2 > 0 astfel ncat:
0 < c1 f (n) < T (n) < c2 f (n), n > N
28
(3.1)
(3.2)
(3.3)
Din cele de mai sus, precum si analiza facuta la algoritmul de sortare prin
insertie rezulta ca complexitatea acestuia este T (n) = O(n2 ) si T (n) = (n).
Calculul complexitatii algoritmului de sortare prin interclasare se efectueaza
mai jos.
Cand un algoritm contine o apelare a lui nsusi se numeste recursiv. In
acest caz timpul de executie se descrie printr-o ecuatie recursiva. Pentru
rezolvarea unei asemenea ecuatii vom folosi metode matematice care vor fi
descrise n capitolul dedicat metodei Divide et impera.
Ne vom limita acum la a scrie recurenta pentru sortare prin interclasare.
Daca timpul pentru un sir cu n elemente este T (n), atunci timpul pentru
sirul cu n2 elemente va fi T n2 , deci:
T (n) =
2T
a
n
2
, daca n = 1
+ c n , daca n > 1
Rezumat
Pentru compararea a doi algoritmi, cel mai utilizat criteriu este cel al
timpului de executie relativ la dimensiunea datelor de intrare. Calculul complexitatii unui algoritm este o etapa obligatorie a solutiei acestuia. Pentru
exprimarea complexitatii se folosesc notatiile O(), (), () care sunt legate
de viteza de variatie a timpului cerut pentru rezolvarea unei instante, n
functie de dimensiunea intrarii si care sunt cu atat mai corecte cu cat n este
mai mare.
Subalgoritmii sunt o modalitate de a concepe blocuri de cod parametrizate, apelabile prin intermediul unei interfete (liste de parametri de apel).
Ele efectueaza prelucrari asupra datelor transmise, rezultatul returnat fiind
folosit mai apoi de algoritmul apelant. De asemenea, reprezinta o forma de
abstractizare, utilizata n toate paradigmele de programare.
Exercitii (rezolv
arile la pagina 157)
1. Determinati complexitatea algoritmilor din capitolul 2.
30
produsului scalar.
3. Se da n, vectorul A(i), i = 1, n si un numar x. Sa se scrie un algoritm
care gaseste indicele j pentru care A(j) = x (problema cautarii).
4. Cum se modifica algoritmul de mai sus daca stim ca vectorul este sortat
crescator?
5. Se dau n si vectorul A(i), i = 1, n. Sa se gaseasca cel mai mic element.
6. Reluati problema anterioara n situatia n care trebuie sa aflati cate elemente minime sunt. Incercati sa rezolvati problema folosind o singura
parcurgere a sirului.
7. Se dau n si vectorul A(i), i = 1, n. Ce numar apare cel mai des si de
cate ori?
8. Cum se modifica algoritmul de mai sus daca vectorul este sortat crescator?
9. Se dau n, ((A(i, j), j = 1, n), i = 1, n) (A este o matrice patratica, cu
n linii si n coloane). Sa se calculeze produsul elementelor diferite de 0:
(a) din toata matricea;
(b) de pe diagonala principala;
(c) de sub diagonala principala.
31
32
Capitolul 4
Structuri elementare de date
Obiective
Am folosit, pana acum, structuri de date foarte simple cum ar fi:
variabile simple (dintre care unele aveau domeniul de valori format doar
din doua valori)
vectori cu componente sortate sau nesortate (asa cum sunt folositi n
matematica)
matrici considerate ca vectori de vectori sau ca masive biortogonale
Vom studia acum alte structuri de date necesare n algoritmii ulteriori: lista,
stiva, coada, arborii binari, mpreuna cu diferite forme de reprezentare a
acestora.
Structuri liniare
O structura liniara este o multime de n 0 componente x(1), x(2), . . . x(n)
cu proprietatile:
1. cand n = 0 spunem ca structura este vida;
2. daca n > 0 atunci x(1) este primul element iar x(n) este ultimul element;
3. oricare ar fi x(k) unde k {2, . . . , n 1} exista un predecesor x(k 1)
si un succesor x(k + 1).
Ne va interesa sa executam cu aceste structuri urmatoarele operatii:
33
Stiva
Una din cele mai cunoscute structuri liniare este stiva. O stiva este caracterizata prin disciplina de intrare si de iesire. Sa consideram o multime
de carti puse una peste alta ntr-o cutie stramta; exista o prima carte care
se poate lua foarte usor (TOP) si o carte carte care se poate lua numai daca
se nlatura toate celelate carti (BOTTOM).
Disciplina unei stive este ultimul intrat, primul iesit (disciplina care nu
v-ar place sa fie respectata cand stati la rand la lapte!), prescurtat LIFO
(Last In, First Out).
Diferentele fata de un vector sunt:
- un vector are o lungime fixa, nonzero, cunoscuta aprioric;
- o stiva poate fi vida;
- stiva are un numar variabil de elemente n timpul executiei unui algoritm
Se pune problema reprezentarii concrete a unei stive n memoria unui calculator. Putem aloca o stiva n doua moduri:
1. Secvential
2. Inlantuit
34
Alocarea secvential
a a stivei
Folosim vectorul ca fiind structura cea mai apropiata de structura reala
a memoriei. In vectorul (x(i), i = 1, n) consideram:
x(1)
BOTTOM
x(2)
x(3)
...
x(k) . . .
TOP
x(n)
adica din cele n componente ale unui vector doar primele k elemente fac parte
din stiva.
Algoritmul de intrare n stiva este:
a Stiva
dac
a k = n atunci
Depasire
altfel
k =k+1
x(k) = a
sf
arsit daca
Return
Algoritmul de iesire (scoatere) din stiva este:
a Stiva
dac
a k = 0 atunci
Stiva vida
altfel
a = x(k)
k =k1
sf
arsit daca
Return
Alocarea nl
antuit
a a stivei
In alocarea nlantuita fiecare element al structurii este nsotit de adresa
de memorie la care se afla precedentul element. Vom folosi semnul * cu sensul
nici o adresa de memorie. Vom avea varful stivei pus n evidenta (ancorat)
n IN C si elementul de la baza stivei va contine n campul LEG adresa *
(adica nici o adresa), ca n figura:
IN F O
IN C
LEG
x(1)
Coada
O alta structura de date folosita n conceperea algoritmilor este coada.
O coada este caracterizata si ea de o disciplina de intrare/iesire, binenteles
diferita de cea a stivei. De data aceasta puteti sa va ganditi la coada la lapte:
primul care s-a asezat la coada va fi primul servit, adica primul care iese din
coada.
Disciplina unei cozi este deci primul venit, primul plecat (first in, first
out - FIFO).
1
Cunoscut
a n cadrul limbajelor de programare drept heap; obtinerea de memorie se
face prin alocare dinamic
a, eliberarea ei prin dealocare.
36
Alocarea secvential
a a cozii
Coada alocata secvential si va gasi locul tot ntr-un vector (x(i), i = 1, n):
x(1)
x(2)
...
x(F )
FAT
A
...
x(S)
SFARSIT
...
x(n)
Alocarea nl
antuit
a a cozii
Alocarea nlantuita se face similar cu alocarea nlantuita a stivei n noduri
de tipul:
IN F O
x(F )
FAT
A
...
LEG
x(S) *
SFARS
IT
A=*
atunci
daca FAT
{Coada este vida}
FAT
A=y
altfel
IT)=y
LEG(SFARS
sfarsit daca
IT =y
SFARS
Return
IT=*.
Initial, coada este vida, i.e. FAT
A=SF
ARS
Algoritmul de iesire din coada este:
a Coada
daca FAT
A=* atunci
Coada este vida
altfel
a = IN F O(FAT
A)
aux =FAT
A
FAT
ALEG(FAT
A)
aux LIBERE
sfarsit daca
Return
38
Arbori binari
Un arbore n care orice varf (nod) are 0, 1 sau 2 descendenti se numeste
arbore binar. Un arbore binar care are varfuri cu 0 sau 2 descendenti se
numeste arbore binar strict. Un astfel de arbore se poate folosi, de exemplu,
la reprezentarea n memorie a unei expresii aritmetice.
Exemplu: expresia
2
(a + b) c 3
c
se va reprezenta astfel:
Figura 4.2: Arborele binar atasat expresiei aritmetice (a + b) c c23 . Semnul
este reprezentare pentru operatorul de ridicare la putere.
Alocarea secvential
a a unui arbore binar
In vectorul (x(i), i = 1, n) vom avea urmatoarele reguli:
- radacina este n x(1);
- pentru fiecare nod x(i), descendentul sau din stanga este x(2 i) iar cel
din dreapta este x(2 i + 1);
- daca nu exista descendent, se pune *.
Reprezentarea secventiala a arborelui din figura 4.2 este:
1
/ + c
2 3 4 5
2 a b
6 7 8 9
*
10
* * *
11 12 13
39
c
14
3
15
* * ...
16 17 . . .
*
31
Alocarea nl
antuit
a a unui arbore binar
In acest caz se folosesc noduri de forma IN F O LS LD , unde LS
si LD contin adresele de memorie ale nodurilor descendent stang, respectiv
descendent drept. Reprezentarea nlantuita a arborelui din figura 4.2 este
data n figura 4.3.
Figura 4.3: Alocarea nlantuita pentru arborele din figura 4.2.
Alocarea mixt
a a unui arbore binar
La acest tip de alocare se folosesc trei vectori IN F O, LS, LD unde LS(i)
si LD(i) contin indicii unde se afla memorati descendentul stang, respectiv
descendentul drept ai nodului i.
Reprezentarea mixta a arborelui din figura 4.2 este data n figura 4.4.
IN F O
1
2
3
/
4
+
5
c
6
2
7
8
a
9
b
10
c
11
3
LS
2
4
6
8
*
*
10
*
*
*
*
LD
3
5
7
9
*
*
11
*
*
*
*
40
- arborele binar se defineste recursiv ca: radacina, subarbore stang, subarbore drept (fiecare subarbore este la randul lui un arbore binar, posibil vid);
- la parcurgere mai ntai va fi subarborele stang naintea celui drept.
Dupa pozitia radacinii (R) relativ la subarborele stang (S) si subarborele
drept (D), avem urmatoarele 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
2 c
+ b
c-
/ c
+ c
2 c
3 /
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). Exista diferite tipuri de
reprezentari ale acestor structuri: pe baza de tablouri, folosind alocarea dinamica, sau o varianta mixta (la arbori). Stiva si coada au politici specifice
de acces, iar arborele poate fi parcurs n moduri diferite pentru procesarea
datelor.
Exercitii (rezolv
arile la pagina 167)
1. Scrieti algoritmii de intrare/iesire din coada circulara.
42
43
44
Capitolul 5
Recursivitate
Obiective
Capitolul detaliaza modul de lucru al unui subalgoritm recursiv. Este
dat un exemplu simplu de algoritm recursiv, pentru care se descriu toate
informatiile care se depun pe stiva la executia lui. Explicatiile date sunt
aplicabile pentru orice caz de recursivitate.
Prezentare general
a
Recursivitatea reprezinta un mod de elaborare a algoritmilor, caracterizat
prin faptul ca un subalgoritm se apeleaza pe el nsusi (direct sau indirect).
Ea a aparut din necesitati practice, permitand implementarea unei functii
matematice recursive. Nu reprezinta o modalitate mai puternica sau mai
slaba din punct de vedere al rezultatelor ce se pot obtine comparativ cu
metodele iterative; sa demonstrat ca aceste doua abordari sunt egale ca si
putere de calcul. Algoritmii recursivi nu sunt mai rapizi decat algoritmii
iterativi echivalenti, dar uneori sunt mai expresivi si mai usor de ntretinut
(modificat).
Pentru ntelegerea algoritmilor recursivi, este esentiala ntelegerea lucrului cu stiva. Inainte de a se efectua un apel de subalgoritm (recursiv sau nu),
se salveaza pe stiva adresa de memorie a instructiunii de la care se va continua executia dupa ntoarcerea din apel. La intrarea ntrun subalgoritm se
rezerva spatiu pe stiva pentru parametrii transmisi si pentru variabilele care
au fost declarate n subalgoritm (variabile locale). La iesirea din apel, stiva
se elibereaza de informatia depusa la intrarea n apel, iar adresa urmatoarei
instructiuni ce se va executa se preia de pe varful stivei (si de asemenea
aceasta informatie va fi scoasa de pe stiva).
45
scrie( Sfarsit )
Stop
Recursiv(n)
i = 2 n{i este variabila locala, alocata n subalgoritmul Recursiv }
daca n = 0 atunci
Return
altfel
Recursiv(n 1)
i=i+1
scrie( i )
sfarsit daca
Return
Ca si comentarii sunt trecute niste adrese la care se presupune a se afla
instructiunile corespunzatoare. Sa presupunem ca se va citi n = 2. Evolutia
stivei programului este:
Inainte de apelul Recursiv(2) din algoritmul Exemplu se scrie pe stiva
adresa de memorie a instructiunii care se va executa dupa ce se va
reveni din acest apel (n cazul nostru instructiunea de scriere cu adresa
):
adr =
n=2
Se apeleaza Recursiv(2);
Se calculeaza i = 2 n = 4,
Se face testarea n = 0; deoarece este conditia nu este ndeplinita, se
intra pe ramura altfel.
Avem din nou apel: nainte de acesta se salveaza adresa de memorie
46
n=2 i=4
n=2
Se intra n Recursiv(1);
Se calculeaza i = 2 n = 2
Se face testarea n = 0; deoarece este conditia nu este ndeplinita, se
intra pe ramura altfel.
Avem din nou apel: nainte de acesta se salveaza adresa de memorie
a instructiunii de la care se va continua executia (incrementarea de la
adresa ):
adr =
adr =
adr =
n=1 i=2
n=2
i=4
n=2
n=2 i=4
n=2
n=2
Exemple
Calculul lui n!
Pentru un n natural, definim valoarea n! n mod recursiv astfel:
1,
pentru n = 0
n! =
n (n 1)!, pentru n > 0
(5.1)
S
irul lui Fibonacci
Sirul lui Fibonacci se defineste astfel:
n=0
0,
1,
n=1
f (n) =
f (n 1) + f (n 2), n 2
Vom implementa recursiv acest algoritm:
Start FiboRec
citeste( n )
scrie( F ibo(n) )
Stop
unde algoritmul recursiv F ibo este:
49
(5.2)
Fibo(n)
daca n = 0 sau n = 1 atunci
F ibo = n
altfel
F ibo = F ibo(n 1) + F ibo(n 2)
sfarsit daca
Return
Complexitatea teoretica este data de ecuatia recursiva T (n) = T (n 1) +
T (n 2) + c, care duce la T (n) = (n ), unde = 1+2 5 (taietura de
aur)(a se vedea Anexa A) . Complexitatea exponentiala mare este datorata
faptului ca se calculeaza de mai multe ori aceleasi valori: de exemplu, n
f (n) = f (n 1) + f (n 2), f (n 2) este apelat atat pentru calculul lui
f (n 1), cat si pentru cel de al doilea membru al partii drepte a ecuatiei
anterioare. Redundanta de calcul devine tot mai accentuata pentru valori
mici ale argumentului.
Sa vedem o implementare iterativa:
Start FiboIter
citeste( n )
f (0) = 0
f (1) = 1
pentru i = 2, n
f (i) = f (i 1) + f (i 2)
sfarsit pentru
scrie( f (n) )
Stop
Complexitatea algoritmului anterior este evident (n). Acest fapt se datoreaza memorarii valorilor lui f si evitarii calculului repetat (ca n algoritmul
precedent).
Rezumat
Scrierea recursiva a algoritmilor este deseori mai usoara decat cea iterativa. In esenta, orice apel de subalgoritm (recursiv sau nu) determina
depunerea unor informatii pe o stiva gestionata automat. Codul rezultat
este mai scurt si mai usor de depanat; nsa o implementare recursiva poate
duce la o complexitate crescuta, datorita rezolvarii n mod repetat a acelorasi
probleme, deci este necesara de fiecare data o analiza pertinenta a complexitatii (un exemplu relevant este dat n cele doua implementari pentru
calculul sirului lui Fibonacci de mai sus).
50
Exercitii (rezolv
arile la pagina 172)
1. Sa se calculeze recursiv maximul unui sir.
2. Sa se calculeze recursiv suma elementelor dintrun sir.
3. Sa se calculeze puterea a na a unui numar real a, cu o complexitate
mai buna decat (n).
4. Sa se dea un contraexemplu pentru care algoritmul de la 3 nu da
numarul optim de nmultiri.
5. Sa se scrie un algoritm care calculeaza termenul f ibo(n) al sirului Fibonacci n timp mai bun de (n) (a se veda si problema 3).
6. Sa se dea algoritmi recursivi pentru parcurgerea n preordine, postordine, inordine a unui arbore binar.
51
52
Capitolul 6
Metoda Backtracking
Obiective
Acest capitol introduce una din cele mai generale modalitati de rezolvare
a problemelor: metoda Backtracking. Ea duce la scrierea unor algoritmi care
evita generarea tuturor combinatiilor posibile si apoi la validarea rezultatelor.
Este prezentata schema generala de algoritm backtracking si probleme clasice
pentru a caror rezolvare se foloseste aceasta abordare.
Prezentare general
a
Metoda Backtracking se poate aplica algoritmilor care trebuie sa construiasca o solutie de forma
x = (x(1), x(2), . . . , x(n))
(6.1)
x(k) = a
daca (x(1), x(2), . . . , x(k)) satisface conditiile de continuare atunci
k =k+1
sfarsit daca
altfel
k =k1
sfarsit daca
sf
arsit daca
sf
arsit cat timp
Return
Exemplu
Vom utiliza acest model la construirea unui algoritm pentru problema
asezarii damelor pe tabla de sah astfel ncat oricare doua dame sa nu se
atace (2 dame se ataca daca sunt pe aceesi linie, coloana, diagonala).
Vom considera multimile (Ai )i=1,n ca fiind pozitiile (de la 1 la n) pe care
le poate ocupa o dama pe linia i. Evident ca de data aceasta toate multimile
Ai sunt egale.
Deci o configuratie pe o tabla de n = 5 precum mai jos va fi simbolizata
de vectorul solutie x = (1, 3, 5, 2, 4) si se poate vedea ca n acest caz conditiile
interne sunt respectate (oricare doua dame nu se bat).
,
,
,
,
,
Rezumat
Multe probleme cer generarea uneia/mai multora/tuturor solutiilor care
respecta anumita cerinte. O abordare de tipul genereaza toate combinatiile,
apoi retinele pe cele valide (bruteforce) este posibila, dar si ineficienta.
Metoda backtracking urmareste depistarea cat mai devreme cu putinta a
solutiilor care sunt invalide (pe masura ce componentele lor se genereaza).
Desi algoritmii care rezulta sunt de complexitate exponentiala, multe solutii
admisibile, dar invalide sunt evitate.
56
Exercitii (Rezolv
arile la pagina 175)
1. Scrieti un algoritm pentru parcurgerea tablei de sah cu un cal (calul
sare pe diagonala unui dreptunghi cu laturile de un patrat si doua
patrate). Fiecare patrat al tablei trebui sa fie vizitat exact o singura
data.
2. Avand 4 culori si o harta cu n tari (data printr-o matrice de adiacenta:
aij = 1 daca tara i este vecina cu tara j, 0 altfel), sa se coloreze
harta astfel ca doua tari vecine sa nu aiba aceeasi culoare (doua tari se
considera a fi vecine daca au o frontiera comuna).
3. O organizatie are n componenta sa n barbati si m femei. Sa se scrie
un algoritm care listeaza toate modalitatile n care se poate alcatui o
delegatie care sa contina cel putin k femei, k < m.
4. Cum poate fi platita o suma de x lei, n bancnote de valoare v(i),
i = 1, n din care avem cate b(i) bucati? Sa se dea toate solutiile posibile.
Pentru alte probleme cum ar fi: generarea patratelor magice vezi [1].
57
58
Capitolul 7
Generarea submultimilor
Obiective
De multe ori solutiile care se cer pentru rezolvarea unei (sub)probleme
sunt de o forma particulara: produse carteziene, familia submultimilor, a
combinatiilor, aranjamentelor, permutarilor, submultimilor de suma data.
Acest capitol contine rezolvari clasice pentru aceste probleme, n mai multe
variante.
Schema general
a de lucru
O aplicatie imediata a metodei Backtracking este generarea submultimilor.
Vom considera sumbultimi ale multimii A = {a(1), a(2), . . . , a(n)} cu n elemente. Evident ca avem bijectie ntre multimea indicilor {1, 2, . . . , n} si
elementele multimii A. Exemplu:
{1, 3, 4, 5} {a(1), a(3), a(4), a(5)}
Ca atare, vom presupune pentru simplificarea algoritmilor ca a(i) = i, i
{1, . . . , n}.
Avem la dispozitie urmatoarele modalitati de reprezentare a unei submultimi
X cu i elemente ale lui A:
1. Printrun vector x cu i componente care contine elementele submultimii,
precizanduse valoarea lui i sau completand componentele lui x cu o
valoare neutilizata (de exemplu 0);
2. Analog cu metoda 1, dar plasand elemetele submultimii la sfarsitul
vectorului x;
59
repeta
cheama Generare(x, n, ig)
daca ig = 0 atunci
exit{Iesi din ciclare}
altfel
cheama Prelucrare(x, n){prelucreaza elementul proaspat obtinut al
submultimii}
sf
arsit daca
p
an
a cand f als
In majoritatea algoritmilor pe care i vom prezenta, vectorii vor fi generati
n ordine lexicografica1 , ceea ce corespunde strategiei generale Backtracking.
In algoritmul urmator elementele produsului cartezian vor fi generate succesiv n vectorul x cu m elemente, unde o configuratie x = (x(1), x(2), . . . , x(m))
va desemna elementul (A1 (x(1)), A2 (x(2)), . . . , Am (x(m))) al produsului cartezian.
Vom porni deci cu un element initial x = (1, 1, . . . , 1) care nseamna ca
din fiecare multime Ai luam primul element. In continuare vom determina
cel mai mare indice i cu proprietatea ca x(i) < n(i) si vom alege ca succesor
al lui x = (x(1), x(2), . . . , x(m)) elementul x = (x(1), x(2), . . . , x(i1), x(i)+
1, 1, . . . , 1).
Daca pentru fiecare i {1, . . . , m} avem x(i) = n(i) nseamna ca aceasta
a fost ultima componenta a produsului cartezian si generarea s-a terminat.
Algoritmul pentru generarea produsului cartezian este:
ProdusCartezian(x, m, n, ig)
dac
a ig = 0 atunci
pentru i = 1, m
1
Dou
a siruri x = (x(1), . . . , x(n)) si y = (y(1), . . . , y(n)) sunt n ordine lexicografica
strict cresc
atoare dac
a exist
a k 1, k < n astfel ncat x(i) = y(i) pentru orice i k si
x(k + 1) < y(k + 1). Exemplu: (1, 1, 2) < (1, 2, 1) n sens lexicografic. Pentru cazul n care
x si y au forma general
a x = (x(1), . . . , x(m)) si y = (y(1), . . . , y(n)), daca exista k m, n
astfel nc
at (x(1), . . . , x(k)) < (y(1), . . . , y(k)) atunci x este mai mic (lexicografic) decat y.
Daca un astfel de k nu exist
a (adic
a x(1) = y(1), . . . , x(p) = y(p) unde p este min(m, n)),
atunci cel mai scurt dintre vectori este considerat cel mai mic. Exemplu: (1, 2) < (1, 3, 4),
(1, 2) < (1, 2, 3).
61
x(i) = 1
sfarsit pentru
ig = 1
Return
sfarsit daca
pentru i = m, 1, 1
daca x(i) < n(i) atunci
x(i) = x(i) + 1
Return
altfel
x(i) = 1
sfarsit daca
sfarsit pentru
ig = 0
Return
sf
arsit daca
pentru i = n, 1, 1
daca x(i) < i atunci
x(i) = x(i) + 1
pentru j = i + 1, n
x(j) = x(j 1) + 1
sfarsit pentru
Return
sf
arsit daca{x(i) < i}
sf
arsit pentru
ig = 0
Return
63
x(i) = i
sfarsit pentru
ig = 1
Return
sfarsit daca
pentru i = m, 1, 1
daca x(i) < n m + i atunci
x(i) = x(i) + 1
pentru j = i + 1, m
x(j) = x(j 1) + 1
sfarsit pentru
Return
sfarsit daca
sfarsit pentru
ig = 0
Return
A doua metoda foloseste reprezentarea elementelor prin vectorul caracteristic, generat n ordine lexicografica crescatoare. Primul element va fi:
x = (0, 0, . . . , 0, 1, 1, . . . , 1) vectorul cu ultimele m pozitii egale cu 1 si 0 n
rest.
Fie acum vectorul x caruia vrem sa i determinam succesorul x0 ; pentru
aceasta vom pune n evidenta ultima secventa de cifre 1:
X =(. . . , 0,
1,
a
...
1,
b
0, . . . , 0)
unde a este indicele unde ncepe aceasta secventa iar b indicele ultimului
element egal cu 1.
Trecerea la x0 depinde de faptul ca ultima secventa contine un singur
element (a = b) sau mai multe elemente (a < b).
Daca a = b atunci x0 (a 1) = 1, x0 (a) = 0 si n rest x0 (i) = x(i) va
determina configuratia urmatoare.
Daca a < b atunci rezulta ca x(a 1) = 0. Sa notam cu l1 lungimea
secventei de elemente 1, deci l1 = b a + 1 si cu l0 lungimea secventei de
zerouri de la sfarsitul lui x, deci l0 = n b. Rezulta ca l1 2 si l0 0.
Trecerea de la x la x0 se poate vedea usor n urmatoarea schema:
x = (. . . , 0,
1,
a
l1
...,
64
1,
b
0,
l0
...,
0)
x = (. . . , 1,
0,
a
...,
1,
n l1 + 2
l1 1
. . . , 1)
(7.1)
p(i) = i
sf
arsit pentru
ig = 1
Return
sf
arsit daca
i=n1
c
at timp p(i) > p(i + 1)
i=i1
daca i = 0 atunci
ig = 0
Return
sf
arsit daca
sf
arsit cat timp
k = n{ cautarea lui k}
c
at timp p(i) > p(k)
k =k1
sf
arsit cat timp
{schimba p(i) cu p(k)}
p(i) p(k)3
{calculeaz
a mijlocul secventei}
m = ni
{[x] este partea ntreaga a lui x}
2
pentru j = 1, m
p(i + j) p(n + 1 j)
sf
arsit pentru
Return
Metoda pe care o prezentam n continuare se bazeaza pe generarea recursiva
a permutarilor. Daca avem Sk multimea permutarilor de k elemente de forma
p = (p(1), . . . , p(k)) Sk , atunci Sk+1 va contine pe baza fiecarei permutari p
cate k +1 permutari P 0 : (p(1), . . . , p(k), k +1), (p(1), . . . , p(k 1), k +1, p(k)),
. . . , (p(1), k + 1, p(2), . . . , p(k)), (k + 1, p(1), . . . , p(k)). Deci permutarile Sn
vor fi frunzele unui arbore construit astfel:
- radacina contine 1;
- descendentii unei permutari p = (p(1), . . . , p(k)) sunt cele k + 1 permutari descrise mai sus
Putem atasa acestui arbore unul n care toate permutarile sunt de n elemente, prin completarea permutarii de k elemente cu componentele (k +
1, k + 2, . . . , n).
3
Notatia a b nseamn
a interschimba valoarea lui a cu valoarea lui b, care se
efectueaz
a prin urm
atoarea secvent
a: aux = a, a = b, b = aux.
67
Return
sf
arsit pentru
ig = 0
Return
unde PermCircStanga(p, n, i) este urmatorul algoritm:
PermCirc(p, n, i)
x = p(1)
pentru j = 1, i 1
p(j) = p(j + 1)
sf
arsit pentru
p(i) = x
Return
sfarsit pentru
ig1 = 1
Return
sfarsit daca
sfarsit daca
Return
Sa observam ca metoda nu genereaza aranjamentele n ordine lexicografica
crescatoare. Urmatoarea metoda urmareste sa pastreze aceasta ordine, conform strategiei generale Backtracking.
Fie x = (x(1), . . . , x(m)) un aranjament oarecare. Pentru determinarea
lui x0 , aranjamentul succesor, n ordine lexicografica, vom determina mai ntai
indicele i, cel mai mare indice care poate fi marit. Un x(i) nu poate fi marit
daca 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 arata ca valoarea k este
disponibila iar disp(k) = 1 arata ca valoarea x(k) nu este disponibila (este
deja folosita n x). Cand se gaseste valoarea x cu proprietatea mentionata,
x(i), x(i + 1), . . . , x(m) vor primi cele mai mici valori disponibile, n ordine
crescatoare. Rezulta de aici ca pana la gasirea lui i toate valorile cercetate
regresiv vor fi facute disponibile.
Obtinem n acest fel urmatorul algoritm:
Aranj2(x, n, m, ig, disp)
daca ig = 0 atunci
pentru i = 1, m
x(i) = i
disp(i) = 1
sfarsit pentru
pentru i = m + 1, n
disp(i) = 0
sfarsit pentru
ig = 1
Return
sfarsit daca
pentru i = m, 1, 1
disp(x(i)) = 0
pentru j = x(i) + 1, n
daca disp(j) = 0 atunci
x(i) = j
disp(j) = 1
k=0
pentru l = i + 1, m
70
repeta
k =k+1
pana cand disp(k) = 0
x(l) = k;
disp(k) = 1
sfarsit pentru
Return
sfarsit daca
sf
arsit pentru
sf
arsit pentru
ig = 0
Return
x(i) a(i) = M
(7.3)
i=1
x(i) a(i) M
(7.4)
i=1
si
k
X
n
X
x(i) a(i) +
i=1
a(i) M
(7.5)
i=k+1
Vom considera
S=
k
X
x(i) a(i)
i=1
71
(7.6)
si
R=
n
X
a(i)
(7.7)
i=k+1
sfarsit pentru
altfel
S = S x(k) a(k)
R = R + a(k)
x(k) = 1
k =k1
sfarsit daca
c
at timp k > 0
daca x(k) < 1 atunci
x(k) = x(k) + 1
S = S + x(k) a(k)
daca S = M atunci
ig = 1
Return
sfarsit daca
daca S < M si S + R M atunci
R = R x(k)
k =k+1
sfarsit daca
daca S + R < M atunci
S = S + x(k) a(k)
R = R + a(k)
x(k) = 1
k =k1
sfarsit daca
altfel
S = S a(k)
72
R = R + a(k)
x(k) = 1
k =k1
sf
arsit daca
sf
arsit cat timp
ig = 0
Varianta recursiva, mult mai usor de urmarit este data mai jos. Variabila
ramas este egala cu M S din algoritmul precedent.
SubmultimiRecursiv(a, x, n, k, ramas)
dac
a ramas = 0 atunci
cheama Prelucrare(a, x, k 1)
Return
sf
arsit daca
dac
a k > n sau ramas < 0 atunci
Return
sf
arsit daca
pentru x[k] = 0, 1
cheama SubmultimiRecursiv(a, x, n, k + 1, ramas x[k] a[k])
sf
arsit pentru
Return
Rezumat
Pentru determinarea unor submultimi (de o natura particulara) a unei
multimi exista algoritmi specializati. Se pot da, evident, mai multi algoritmi
pentru generarea unei solutii. Aceste tipuri de probleme se ntalnesc adeseori
sub o forma mascata n enuntul altor probleme, sau ca etapa intermediara
ntrun caz mai mare.
Exercitii (rezolv
arile la pagina 182)
1. Sa se scrie algoritmi recursivi pentru generarea tipurilor de submultimi
din acest capitol.
73
74
Capitolul 8
Metoda Divide et Impera
Obiective
Capitolul prezinta metoda Divide et Impera, ce se poate utiliza atunci
cand rezolvarea unei probleme se face prin spargerea ei n cazuri de dimensiune mai mica (dar de aceeasi natura cu problema initiala), iar apoi prin
recombinarea solutiilor obtinute. Determinarea complexitatii se reduce la
rezolvarea unei recurente, pentru care o unealta utila este Teorema centrala.
Este dat cel mai eficient algoritm de sortare (Quicksort) si se demonstreaza
o limita inferioara pentru algoritmii de sortate bazati pe comparatii.
Prezentare general
a
Aceasta metoda nu este ceva nou pentru noi. Am rezolvat cu ea problema sortarii prin interclasare n capitolul 3, pagina 24. Metoda consta n
urmatoarele etape:
1. Partitionarea problemei initiale n mai multe probleme mai mici;
2. Rezolvarea (de obicei recursiva) a fiecarei subprobleme; daca o subproblema este de dimensiune suficient de mica, atunci ea se poate rezolva
printro metoda ad-hoc, care poate fi mai putin performanta pentru
cazuri de dimensiune mare;
3. Combinarea rezultatelor partiale pentru a obtine rezultatul problemei
initiale.
Daca o problema a fost divizata n a subprobleme care au dimensiunea nb
si sunt rezolvate recursiv, atunci complexitatea este data de formula:
n
T (n) = aT
+ f (n)
(8.1)
b
75
Metoda I
O mutare poate fi scrisa ca o pereche (i, j) cu i {1, 2, 3} si i 6= j,
semnificand ca se muta discul cel mai mic de pe tija i pe tija j. Mai notam
cu H(m, i, j) sirul mutarilor necesare pentru a muta primele m discuri (cele
mai de sus) de pe tija i pe tija j. In aceste ipoteze, problema se reduce la a
determina sirul de mutari H(m, i, j). Observam ca putem scrie:
(i,j),
pentru m = 1
H(m, i, j) =
(8.3)
H(m-1, i, k),(i,j),H(m-1, k, j) pentru m 6= 1
unde k = 6 i j este tija diferita de tijele i si j.
Cu aceasta reducem rezolvarea problemei cu n discuri la rezolvarea a
doua probleme cu n 1 discuri. Algoritmul se bazeaza pe ideea ca n loc
de H(m, i, j) putem scrie H(i, j), memorand valoarea lui m separat. Se
mai poate observa ca H(i, j) se transforma n H(i, k), (i, j), H(k, j) cu
k = 6 i j. Avand n mijloc tot perechea (i, j), rezulta ca imediat ce
o pereche este generata, ea poate fi nscrisa direct pe locul ei. Algoritmul
obtine cele 2n 1 mutari folosind n acest scop o matrice M cu 2 linii si
2n 1 coloane, fiecare coloana memorand o mutare. Incepem prin a nscrie
perechea (1,2) pe coloana 2n1 . Perechile ce vor lua nastere din ea vor fi
nscrise pe coloanele 2n2 si 2n1 + 2n2 . Se observa ca acest algoritm face
parte din clasa algoritmilor Divide et Impera.
Mai general, pentru fiecare valoare a lui m {n, n 1, . . . , 2} se expandeaza perechile din coloanele c de forma (c = M2m + 2m1 )1 , perechile
care rezulta din cea din coloana c fiind nscrise pe coloanele c2m2 , c+2m2 .
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)
1
78
j = M (2, l)
k =6ij
M (1, l k3) = i
M (2, l k3) = k
M (1, l + k3) = k
M (2, l + k3) = j
sf
arsit pentru
k1 = k2
k2 = k3
k3 = k3/2
sf
arsit pentru
Metoda a IIa
Varianta recursiva este bazata tot pe metoda Divide et Impera. Aceasta
metoda este eleganta din punct de vedere al redactarii, dar ineficienta din
cauza spatiului ocupat n stiva si a ntarzierilor datorate apelurilor n cascada
a procedurii.
Hanoi2(n, i, j)
dac
a n = 1 atunci
muta discul superior de pe tija i pe tija j
altfel
cheama Hanoi2(n 1, i, 6 i j)
muta discul superior de pe tija i pe tija j
cheama Hanoi2(n 1, 6 i j, j)
sf
arsit daca
Return
Metoda a IIIa
Vom porni de la algoritmul precedent, pe care l vom transforma ntr
unul iterativ. Apelul Hanoi2(4,1,2) va duce la arborele de apeluri recursive
din figura 8.1, unde n fiecare nod este memorat un triplet de forma (n, i, j).
Prin parcurgerea n inordine a acestui arbore binar si afisarea pentru
fiecare nod a ultimelor doua numere memorate n el, se ajunge la determinarea solutiei cerute. Pentru parcurgere se va folosi o stiva S n care se
79
vor memora triplete de forma (a, b, c). Arborele nu este construit n mod
explicit.
Hanoi3(n)
i=1
j=2
gata = f als
niv = 1{pornim de la radacina, care se afla pe nivelul 1}
S = {S este stiva}
con = 0
repeta
cat timp niv < n 1
niv = niv + 1
(i, j, niv) S
j =6ij
sfarsit cat timp
scrie( i, 6 i j )
scrie( i, j )
scrie( 6 i j, j )
daca S = atunci
gata = adevarat
altfel
(i, j, niv) S
scrie( i, j )
i=6ij
sfarsit daca
p
ana cand gata = adevarat
Return
Observatie: Pentru implementarea stivei se poate folosi o matrice cu n 2
linii si 3 coloane.
6 3 2 6 3 1 2 8
j
81
6 6 8
j
3 6 6
j i
3 2 1
1 3
i
3
j
2 3
j
2
i
3
j
2 3
2
j
3
i
2 3
TQ (1) + TQ (n 1) + TP (n) =
TQ (n 1) + TP (n) + c =
TQ (1) + TQ (n 2) + TP (n 1) + TP (n) + c =
TQ (n 2) + TP (n 1) + TP (n) + 2c =
=
n
X
= TQ (2) +
TP (k) + (n 2)c =
TQ (n) =
=
=
=
=
k=3
= TQ (1) + TQ (1) +
=
n
X
k=2
n
X
TP (k) + cn =
TP (k) + (n 2)c =
k=2
n
X
c k + c n = (n2 )
k=2
2
n! ' 2n
e
obtinem ca:
h log2 n! > n log2 n n log2 e = (n log2 n)
Se obtine ca T (n) = (n log2 n). Am demonstrat deci ca pentru algoritmii care se bazeaza doar pe comparatii complexitatea nu poate sa scada
sub n log2 n. Pentru cazuri speciale ale sirului ce trebuie sortat, a se vedea
capitolul 9.
Rezumat
Metoda Divide et Impera se foloseste atunci cand rezolvarea unei probleme se poate face prin descompunerea ei n cazuri de dimensiune mai mica,
rezolvarea acestora dupa acelasi principiu, apoi combinarea rezultatelor lor.
Deseori algoritmii de acest tip se scriu recursiv (datorita naturii lor), iar determinarea complexitatii se face prin rezolvarea unei ecuatii recursive, a carei
solutii se poate da de multe ori pe baza Teoremei centrale.
Un exemplu important de algoritm de tip divide et Impera este Quicksort.
Problemele de la sfarsitul capitolului dau si alte exemple ale utilitatii metodei.
Exercitii (rezolv
arile la pagina 184)
1. Bazanduva pe observatia ca mutarea din mijloc se stie, scrieti un
algoritm iterativ pentru problema turnurilor din Hanoi, punand mereu
mijloacele intervalelor obtinute prin njumatatire ntr-un sir cu numarul
cunoscut de mutari.
2. Avand la intrare un sir ordonat crescator, sa se scrie un algoritm de
tip Divide et Impera pentru gasirea pozitiei unui element de valoare
data x, daca el exista. Se va folosi metoda njumatatirii intervalului dat
de numarul de elemente al sirului. Care este complexitatea cautarii?
3. Rezolvati aceeasi problema prin cautare secventiala (testarea pe rand
a elementelor). Care este n acest caz complexitatea?
2
Se poate ar
ata c
a:
2nn+ 2 en e(12n+1)
84
< n! <
2nn+ 2 en e(12n)
85
86
Capitolul 9
Sortare si statistici de ordine
Obiective
In acest capitol ne propunem sa rezolvam urmatoarele probleme, relative
la un sir cu n elemente:
1. sa se gaseasca a i-a componenta n ordine statistica a sirului
2. sa se gaseasca mediana (componenta din mijloc) unui sir
3. sa se gaseasca cele mai mici doua elemente dintrun sir
4. determinarea simultana a minimului si maximului
Observatie: Vom considera ca sirul este cu elemente distincte n rezolvarea
tuturor problemelor enuntate, urmand apoi sa extindem rezultatele obtinute
si pentru situatia n care elementele se repeta.
Vor fi considerati mai multi algoritmi care rezolva aceasta problema.
Capitolul contine si prezentari ale altor algoritmi de sortare (heapsort, cu
care ocazie se introduce si structura de date numita heap, sortari n timp
liniar).
Exemplific
ari, generalit
ati
Exemple: Fie x = (5, 4, 2, 8, 9, 1, 7). Ce se ntelege prin a i-a componenta
n ordine statistica? Avem n = 7 si n mod evident 1 i 7. Daca l
consider pe i = 3, vom ntelege prin a i-a componenta n ordine statistica a
sirului x elementul al treilea din sirul x sortat n ordine crescatoare.
x = (1, 2, 4, 5, 7, 8, 9),
87
x(3) = 4
Determinarea simultan
a a minimului si a maximului
MinMax(x, n)
min = x(1)
max = x(1)
pentru i = 2, n, 1
daca x(i) < min atunci
min = x(i)
altfel
daca x(i) > max atunci
max = x(i)
sfarsit daca
sfarsit daca
sfarsit pentru
scrie( minimul este , min )
scrie( maximul este , max )
Return
Numarul de comparatii ntre elemente necesare n determinarea independenta a min si max este 2(n 1) (prin doua parcurgeri ale sirului), n schimb
pentru determinarea simultana a min si max avem nevoie de 3 dn/2e 2
comparatii (demonstrati acest lucru). Sugeram cititorului sa verifice algoritmul pentru intrarea x = (1, 2, 3, 4, 5, 6, 85, 2).
88
G
asirea celor mai mici dou
a elemente dintrun
sir
Ideea algoritmului prezentat mai jos este urmatoarea: se compara elementele 1 cu 2, 3 cu 4, etc; pentru fiecare pereche comparata se determina
cel mai mic. Aceste elemente castigatoare se vor compara la randul lor pe
perechi adiacente, ca mai sus. Cand ramane un singur element, acesta este
minimul sirului. Atasam acestor operatii un arbore binar (posibil nestrict)
echilibrat, construit n felul urmator: exista n noduri terminale (frunze),
fiecare continand drept informatie cate un element din sir; un nod neterminal are drept valoare minimul din cei doi fii (figura 9).
(a)(b)
ArArborele
borele
dede
comparat
comparat
iiii
penpentru
tru
vecvectorul
torul
(7,(7,
6,6,
4,4,
5,5,
2,2,
3,1,
1)3)
90
Problema selectiei
Selectie n timp mediu liniar
In general determinarea celei de a ia componente este o problema avand
ca date de intrare un sir a cu n elemente distincte si un numar i cu proprietatea 1 i n, iar ca data de iesire un element rez al sirului a care are
proprietatea ca exista exact i 1 elemente ale sirului mai mici decat rez.
Sel(A, p, r, i, rez)
dac
a p = r atunci
rez = a(p)
Return
sf
arsit daca
Part(A, p, r, k){Part este algoritmul de partitionare din QuickSort}
dac
a i k atunci
Sel(a, p, k, i, rez)
altfel
Sel(a, k + 1, r, i k, rez)
sf
arsit daca
Return
Algoritmul Sel aplicat unui sir deja sortat are (cel mai rau caz) complexitatea O(n2 ). Se poate arata (vezi lucrarile [5], [2]) ca pentru cazul mediu
complexitatea este de O(n).
91
Heapsort
Fie vectorul:
v = 17 13 9 8 7 9 3 2 4 1
1 2 3 4 5 6 7 8 9 10
Arborele atasat este cel din Figura 9.2 (a se revedea sectiunea 4, pagina
40). Acest arbore are adancimea de blog2 nc.
92
Figura 9.3: Obtinerea unui maxheap din sirul (5, 3, 2, 1, 10, 4, 15, 11, 9, 6).
Complexitatea: pentru un nod de naltime h, complexitatea algoritmului
Heapify este de (h). Se poate arata ca pentru un heap cu n noduri exn
ista cel mult b 2h+1
c noduri de naltime h. Deci timpul de executie pentru
ConstruiesteHeap este:
blog2 nc
blog2 nc l
m
X
X
h
n
O(h) = O n
T (n) =
h+1
2
2h
h=0
h=0
Dar
blog2 nc
X h
X
h
1/2
=
=2
h
h
2
2
(1 1/2)2
h=0
h=0
Algoritmul sort
arii prin num
arare (CountSort)
Un astfel de algoritm este algoritmul de sortare prin numarare numit
CountSort. Fie (x(i))i=1,n un sir de numere naturale strict pozitive si k
maximul acestui sir. Sortarea prin numarare consta n gasirea a cate elemente
sunt mai mici decat x(i) (pentru fiecare i); n acest fel vom sti pe ce pozitie
trebuie sa se afle x(i) n sirul sortat (daca sunt m elemente mai mici decat
x(i), atunci el ar trebui sa se afle pe pozitia m + 1 n sirul sortat).
Consideram sirul initial x = (x(1), x(2), . . . , x(n)); y = (y(1), y(2), . . . , y(n))
va fi sirul sortat, iar c = (c(1), c(2), . . . c(k)) este un sir ajutator.
CountSort(x, n, y, k)
pentru i = 1, k
c(i) = 0
sf
arsit pentru
pentru j = 1, n
c(x(j)) = c(x(j)) + 1
sf
arsit pentru
pentru i = 2, k
95
(prima cifra). Esential este ca daca sortarea se face dupa cifra i (numerotarea
cifrelor o consideram de la stanga spre dreapta, de la cifra cea mai semnificativa la cea cea mai putin semnificativa), atunci se pleaca de la ordinea
obtinuta anterior pentru cifra i + 1 (1 i < d); n al doilea rand, algoritmul
care este folosit pentru sortarea cifrelor trebuie sa aiba urmatoarea proprietate: daca ntrun sir x = (x(1), . . . , x(n)) avem ca x(i) = x(j) si i < j, prin
sortare se ajunge ca x(i) sa fie repartizat pe pozitia i0 , x(j) sa fie repartizat
pe pozitia j 0 , iar i0 < j 0 (spunem ca algoritmul de sortare este stabil ).
In pseudocod, algoritmul este:
OrdonarePeBazaCifrelor(a, d)
pentru i = d, 1, 1
sorteaza stabil tabloul a dupa cifra i
sf
arsit pentru
Return
Analiza algoritmului depinde n mod evident de complexitatea algoritmului
prin care se face sortarea pe baza unei cifre. De exemplu, daca numerele sunt
n baza b, se poate folosi sortarea prin numarare din sectiunea precedenta
(care este stabil). Pentru fiecare cifra dupa care se face sortarea avem timpul
(n + b), iar pentru sortarea completa (ciclul pentru) complexitatea este
(d(n + b)). Daca d este constant si b = O(n), atunci complexitatea este
liniara.
Sortare pe grupe
Pentru algoritmul prezentat aici, n ipoteza n care numerele sunt uniform
distribuite (daca consideram intervalul dat de minimul si maximul din sir si
daca mpartim acest interval n k subintervale de lungime egala, atunci sansa
ca un numar oarecare din sir sa se gaseasca n oricare din cele k subintervale
este aceeasi; altfel spus, nu exsite zone n care numerele sa se concentreze n
numar mult mai mare decat n alte regiuni de aceeasi dimensiune), se poate
da un algoritm care n medie are complexitatea (n).
Vom considera fara a restrange generalitatea ca numerele sunt n intervalul [0, 1) (daca nu sunt, atunci putem proceda astfel: determinam min =
min x(i), i = 1, n si max = max x(i), i = 1, n; se efectueaza x(i) = x(i)min,
pentru i = 1, n - n acest fel se aduc numerele n intervalul [0, max min];
apoi se face mpartirea x(i) = x(i)/(max min + 1), prin care se aduc numerele la intervalul [0, 1); dupa sortare se fac operatiile inverse, n ordinea
nmultire, adunare). principiul algoritmului consta n a mparti intervalul
[0, 1] n n intervale egale si sa introducem cele n numere n cele n grupe.
Daca numerele sunt uniform distribuite, nu ar trebui sa apara diferente mari
97
ntre grupe, din punct de vedere a cate numere contin. Se sorteaza numerele
din fiecare grupa, dupa care se trece prin fiecare grupa si se obtine ordinea
crescatoare.
Algoritmul de mai jos considera ca fiecare grupa este implementata ca o
lista nlantuita. Pentru subintervalul (k n1 , (k + 1) n1 ), 0 k < n vom avea
lista nlantuita b(k).
SortarePeGrupe(a, n)
pentru i = 1, n
insereaza a(i) n lista b (bn a(i)c)
sfarsit pentru
pentru i = 0, n 1
Sorteaza lista b(i) folosind sortarea prin insertie
sfarsit pentru
Concateneaza listele b(0), b(1), . . . , b(n)
Return
Plecand de la proprietatea ca numerele sunt uniform distribuite, se arata n
[2] ca timpul mediu pentru sortarea pe grupe este O(n).
Rezumat
Notiunea de statistica de ordine generalizeaza pe cele de minim, maxim,
mediana. Selectarea celei de a i-a statistici de ordine se poate realiza n timp
liniar. Pentru cazuri particulare ale vectorilor care se sorteaza, se pot folosi
algoritmi liniari (se depaseste deci bariera de (n log n) demonstrata pentru
algoritmi care lucreaza exclusiv pe baza comparatiilor).
Exercitii (rezolv
arile la pagina 186)
1. Descrieti un algoritm care, fiind data o multime S de n numere ntregi
si distincte si un ntreg pozitiv k n, determina cele k numere care
sunt cele mai apropiate de mediana lui S.
2. Fie a[1 . . . n], b[1 . . . n] doua siruri sortate. Scrieti un algoritm performant care gaseste mediana celor 2n numere.
3. Fiind data o multime de n numere, dorim sa gasim cele mai mari i
numere n ordinea sortata, folosind un algoritm bazat pe comparatii.
Sa se scrie mai multe variante si sa se compare din punct de vedere al
complexitatii.
98
99
100
Capitolul 10
Metoda Greedy
Obiective
Capitolul introduce o noua metoda de rezolvare a problemelor. Metoda
aceasta va arata cum se abordeaza problemele pentru care solutia se poate
construi prin alegeri ale componentelor, alegeri asupra carora nu se revine.
Va trebui sa demonstram corectitudinea algoritmilor (fara de care acesta ar
fi doar un algoritm euristic vezi capitolul 12). Probleme clasice, ale caror
rezultate se pot folosi n cazuri mai generale sunt enuntate si rezolvate (sau
sunt facute trimiteri bibliografice).
Prezentare general
a
Metoda Greedy este o metoda de elaborare a algoritmilor ce determina
solutia unei probleme de optimizare (sau pusa sub aceasta forma) nghitind
(de aici greedy = lacom) cate o componenta n solutia finala. Procedeul este
fara a reveniri cu alegere pe baza unui criteriu local folosind, eventual, o
pregatire prealabila a datelor.
Modul de rezolvare al problemelor dat de aceasta strategie este de a construi solutia componenta cu componenta. Se pleaca de la solutia vida si la
fiecare pas al algoritmului se selecteaza o valoare pentru o componenta; daca
aceasta valoare convine, atunci aceasta este pusa n solutie pe componenta
respectiva. Metoda seamana cu metoda backtracking, dar, spre deosebire de
aceasta, nu revine sa nlocuiasca componente o data fixate.
Strategia descrisa anterior are un posibil inconvenient dat de modul de
selectie. Daca la fiecare pas alegem cea mai buna valoare n spiritul unui
criteriu local nu este sigur ca n final vom obtine solutia optima cautata.
Metoda trebuie completata cu o demonstratie ca optimul obtinut prin pasi
101
de optim local , este un optim global. Altfel, daca solutia gasita se aproapie
de solutia optima spunem ca avem o solutie Greedy eurisitc
a (vezi capitolul
12).
Procedura Greedy are deci urmatoarea forma:
Greedy(x)
x=
c
at timp solutia nu este completa
selecteaza o valoare a
daca valoarea a convine atunci
x = x {a}
sfarsit daca
sfarsit cat timp
Return
Pentru a elabora un algoritm prin metoda greedy vom parcurge etapele:
1. Pregatirea datelor ( de obicei sortari)
2. Elaborarea unui criteriu local de alegere
3. Elaborarea algoritmului dupa schema de mai sus
4. Demonstrarea corectitudinii alegerii facute aratand ca solutia x respecta criteriul global. Altfel se ncearca sa se determine cat de departe
este solutia gasita de algoritm fata de solutia cautata.
Complexitatea solutiei Greedy este data de complexitatea pregatirii datelor,
de complexitatea selectiei valorii la fiecare pas si de numarul de selectii facute
de catre algoritm. In general, metodele de tip Greedy sunt metode de complexitate polinomiala, ceea ce ne face sa catam o solutie de acest tip, n prima
instanta.
Dam, n continuare cateva exemple clasice:
Arborele Huffman
Una din cele mai interesante probleme este urmatoarea:
Fie S = {s1 , s2 , . . . , sn } este o multime de semnale si p = (p(i), i = 1, n)
probabilitatile asociate lui S. Sa se determine codul de lugime optima asociat
lui S.
De exemplu alfabetul Morse este un astfel de cod. O caracteristica a
acestui cod este aceea ca simbolurile cele mai probabile sunt codificate cu mai
putine caractere, astfel ncat, la tramsmiterea unui mesaj, costul transmisiei
sa fie minim. Mai ntai cateva notiuni de teoria codurilor.
Un cod binar este o aplicatie injectiva : S {0, 1}+ . Lungimea medie
aPcodului cu sistemul de probabilitati dat mai sus este definita de L() =
n
arul de caractere a lui (si )). Vom
i=1 |(si )|p(i) (unde |(si )| este num
spune ca un cod este optim daca minimizeaza lungimea medie L() . Asociem
unui cod instantaneu : S {0, 1}+ arborele T care are urmatoarele
proprietati:
- numarul de frunze este egal cu numarul de semnale ale lui S;
- drumul, n arbore, de la radacina la orice frunza s S este o codificarea
(s) prin concatenarea marcajelor muchiilor.
Lungimea medie a unui cod este data de lungimea medie a arborelui asociat,
adica:
n
n
X
X
L() =
|(si )|p(i) =
nivT (si )p(i) = L(T )
i=1
i=1
L(H) =
n
X
niv(si ) =
i=1
n2
X
i=1
n2
X
i=1
+ p(n 1) + p(n) =
n2
X
=
niv(si ))p(i) + p(n 1, n)niv(sn,n1 ) + p(n 1) + p(n)
i=1
Interclasare optimal
a
Avand la intrare o multime de vectori sortati crescator se cere algoritmul
care arata ordinea interclasarii acestora cu numarul minim de comparatii.
Avem deci la intrare: n - numarul de vectori, nr = (nr(i), i = 1, n) numarul de elemente din fiecare vector, x(i) = (x(i, j), j = 1, nr(i)) sirul de
vectori.
107
pentru k = 1, n1 + n2
x(j + 1, k) = y(k)
sf
arsit pentru
nr(j + 1) = n1 + n2
sf
arsit pentru
Return
In algoritmul de mai sus procedura Sortare face rearanjarea elementelor (liniilor) matricii x descrescator n functie de vectorul nr iar Inter face interclasare directa a doi vectori.
Pentru a analiza corectitudinea algoritmului definim arborele asociat unei
ordini de interclasare:
1. Unui vector i asociem un nod care are ca informatie numarul de elemente ale vectorului;
2. pentru interclasarea a doi vectori x(1) si x(2), arborele asociat este
reprezentat grafic n figura 10.2;
3. Daca avem T1 arborele asociat sirului de vectori x(i1 ), . . . , x(ik ) si T2
arborele asociat sirului de vectori x(ik+1 ), . . . , x(in ) atunci T din figura
10.3 va fi arborele asociat prin strategie sirului de vectori x(i1 ), . . . , x(in ).
Rezumat
Metoda Greedy nseamna construirea pas cu pas a solutiei, prin alegerea
componentelor pe baza unei strategii deduse din enuntul problemei. Spre
deosebire de metoda Backtracking, nu se revine asupra unei decizii. Metoda
greedy poate fi folosita si pentru elaborarea unor algoritmi euristici (care nu
determina optimul, sau pentru care o demonstratie de corectitudine nu este
data). Pentru fiecare rezolvare greedy trebuie sa se ncerce justificarea prin
metode matematice a optimalitatii solutiei.
Exercitii (rezolv
arile la pagina 190)
1. Daca x = (x(i), i = 1, m), y = (y(i), i = 1, n) reprezinta doua multimi
de elemente, atunci sa se determine multimea intersectie z = (z(i), i =
1, k).
2. Danduse n obiecte de cost c = (c(i), i = 1, n) cu greutatea g =
(g(i), i = 1, n) si un rucsac de capacitate maxima G, sa se elaboreze un
algoritm de umplere a rucsacului de cost maxim. Un obiect poate fi pus
n rucsac ntrun anumit procent (problema continu
a a rucsacului).
110
n
X
i=1
111
112
Capitolul 11
Programare dinamic
a
Obiective
In acest capitol se prezinta metoda programarii dinamice. Denumirea de
programare nu vine de la un program scris ntrun limbaj oarecare pentru
calculator; denumirea vine de la utilizarea a unor tabele care se completeaza
sistematic. La fel ca la metoda Divide et impera problema initiala este
mpartita n subprobleme si rezultatul se obtine din combinaarea solutiilor
subproblemelor, dar de data aceasta subproblemele nu mai sunt disjuncte, ci
se suprapun partial. Dupa parcurgerea materialului, cititorul ar trebui sa fie
familiarizat cu trasaturile generale ale unei probleme pentru care se apeleaza
la programarea dinamica si sa si nsuseasca strategia generala de rezolvare
a unei probleme folosind aceasta metoda.
Prezentare general
a
Programarea dinamica se aplica problemelor de optimizare sau problemelor care pot fi puse sub aceasta forma.
O problema de optimizare nseamna optimul (maximul sau minimul) uneia sau mai multor functii pe un anumit domeniu (numit al solutiilor admisibile). Solutia unei astfel de probleme are, deci, un dublu aspect. Valoarea
functiei (sau functiilor) obiectiv solutia n spatiul obiectivelor, si punctul
din domeniu pe care se obtine aceasta valoare solutia n spatiul deciziilor.
Pentru realizarea uniu algoritm prin metoda programarii dinamice trebuie
sa parcurgem urmatorii patru pasi:
1. Caracterizarea structurii unei solutii optime ca avand subsolutii optime
2. Definirea recursiva a valorii unei solutii optime
113
produs de matrici este complet parantezat daca este format dintro sigura
matrice sau daca este format din factori complet parantezati.
De exemplu produsul A1 A2 A3 A4 poate fi parantezat astfel:
(A1 (A2 (A3 A4 )))
(A1 ((A2 A3 ) A4 ))
((A1 A2 ) (A3 A4 ))
((A1 (A2 A3 )) A4 )
(((A1 A2 ) A3 ) A4 )
adica n cinci moduri.
Modul n care punem parantezele poate sa dea diferente mari la numarul
de nmultiri de numere. Spunem n mod special cand este vorba de nmultiri
de numere pentru a ne difierentia de nmultirile de matrici.
Pentru a vedea modul n care apar numere diferite de nmultiri de numere,
la parantezari diferite ale produsului de matrici, sa consideram problema
sirului A1 , A2 , A3 .Fixam dimensiunile matricilor la 10 50, 50 5, 5 100.
Daca efectuam nmultirile pentru ((A1 A2 ) A3 ), atunci vom avea 10 50
5 = 2500 nmultiri scalare pentru a efectua (A1 A2 ), care va fi o matrice
de dimensiune 10 5, plus alte 10 5 100 = 5000 nmultiri pentru a
efectua (A1 A2 ) A3 , deci n total rezulta 7500 nmultiri de numere. Pentru
parantezarea (A1 (A2 A3 )) vom avea 505100 = 25000 nmultiri de numere
pentru a efectua A2 A3 , care va fi o matrice de dimensiune 50 100, plus
alte 10 50 100 = 50000 nmultiri de numere pentru a efectua A1 (A2 A3 ).
Deci rezulta un numar de 75000 nmultiri de numere; a doua varianta este
de 10 ori mai proasta.
Cum ar fi sa calculam numarul de nmultiri de numere pentru toate parantezarile posibile?
Sa notam cu P (n), numarul de parantezari distincte al unui sir de n
matrici. Un sir de n matrici se poate mpati printro paranteza pusa ntre
matricile k si k + 1 pentru orice k = 1, . . . , n 1 si apoi putem descompune
n paranteze, n mod independent, fiecare din siruri. Obtinem, n acest mod,
ecuatia de recurenta:
daca n = 1
1
n1
P
(11.1)
P (n) =
P (k)P (n k) daca n 2
k=1
Matrice
A1
A2
A3
A4
A5
A6
A7
Dimensiune
25 35
35 15
15 5
5 10
10 20
20 10
10 20
2
13125
0
0
0
0
0
0
3
0
2625
0
0
0
0
0
4
0
0
750
0
0
0
0
1
5
6
7
0
0
0
0
0
0
0
0
0
0
0
0
0
1000
0
0
0
2000
0
0
0
0
4000 0
0
0
0
0
2
1
0
0
0
0
0
0
3
0
2
0
0
0
0
0
4
0
0
3
0
0
0
0
5
0
0
0
4
0
0
0
6
0
0
0
0
5
0
0
7
0
0
0
0
0
6
0
1
0
0
0
0
0
0
0
2
13125
0
0
0
0
0
0
3
7000
2625
0
0
0
0
0
4
5
6
7
1
0
0
0
0
0
0
4375
0
0
0
750 2500
0
0
0
0
0
1000 2000
0
0
0
2000 4000 0
0
0
0
4000 0
0
0
0
0
0
2
1
0
0
0
0
0
0
3
1
2
0
0
0
0
0
4
0
3
3
0
0
0
0
5
0
0
3
4
0
0
0
6
0
0
0
5
5
0
0
7
0
0
0
0
6
6
0
1
0
0
0
0
0
0
0
2
13125
0
0
0
0
0
0
3
7000
2625
0
0
0
0
0
4
5
6
7
1
8250
0
0
0
0
4375 7125
0
0
0
0
750 2500 2750
0
0
1000 2000 3000 0
0
0
2000 4000 0
0
0
0
4000 0
0
0
0
0
0
2
1
0
0
0
0
0
0
3
1
2
0
0
0
0
0
4
3
3
3
0
0
0
0
5
0
3
3
4
0
0
0
6
0
0
3
5
5
0
0
7
0
0
0
6
6
6
0
1
0
0
0
0
0
0
0
2
13125
0
0
0
0
0
0
3
4
5
6
7
1
7000 8250 10500
0
0
0
0
2625 4375 7125 6375
0
0
750 2500 2750 4500 0
0
0
1000 2000 3000 0
0
0
0
2000 4000 0
0
0
0
0
4000 0
0
0
0
0
0
0
2
1
0
0
0
0
0
0
3
1
2
0
0
0
0
0
4
3
3
3
0
0
0
0
5
3
3
3
4
0
0
0
6
0
3
3
5
5
0
0
7
0
0
3
6
6
6
0
1
0
0
0
0
0
0
0
2
13125
0
0
0
0
0
0
3
4
5
6
7
1
7000 8250 10500 10250
0
0
2625 4375 7125 6375 9125 0
0
750 2500 2750 4500 0
0
0
1000 2000 3000 0
0
0
0
2000 4000 0
0
0
0
0
4000 0
0
0
0
0
0
0
2
1
0
0
0
0
0
0
3
1
2
0
0
0
0
0
4
3
3
3
0
0
0
0
5
3
3
3
4
0
0
0
6
3
3
3
5
5
0
0
7
0
3
3
6
6
6
0
Tot din tabel deducem ca avem o paranteza dupa A3 (s(1, 7)), apoi ntre
A1 si A3 dupa cum spune s(1, 3) paranteza este dupa A1 , iar ntre A4 si
A7 paranteza este dupa A5 (vezi s(4, 7)) si ntre A6 si A7 , dupa cum spune
s(6, 7) = 6, paranteza este dupa A6 . Deci parantezarea va arata astfel:
(A1 (A2 A3 ))(((A4 A5 )A6 )A7 ).
Constatam usor ca timpul de executie este (n3 ).
119
1
0
0
0
0
0
0
0
2
13125
0
0
0
0
0
0
3
4
5
6
7000 8250 10500 10250
2625 4375 7125 6375
0
750 2500 2750
0
0
1000 2000
0
0
0
2000
0
0
0
0
0
0
0
0
7
12500
9125
4500
3000
4000
4000
0
1
0
0
0
0
0
0
0
2
1
0
0
0
0
0
0
3
1
2
0
0
0
0
0
4
3
3
3
0
0
0
0
5
3
3
3
4
0
0
0
6
3
3
3
5
5
0
0
7
3
3
3
6
6
6
0
daca i = n
daca i < n
(11.3)
sfarsit daca
sfarsit pentru
sfarsit pentru
LM ax = L(1)
pozLM ax = 1
pentru i = 2, n
daca L(i) > LM ax atunci
LM ax = L(i)
pozLM ax = i
sfarsit daca
sfarsit pentru
scrie( Lungimea maxima a unui subsir crescator este:, LMax )
scrie( a(LMax) )
i = pozLM ax
cat timp sol(i) 6= i
i = sol(i)
scrie( a(i) )
sfarsit cat timp
Stop
Complexitatea acestui algoritm este (n2 ), datorita celor doua cicluri pentru
imbricate. Mentinam ca nu este absolut necesar vectorul sol, dar determinarea solutiei se face mai usor pe baza lui.
Rezumat
Programarea dinamica este o metoda care se folosesste n cazurile n care
optimul general implica optimul partial; n astfel de cazuri, demonstratia se
face de cele mai multe ori prin reducere la absurd. Pentru evitarea calcularii
acelorasi rezultate de mai multe ori, se apeleaza la tabelare rezultatele
pentru cazurile luate n considerare sunt memorate ntro structura de tip
matricial. Pe baza acestor informatii, se poate determina solutia atat n
spatiul oboectivelor cat si n spatiul deciziilor.
Exercitii (rezolv
arile la pagina 196)
1. Gasiti o parantezare optima a produsului unui sir de matrici al carui
sir de dimensiuni este (5, 10, 3, 12, 5, 50, 6) (adica 5 10, 10 3, etc).
2. Aratati ca o parantezare completa a unei expresii cu n elemente are
exact n 1 perechi de paranteze.
122
i=1
n(n+1)(2n+1)
).
6
123
124
Capitolul 12
Algoritmi euristici
Obiective
In acest capitol prezentam, printr-un exemplu, algoritmii euristici.
Prezentare general
a
Un algoritm euristic da o soluie unei probleme de optimizare pentru
care nu avem o demonstratie a optimalitatii, dar exista motive sa credem
ca nu este prea departe de optim. Un astfel de algoritm seamana cu metoda
Greedy, adica construieste solutia pe baza unui criteriu de optim local , dar
lipseste demonstratia optimalitatii.
O problem
a de t
aiere cu dou
a criterii de optimizare.
Avem de a face cu o problema practica: avand de acoperit o camera
dreptunghiulara cu linoleum sau mocheta; materialul se afla n rulou care are
latime fixa si care poate fi taiat tot numai ca un dreptunghi (la o anumita
lungime care trebuie determinata). Cautam o solutie de acoperire a camerei
cu un numar minim de bucati si cu o pierdere minima de material (pierderea
este diferenta dintre materialul cumparat si suprafata camerei).
Pentru nceput vom arata ca cele doua criterii sunt contrarii, apoi vom
construi algoritmul care gaseste puncte de optim Pareto, adica puncte fata
de care nu putem gasi altele cu ambele criterii satisfacute mai bine , adica si
mai mica pierdere si mai putine bucati.
125
nou.a = z.a z.y; nou.b = z.b; nou.x = z.x z.b; nou.y = z.y
altfel
nou.a = z.a; nou.b = z.b z.x; nou.x = z.x; nou.y = z.y z.a
sfarsit dacaS
daca x 6= 0 y 6= 0 atunci
exit
altfel
cheama Arbores(y)
cheama Arbored(y)
sfarsit daca
Return
Unde listare(z) este listarea frunzelor grafului ntrun mod convenabil.
Rezumat
Sunt cazuri n care, sau nu exista demonstratia, sau chiar nu se poate
demonstra ca solutia unui algoritm este cea cautata. Dar, macar din practica,
se vede ca solutia nu este departe de optim. Avem de a face atunci cu un
algoritm euristic. Acesti algoritmi sunt cateodata preferati chiar cand exista
algoritmi exacti, pentru viteza lor superioara.
Exercitii
1. Sa se studieze complexitatea algoritmului de mai sus
2. Sa se transforme algoritmul de mai sus astfel ncat sa se opreasca dupa
un numar stabilit de pasi.
3. Sa se realizeze un algoritm care pastreaza din arborele binar numai
ultimul nivel si cel urmator, care se construieste.
4. Sa se modifice algoritmul astfel ca el sa dea si solutiile n care exista
acoperire dar ramane un rest de material.
Pentru solutii rugam cititorul sa consulte [3] si [4].
128
129
130
Capitolul 13
Algoritmi n algebra liniar
a.
Obiective
In acest capitol prezentam o foarte interesanta aplicatie a metodei divide
et impera la nmultirea matricilor si o metoda de rezolvare a sistemelor de
ecuatii liniare.
A1 A2
A3 A4
B1 B2
B3 B4
(13.1)
(13.2)
(13.4)
care, conform teoremei centrale din capitolul 8 da: T (n) = (n3 ) adica
nam facut nici o mbunatatire. Strassen a descoperit ca se pot face doar 7
nmultiri de matrici dupa cum urmeaza:
m1 = (A2 A4) (B3 B4)
(13.5)
(13.6)
(13.7)
m4 = (A1 + A2) B4
(13.8)
m5 = A1 (B2 B4)
(13.9)
m6 = A4 (B3 B1)
(13.10)
m7 = (A3 + A4) B1
(13.11)
(13.12)
C2 = m4 + m5
(13.13)
C3 = m7 + m6
(13.14)
C4 = m2 m3 + m5 m7
(13.15)
C1 C2
C3 C4
Algoritmul n cazul n = 2k va fi :
Strassen(x,y,n,z)
daca n > 1 atunci
descompune matricile x si y n x1,x2,x3,x4,y1,y2,y3,y4
n1 = [n/2]
Strassen( x2 x4,y3 y4,n1,m1)
Strassen( x1 + x4,y1 + y4,n1,m2)
Strassen( x1 x3,y1 + y2,n1,m3)
Strassen( x1 + x2,y4,n1,m4)
Strassen( x1,y2 y4,n1,m5)
Strassen( x4,y3 y1,n1,m6)
Strassen( x3 + x4,y1,n1,m7)
z1 = m1 + m2 m4 + m6
z2 = m4 + m5
132
(13.16)
z3 = m6 + m7
z2 = m2 m3 + m5 m7
recompune matricea z
sf
arsit daca
Return
Ecuatia functionala pentru calculul complexitatii va fi :
T (n) = 7T (n/2) + (n2 )
Ceea ce da :
7
T (n) = (nlog2 )
Metoda LUP
Sa consideram sistemul:
(13.17)
unde:
A=
...
b1
b2
B=
...
bn
x1
x2
X=
...
xn
(13.18)
(13.19)
(13.20)
l11 0 0
l21 l22 0
L=
l31 l32 l33
...
ln1 ln2 ln3
Care are toate elementele de deasupra
iar U este o matrice supradiagonala
0
0
u33
U =
...
0
0
0
... 0
... 0
... 0
(13.21)
... lnn
diagonalei principale egale cu 0,
... u1n
... u2n
... u3n
(13.22)
... unn
y1 = b01 /l11
i1
P
yi = 1/lii (b0i
(lij yj )
j=1
pentrui = 2, n
134
(13.23)
(13.24)
(13.25)
xn = yn /unn
n
P
yi = 1/uii (yi
(uij xj )
j=i+1
pentrui = n 1, 1
(13.26)
A=
= l
...
A0
an1 an2 ... ann
(13.27)
a11 rt
l
A0
=
1
0
l/a11 In1
a11 rt
0
A0 lrt /a11
(13.28)
unde 0 si 1 este, dupa nevoi, vector linie sau coloana cu toate elementele
egale cu 0 sau 1, de ordinul n-1 si matricea
A0 lrt /a11
este complementul Schurr al matricii A cu pivotul a11 .
Prin inductie, sa presupunem ca orice matrice de ordin < n se poate
descompune n produs de matrici LU atunci:
A0 lrt /a11 = L0 U 0
135
rezulta ca:
A=
1
0
l/a11 In1
a11 rt
1
0
a11 rt
0
A0 lrt /a11
l/a11 In1
0
L0 U 0
(13.29)
1
0
a11 rt
=
= LU
(13.30)
l/a11 L0
0
U0
(13.31)
(13.32)
1 1 2
1
1 1 2 1
1 1 1
1 0
1
1 2
A=
(13.33)
2 1 2 2 2 1 6 0
1 0 1
0
1 1 1 1
Pentru ca a22 = 0 schimbam liniile 2 si 3 ntre
1 1
2
1
1 1
2 -1 6 0
2 1
1 0
1 0
1 2
1 1 1 1
1 1
Deci vom avea descompunerea:
1 0 0
2 1 0
A=
1 0 1
1 1 5
1
0
0 0
0 0
0
1
136
1
1
0
0
ele.
2
1
6 0
-1 2
5
1
(13.34)
1
0
2
-11
(13.35)
2
6
1
0
x1 + x2 + 2x3 + x4 = 5
x1 + x2 + x3 + x4 = 2
2x1 + x2 2x3 + 2x4 = 3
x1 + 0x2 + x3 + 0x4 = 2
(13.36)
y1 + y2 5y3 + y4 = 2
care da:
y1
y2
y3
y4
=5
= 7
= 3
= 11
x1 + x2 + 2x3 + x4 = 5
(13.38)
(13.39)
care da:
x1
x2
x
x4
=1
=1
=1
=1
(13.40)
De fapt, n algoritm vom cauta, pentru pivot, cea mai mare valoare n
valoare absoluta.
Algoritmul de descompunere va fi:
LUPDESC(A, n, P, IE)
IE = 0
pentru i = 1, n
pentru j = 1, n
Pi,j = 0
sf
arsit pentru
137
sf
arsit pentru
sf
arsit pentru
y1 = b01
pentru i = 2, n
yi = b0i
pentru j = 1, i 1
yi = yi lij yj
sf
arsit pentru
sf
arsit pentru
Return
Rezolvarea prin nlocuire regresiva va fi:
U-REZ(A,n,y,x)
pentru i = 1, n
pentru j = i, n
uij = aij
sf
arsit pentru
sf
arsit pentru
x1 = y1 /u11
pentru i = n 1, 1, 1
xi = yi /uii
pentru j = 1, i 1
xi = xi yi uij /uii
sf
arsit pentru
sf
arsit pentru
Return
Rezolvarea sistemului va fi:
Start REZ-SIST-EC-LIN
citeste( n, ((aij , j = 1, n), i = 1, n) )
citeste( (bi , = 1, n) )
LUP-DESC(A, n, P, IE)
daca IE = 1 atunci
scrie( sistem necramerian )
Return
sf
arsit daca
L-REZ(A, n, P, B, y)
U-REZ(A, n, y, x)
scrie( (xi , i = 1, n) )
Stop
In [2] se poate vedea demonstratia faptului ca, cu conditii foarte des
ndeplinite, inversarea matricilor si produsul matricilor au aceeasi complexi139
tate.
Pentru complexitatea algoritmului LUP sa observam ca complexitatea
este:
T (n) = (n3 ) + (n2 ) = (n3 )
Pentru inversarea matricilor, algoritmul se poate folosi n felul urmator:
AX = Ei
(13.41)
i = 1, n
unde ei este vectorul
ei1
ei2
Ei =
...
ein
(13.42)
si unde:
eij = 0j =
6 i
eij = 1j = i
(13.43)
LY i = ei
UX = Y i
(13.44)
i = 1, n
Complexitatea inversarii va fi:
T (n) = (n3 + n (n2 = (n3 )
Rezumat
In acest capitol ati vazut cum se poate face nmultirea matricilor n complexitate mai mica decat n3 . Apoi ati nvatat o metoda de rezolvare a sistemelor de ecuatii liniare, crameriene, cu complexitatea n3 , metoda care se
poate aplica si la inversarea matricilor.
Exercitii
1. Scrieti un algoritm care, folosind subalgoritmii de mai sus , inverseaza
o matrice.
140
2. Sa se inverseze matricea:
1
1
A=
2
1
1
1
1
0
2
1
2
1
1 1
1
1
A=
2
2
1
2
2
1
2
1
1
1
B=
3
2
1
1
2
0
1
1
1
2
(13.45)
(13.46)
141
(13.47)
142
Capitolul 14
Teste de autoevaluare
1. Algoritmi de sortare prin metoda Divide et impera.
2. Folosind un subalgoritm pentru nmultirea matricilor sa se scrie algoritmul care ridica la puterea k o matrice patratica de ordinul n.
3. Dandu-se parcurgerile n preordine A, B, C, D, E, F, G si n inordine
C, B, E, D, A, G, F sa se deseneze arborele binar corespunzator.
Solutii
1. (Din oficiu: 1p)
Metoda presupune parcurgerea a trei pasi:
a) mpartirea datelor n mai multe parti;
b) rezolvarea (de obicei recursiva) a problemei pe fiecare bucata
de date;
c) combinarea rezultatelor partiale n rezultatul final.
(1p)
Sortarea prin interclasare presupune:
a) mpartirea sirului initial n doua parti;
b) sortarea fiecarei jumatati;
c) interclasarea bucatilor sortate.
MergeSort(a, p, q)
daca p q atunci
Return
sfarsit daca
143
r = p+q
2
MERGESORT(a, p, r)
MERGESORT(a, r + 1, q)
Interclasare(a, p, r, q)
Return
(1p)
Interclasare(a, p, r, q)
i=p
j =r+1
k=0
repeta
k =k+1
daca a(i) < a(j) atunci
b(k) = a(i)
i=i+1
altfel
b(k) = a(j)
j =j+1
sfarsit daca
pana cand i > r sau j > q
pentru il = i, r
k =k+1
b(k) = a(il)
sfarsit pentru
pentru jl = j, q
k =k+1
b(k) = a(jl)
sfarsit pentru
k=0
pentru i = p, q
k =k+1
a(i) = b(k)
sfarsit pentru
Return
(2p)
Apelarea se face:
Start Sortare1
citeste( n, (a(i), i = 1, n) )
MERGESORT(a, 1, n)
144
scrie( (a(i), i = 1, n) )
Stop
Complexitatea rezulta din:
T (n) = 2T
n
2
+an
Se apeleaza ca si celalalt.
Complexitatea este (n2 ), dar n cazul (frecvent) al mpartirii proportionale a sirului avem (n log n). Se pot face mbunatatiri prin alegerea
aleatoare al lui el (elementul pivot). (1p)
2. Vom scrie doi subalgoritmi: unul pentru nmultirea a doua matrice
patratice si altul pentru ridicarea la putere a unei matrici.
Din oficiu (1p).
InmultireMatrici(a, b, c, n)
pentru i = 1, n
pentru j = 1, n
c(i, j) = 0
pentru k = 1, n
c(i, j) = c(i, j) + a(i, k) b(k, j)
sfarsit pentru
sfarsit pentru
sfarsit pentru
Return
(3p)
PutereMatrice(a, n, k, c)
pentru i = 1, n
pentru j = 1, n
b(i, j) = a(i, j)
sfarsit pentru
sfarsit pentru
pentru p = 2, k
InmultireMatrici(a, n, b, c)
pentru i = 1, n
pentru j = 1, n
b(i, j) = c(i, j)
sfarsit pentru
sfarsit pentru
sfarsit pentru
Return
(4p)
Subalgoritmul PutereMatrice se apeleaza n felul urmator:
Start RidicareLaPutere
citeste( k, n, ((a(i, j), j = 1, n), i = 1, n) )
PutereMatrice(a, n, k, c)
146
147
Figura 14.2:
Arbore care produce la parcurgere n preordine
A, B, C, D, E, F, G, dar nu da rezultat corect pentru inordine:
C, B, E, D, A, G, F , pe cand la noi se obtine: D, C, E, B, A, F, G.
148
Anexa A
Metode utile n calculul
complexit
atii
Sume des nt
alnite
1. Progresia aritmetica:
ak = ak1 + r, k = 2, a1 si r dati
n
X
(a1 + an )n
S =
ak =
2
k=1
(A.1)
(A.2)
2. Sume particulare:
n
X
k =
k=1
n
X
n(n + 1)(2n + 1)
= (n3 )
6
2
n(n + 1)
=
= (n4 )
2
(A.3)
k2 =
(A.4)
k3
(A.5)
k=1
n
X
n(n + 1)
= (n2 )
2
k=1
si n general:
n
X
k p = (np+1 )
k=1
3. Progresii geometrice:
149
(A.6)
ak = ak1 r, k = 1, a1 , r 6= 1 dati
n
n
n1
X
X
X
rn 1
S =
ak =
a1 r k = a1 r
r k = a1 r
r1
k=1
k=1
k=0
(A.7)
n
X
ak
k=1
X
k=1
ak = a1
1
1r
(A.8)
Delimitarea sumelor
De multe ori, sumele nu se regasesc n formele simple date mai sus. Exista diferite tehnici de determinare a unei majorari si minorari a acestora.
Prezentam cateva din ele mai jos.
Inductia matematic
a
Una din modalitatile usor de abordat pentru delimitarea unei sume este
bazata pe inductia matematica. De exemplu, sa demonstram ca seria aritn
P
metica
k are valoarea 12 n(n + 1). Putem verifica usor pentru n = 1, n = 2
k=1
k=
k=1
n
X
1
1
k + (n + 1) = n(n + 1) + (n + 1) = (n + 1)(n + 2).
2
2
k=1
(A.9)
notatie:
n
P
k=0
0
P
k=0
2k = 1 c1,
150
n
P
2k = O(2n ), ceea
k=0
ce am dorit sa aratam.
Atentionam cititorul asupra unui viciu de procedura n cazul utilizarii
acestui instrument matematic, care se regaseste n urmatoarea demonstratie
n
1
P
P
corecta: aratam ca
k = O(n). Desigur,
k = O(1). Acceptand
k=1
k=1
k =
n
X
k + (n + 1)
k=1
= O(n) + (n + 1) gresit !
= O(n + 1).
Greseala n argumentatie este aceea ca O ascunde o constanta care
creste n functie de n si deci nu este constanta. Ar trebui sa aratam ca exista
o constanta care se ascunde n notatia O() si care este deci valabila pentru
toti n.
n
P
k=m
k=m
(A.11)
k=m
m1
= ln(n + 1).
(A.13)
k
x
1
k=1
151
(a)
(b)
M
aarginire
M
rginire
insufeperioar
aa
rioar
= ln n,
k
x
1
k=2
care da limita
n
X
1
k=1
(A.14)
ln n + 1.
(A.15)
Metoda iteratiei
Tehnica urmatoare cere ceva mai multa tehnica matematica, dar rezultatele sunt destul de expresive. Traiectoria ar fi: se executa cativa pasi,
se intuieste forma generala, iar apoi se demonstreaza prin inductie matematica ca aceasta forma este corecta. Sa consideram, de exemplu, recurenta
problemei turnurilor din Hanoi. Pentru un n > 1 obtinem succesiv
2
n1
t(1) +
n2
X
2i .
i=0
(A.16)
n
Rezulta t(n) = 2 1. Prin inductie matematica se demonstreaza acum
cu usurinta ca aceasta formula este corecta.
152
(A.17)
(A.18)
(A.19)
k
X
ci rin
(A.20)
i=1
este o solutie a recurentei (A.17), unde constantele c1 , c2 , . . . , ck sunt determinate de conditiile initiale.
Sa exemplificam prin recurenta care defineste sirul lui Fibonacci:
tn = tn1 + tn2 , n 2
(A.21)
(A.22)
(A.23)
(A.24)
de unde determinam
c1,2 = 1/ 5.
tn = 1/ 5(n ()n )
Ca atare, implementarea recursiva pentru determinarea celui de-al nlea termen al sirului lui Fibonacci duce la o complexitate exponentiala2 .
Daca radacinile ecuatiei caracteristice nu sunt distincte, se procedeaza
dupa cum urmeaza: daca r este o radacina de multiplicitate m a ecuatiei
caracteristice, atunci tn = rn , tn = nrn , tn = n2 rn , . . . , tn = nm1 rn sunt
solutii pentru (A.17). Solutia generala pentru o astfel de recurenta este o
combinatie liniara a acestor termeni si a termenilor proveniti de la celelalte
radacini ale ecuatiei caracteristice. Ca mai sus, trebuie determinate exact k
constante din conditiile initiale.
Vom da din nou un exemplu. Fie recurenta
tn = 5tn1 8tn2 + 4tn3 , n 3
(A.25)
iar t0 = 0, t1 = 1, t2 = 2. Ecuatia caracteristica are radacinile 1 (de multiplicitate 1) si 2 (de multiplicitate 2). Solutia generala este:
tn = c1 1n + c2 2n + c3 n2n .
Din conditiile initiale, obtinem c1 = 2, c2 = 2, c3 = 1/2.
(A.26)
154
155
156
Anexa B
Rezolv
ari
Rezolv
ari pentru capitolul 3
1. Problema 1.
(a) Exemplul 1. acest program contine doar instructiuni elementare
care cer un timp constant de executie. Ca atare, timpul total este
marginit superior de o constanta, fapt figurat prin notatia (1).
(b) Exemplul 2: justificarea este aceeasi cu cea de la punctul precedent, deci complexitatea este (1).
(c) Exemplul 3: citirea datelor de intrare are un cost de t1 (n) = c1
(n+1), unde c1 este o constanta reprezentand timpul necesar citirii
unei valori. Atribuirea s = 0 se executa ntr-un timp constant
t2 = c2 , independent de dimensiunea datelor de intrare. Ciclarea
nseamna t3 = c3 n, unde c3 este timpul necesar pentru calculul
sumei si efectuarea atribuirii; de asemenea, c3 nu depinde de n.
Afisarea se face n timpul t4 = c4 (constant).
Ca atare,
T (n) = t1 (n) + t2 (n) + t3 (n) + t4 (n) = c1 (n + 1) + c2 + c3 n + c4
Conform proprietatii 5, pagina 29, avem ca:
T (n) =
=
=
=
(c1 (n + 1) + c2 + c3 n + c4 )
(max(c1 (n + 1) + c3 n), c2 + c4 )
(c1 (n + 1) + c2 n) = ((c1 + c3 )n + c1 )
((c1 + c2 )n) = (n),
acest element este gasit, atunci este oprita cautarea, altfel se merge la
urmatorul element. La sfarsit se raporteaza rezultatul.
Pentru a semnala faptul ca x a fost gasit, vom folosi o variabila booleana
gasit care va primi valoarea adevarat daca x a fost gasit n sir, f als n
caz contrar.
Start CautareLiniara
citeste( n, (a(i), i = 1, n), x )
gasit = f als{deocamdata nu am gasit pe x n a}
i = 1{i este indicele elementului curent din sir}
repeta
daca a(i) = x atunci
gasit = adevarat
pozitie = i{pozitie este pozitia elementului x n sir}
altfel
i=i+1
sfarsit daca
pana cand i > n sau (gasit = adevarat)
daca gasit = adevarat atunci
scrie( Am gasit , x, pe pozitia , pozitie )
altfel
scrie( x, nu a fost gasit )
sfarsit daca
Stop
Complexitatea este (n), cazul cel mai defavorabil fiind cand x nu se
afla n sir.
Observatie: Deoarece executia instructiunilor din interiorul ciclului trebuie sa se faca cel putin o data, este corecta utilizarea unui ciclu repet
a.
4. Problema 4: vom folosi un algoritm asemanator cu cel de la problema
precedenta, folosind informatia ca sirul este ordonat crescator. Vom da
doua variante, cu analiza de complexitate.
(a) Prima varianta: ncepem cautarea, dar vom impune o conditie
suplimentara: trebuie ca permanent x a(i). Daca avem x >
a(i), atunci este clar ca x nu mai poate fi gasit la dreapta indicelui
i.
Start CautareLiniaraCrescator
citeste( n, (a(i), i = 1, n), x )
gasit = f als{deocamdata nu am gasit pe x n a}
i = 1{i este indicele elementului curent din sir}
159
repeta
daca a(i) = x atunci
gasit = adevarat
pozitie = i{pozitie este pozitia elementului x n sir}
altfel
i=i+1
sfarsit daca
pana cand i > n sau gasit = adevarat sau x > a(i)
daca gasit = adevarat atunci
scrie( Am gasit , x, pe pozitia , pozitie )
altfel
scrie( x, nu a fost gasit )
sfarsit daca
Stop
Complexitatea este aceeasi ca la algoritmul precedent: (n).
Observatie Este esential ca testarea i > n sa se faca nainte de
testarea a(i) x; daca spre exemplu locul lor ar fi fost inversat,
atunci era posibil ca sa avem i = n + 1, iar conditia a(i) x sa
fie testata, desi a(n + 1) nu exista.
(b) A doua varianta speculeaza mai inteligent faptul ca sirul este gata
sortat. Putem mparti sirul n doua subsiruri de dimensiuni cat
mai apropiate. Se testeaza daca elementul de la mijloc este egal
cu x. Daca da, atunci cautarea se opreste cu succes. Daca nu, si
x < elementul din mijloc, atunci cautarea va continua sa se faca n
prima jumatate a sirului (e clar ca n a doua jumatate nu poate fi
gasit); daca x este mai mare decat elementul de la jumatate, atunci
cautarea va continua n a doua jumatate. Cautarea continua pana
cand fie se gaseste x n sir, fie nu mai avem nici un element de
cercetat, caz care nseamna cautare fara succes.
Acest algoritm se numeste algoritmul c
aut
arii binare (prin njumatatire).
Start CautareBinara
citeste( n, (a(i), i = 1, n), x )
gasit = f als{variabila gasit are aceeasi semnificatie ca n
algoritmul anterior}
stanga = 1
dreapta = n
{stanga si dreapta reprezinta capetele ntre care se face cautarea
lui x}
{Initial, cautarea se face n tot vectorul}
160
(b) Vom rezolva problema printro singura parcurgere a sirului; complexitatea teoretica ramane aceeasi.
Start ContorMinim2
citeste( n, (a(i), i = 1, n) )
minim = a(1)
contor = 1
pentru i = 2, n
daca minim > a(i) atunci
minim = a(i){am gasit un minim mai bun}
contor = 1{resetam contorul la 1}
altfel
daca minim = a(i) atunci
contor = contor + 1{am mai gasit o valoare egala cu
minimul curent}
sfarsit daca
sfarsit daca
sfarsit pentru
scrie( minimul este , minim )
scrie( apare de , contor, ori )
Stop
7. Problema 6: vom construi doi vectori: unul retine numerele distincte
din a, iar celalalt contorizeaza numarul de aparitii pentru fiecare numar.
Vom scrie un subalgoritm de tip functie de cautare liniara a unui element x ntr-un vector v care contine un anumit numar de elemente.
Aceasta functie returneaza pozitia pe care se gaseste elementul x n v
sau 0 n cazul n care x nu este cuprins n v:
CautaLiniar(x, v, l)
pozitie = 0
gasit = f als
i=1
repeta
daca x = v(i) atunci
gasit = adevarat
pozitie = i
altfel
i=i+1
sfarsit daca
pana cand i > l sau gasit = adevarat
CautaLiniar = pozitie
Return
163
Start CelMaiDes
citeste( n, (a(i), i = 1, n) )
k = 0{k este numarul de elemente distincte din a}
pentru i = 1, n
pozitieInDif erite =CautaLiniar (a(i), dif erite, k)
daca pozitieInDif erite = 0 atunci
k = k + 1{a(i) nu a mai fost gasit nainte}
{punem a(i) n vectorul dif erite}
dif erite(k) = a(i)
contor(k) = 1
altfel
{a(i) a mai aparut o data nainte n a}
contor(pozitieInDif erite) = contor(pozitieInDif erite)+1{lam mai gasit o data}
sfarsit daca
sfarsit pentru
{acum cautam n vectorul contor sa vedem care numar a aparut
cel mai des}
maximAparitii = contor(1)
pozM ax = 1
pentru i = 2, k
daca contor(i) > maximAparitii atunci
maximAparitii = contor(i)
pozM ax = i
sfarsit daca
sfarsit pentru
scrie( Numarul cel mai frecvent: , dif erite(pozM ax) )
scrie( Apare de , maximAparitii, ori )
Stop
Complexitate: cazul cel mai defavorabil este atunci cand avem doar elemente distincte n sirul a. In acest caz, un apel CautaLiniar(a(i), dif erite, k)
are costul de forma a k + b, a si b constante. Ciclul din programul
principal duce la un timp
T (n) =
n
X
(a k + b) = (n2 )
k=1
dif erit =
(10, 20, 30, 40)
contor =
(1, 1, 1, 1)
k=
4
cel mai frecvent: 10
frecventa:
1
8. Problema 8: vom profita de faptul ca vectorul dat este sortat crescator.
Vom cauta o secventa de numere constante, de lungime maxima.
Start CelMaiDes
inceputSecventa = 1
sf arsitSecventa = 1
lungimeSecventaM axima = 0
suntInSir = adevarat
cat timp suntInSir = adevarat
cat timp sf arsitSecventa n si a(inceputSecventa) = a(sf arsitSecventa)
sf arsitSecventa = sf arsitSecventa + 1
sfarsit cat timp
daca sf arsitSecventainceputSecventa > lungimeSecventaM axima
atunci
apareM axim = a(inceputSecventa)
lungimeSecventaM axima = sf arsitSecventainceputSecventa
sfarsit daca
inceputSecventa = sf arsitSecventa
daca inceputSecventa > n atunci
suntInSir = f als{am parcurs tot sirul}
sfarsit daca
sfarsit cat timp
scrie( Numarul cel mai frecvent: , apareM axim )
scrie( Apare de , lungimeSecventaM axima, ori )
Stop
Complexitate: se observa ca valoarea sf arsitSecventa ia pe rand valorile 1, 2, . . . , n + 1; de fiecare data sunt efectuate operatii elementare,
deci avem T (n) = (n + 1) = (n).
Observatie: Pentru a rezolva problema de la punctul anterior, am putea
face o sortare a sirului, dupa care sa aplicam algoritmul de mai sus.
Complexitatea totala ar fi: sortarea implica un timp (n log n), algoritmul anterior (n), deci per total (n log n), sensibil mai bine decat
(n2 ) care fusese obtinut anterior.
9. Problema 9:
165
Start Matrice
citeste( n )
citeste( ((a(i, j), j = 1, n), i = 1, n) )
{subpunctul a}
p = 1{1 este element neutru pentru nmultire}
pentru i = 1, n
pentru j = 1, n
daca a(i, j) 6= 0 atunci
p = p a(i, j)
sfarsit daca
sfarsit pentru
sfarsit pentru
scrie( Produsul elementelor nenule din matrice este: , p )
{subpunctul b}
p=1
pentru i = 1, n
daca a(i, i) 6= 0 atunci
p = p a(i, j)
sfarsit daca
sfarsit pentru
scrie( Produsul elementelor nenule de pe diagonala principala este:
, p )
{subpunctul c}
p=1
pentru i = 2, n
pentru j = 1, i 1
daca a(i, j) 6= 0 atunci
p = p a(i, j)
sfarsit daca
sfarsit pentru
sfarsit pentru
scrie( Produsul elementelor de sub diagonala principala este:, p )
Stop
Complexitate:
(a) (n2 ) (fiecare element al matricii este luat n considerare o singura data, efectuandu-se cu el operatii elementare);
(b) (n);
(c) T (n) =
n
P
i=2
(i 1)c = c
n
P
i=2
(i 1) = c n(n1)
, deci T (n) = (n2 ).
2
166
Rezolv
ari pentru capitolul 4
1. Problema 1: Vom folosi pentru memorarea elementelor din coada un
vector v de n elemente. Vom presupune ca indicii vectorului sunt ntre
0 si n 1. Deoarece coada este circulara, dupa locatia de indice n 1
urmeaza locatia de indice 0. Initial, coada este vida, fapt semnalat prin
inceput = sf arsit = 0.
x Coada
sf arsit = (sf arsit + 1)mod n
daca sf arsit = inceput atunci
Coada este plina
altfel
v(sf arsit) = x
sfarsit daca
Return
x Coada
daca inceput = sf arsit atunci
Coada este vida
altfel
inceput = (inceput + 1)mod n
x = v(inceput)
sfarsit daca
Return
Complexitatea fiecarui subalgoritm este (1). Initial avem inceput =
sf arsit = 0.
Observatie: Se folosesc efectiv doar n 1 locatii ale vectorului; dar n
acest mod putem distinge situatia de coada plina de situatia de coada
vida.
2. Problema 2:
(a) Parcurgerea n inordine, iterativ:
InordineIterativ(rad)
i = rad
S = { S este o stiva}
repeta
cat timp LS(i) 6=
iS
i = LS(i)
sfarsit cat timp
167
scrie( IN F O(i) )
cat timp LD(i) =
daca S = atunci
Return{ se iese din subalgoritm, deci si din ciclul infinit}
sfarsit dac
a
iS
scrie( IN F O(i) )
sfarsit cat timp
i = LD(i)
pana cand fals{ciclu infinit, din care se iese datorita lui Return}
(b) Parcurgerea n postordine, iterativ:
PostordineIterativ( rad )
i = rad
S = { S este o stiva}
repeta
cat timp LS(i) 6=0
iS
i = LS(i)
sfarsit cat timp
cat timp LD(i) = 0
repeta
scrie( IN F O(i) )
daca S = atunci
Return{se iese din subalgoritm, deci si din ciclul infinit}
sfarsit dac
a
j=i
iS
pana cand j = LS(i)
sfarsit cat timp
iS
i = LD(i)
pana cand fals{ciclu infinit, din care se iese datorita lui Return}
Apelul subalgoritmilor de mai sus se face avand drept parametru de
apel rad, reprezentand adresa radacinii arborelui.
Complexitatea fiecareia din proceduri este de (n), unde n este numarul
de varfuri ale arborelui.
3. Problema 3:
168
Vom extrage succesiv varful stivei S si l vom depune ntr-o alta stiva
T . Extragerile nceteaza fie cand S devine vida, fie cand x este gasit.
Elementele din T vor fi depuse napoi n S.
Extrage( S, x )
T =
gasitx = f als
cat timp S 6= si gasitx = f als
a S{ scoatem varful stivei}
daca a 6= x atunci
a T { l depunem n T }
altfel
gasitx = adevarat
sfarsit daca
sfarsit cat timp
cat timp T 6=
aT
aS
sfarsit cat timp
Return
Complexitate: cazul cel mai defavorabil este acela n care x nu se
gaseste n S. In acest caz, tot continutul lui S este trecut n T si
apoi repus n S. Complexitatea este deci (|S|), unde |S| reprezinta
numarul de elemente aflate initial n S.
Observatie: Primul ciclu c
at timp poate fi scris mai adecvat ca un ciclu
repeta, avand n vedere faptul ca executia ciclui se face cel putin o data.
4. Problema 4:
Rezolvarea este asemanatoare cu cea de la punctul precedent.
Insereaza( S, x, y )
T =
gasitx=fals
cat timp S 6= si gasitx = f als
a S{ scoatem varful stivei}
daca a 6= x atunci
a T { l depunem n T }
altfel
gasitx = adevarat
aS
sfarsit daca
169
Rezolv
ari pentru capitolul 5
1. Problema 1. Definim maximul unui sir x = (x(1), x(2), . . . , x(n)) sub
forma recursiva astfel:
x(1),
daca n=1
maxim(x(1 . . . n)) =
max{x(n), x(1 . . . n 1)}, daca n > 1
unde max{a, b} este maximul dintre a si b. Algoritmul este dat mai
jos:
Start MaximRec
citeste( n, (x(i), i = 1, n) )
M = M axim(x, n)
scrie( M )
Stop
unde subalgoritmul recursiv M axim este:
M axim(x, n)
daca n = 1 atunci
M aximRec = x(1)
altfel
M aximRec = max{x(n), M axim(x, n 1)}
sfarsit daca
Return
Complexitatea este data de ecuatia T (n) = T (n1)+c care are solutia
T (n) = (n).
2. Problema 2. Definim suma elementelor unu sir x = (x(1), . . . , x(n))
sub forma recursiva astfel:
0,
daca n=0
suma(x(1 . . . n)) =
x(n) + suma(x(1 . . . n 1)), daca n > 0
Start SumaRec
citeste( n, (x(i), i = 1, n) )
S = Suma(x, n)
scrie( S )
Stop
unde subalgoritmul recursiv Suma este:
Suma(x, n)
daca n = 0 atunci
172
Suma = 0
altfel
Suma = x(n) + Suma(x, n 1)
sfarsit daca
Return
Complexitatea este data de ecuatia T (n) = T (n1)+c care are solutia
T (n) = (n).
3. Problema 3. Vom da o formula recursiva de calcul a puterii naturale a
unui numar.
1,
daca n = 0
a,
daca n = 1
an =
n/2 2
a
,
daca n > 1, n par
bn/2c 2
, daca n > 1, n impar
a a
Subalgoritmul recursiv este:
Putere(a, n)
daca n = 0 atunci
P utere = 1
Return
sfarsit daca
daca n = 1 atunci
P utere = a
Return
sfarsit daca
temp = P utere(a, bn/2c)
daca n mod 2 = 0 atunci
{n este par}
P utere = temp
altfel
P utere = a temp
sfarsit daca
Return
Complexitatea este data de ecuatia T (n) = T (n/2)+c, de unde T (n) =
(log n) (rezultat al teoremei 1, pagina 76).
4. Problema 4. Pentru a15 , conform algoritmului precedent, avem:
a15 =
(a)2 a
173
2
2
fn1
fn
obtinem matricea
fn
fn+1
de unde deducem
fn1
fn
=A
n1
f0
f1
Postordine(Rad)
daca Rad 6= atunci
Postordine(LS(Rad))
Postordine(LD(Rad))
scrie( Inf o(Rad) ){Sau orice alt subalgoritm de prelucrarea
a informatiei din Rad}
sfarsit daca
Return
Apelul se face ca n cazul anterior.
(c) Parcurgerea recursiva n inordine se efectueaza astfel: se parcurge recursiv subarborele stang, apoi se prelucreaza informatia
din radacina, apoi se parcurge recursiv subarborele drept.
Inordine(Rad)
daca Rad 6= atunci
Inordine(LS(Rad))
scrie( Inf o(Rad) ){Sau orice alt subalgoritm de prelucrarea
a informatiei din Rad}
Inordine(LD(Rad))
sfarsit daca
Return
Apelul se face ca n cazul anterior.
Rezolv
ari pentru capitolul 6
1. Problema 1.
Vom da doua variante de rezolvare: iterativa si recursiva. Ambele
variante vor folosi doi vectori de sarituri: sarituraI si sarituraJ cu
cate 8 elemente, reprezentand cele 8 deplasari de la pozitia curenta: de
exemplu, daca pozitia curenta este pe linia i si coloana j, atunci prima
saritura va duce calul la coordonatele (i+sarituraI(1), j+sarituraJ(1)
(cu conditia sa nu se iasa din perimetrul tablei).
(a) Varianta iterativa: pentru refacerea pasului napoi vom folosi
o matrice patratica deLa de ordinul n, care va retine pentru fiecare
celula de coordonate (i, j) indicele sariturii care a determinat atingerea
pozitiei respective (vom putea sti astfel de unde sa sarit n pozitia
curenta). Solutia se da sub forma unei matrice patratice de ordinul n
continand n fiecare celula de coordonate (i, j) indicele sariturii ce se
face n continuare (cu exceptia ultimei celule n care se sare).
175
Start CalSah
citeste( 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
sfarsit pentru
sfarsit pentru
nrSaritura = 1
i=j=1
cat timp nrSaritura > 0
daca nrSaritura = n2 atunci
ScrieSolutie(tabla, n)
indiceSarituraInapoi = deLa(i, j)
tabla(i, j) = 0
i = i sarituraI(indiceSarituraInapoi)
j = j sarituraJ(indiceSarituraInapoi)
nrSaritura = nrSaritura 1
sfarsit daca
daca tabla(i, j) < 8 atunci
tabla(i, j) = tabla(i, j) + 1
iInainte = i + sarituraI(tabla(i, j))
jInainte = j + sarituraJ(tabla(i, j))
daca PoateSari(iInainte, jInainte, tabla, n)=adevarat atunci
deLa(iInainte, jInainte) = tabla(i, j)
i = iInainte
j = jInainte
nrSaritura = nrSaritura + 1
sfarsit daca
altfel
daca nrSaritura > 1 atunci
iInapoi = i sarituraI(deLa(i, j))
jInapoi = j sarituraJ(deLa(i, j))
176
tabla(i, j) = 0
i = iInapoi
j = jInapoi
sfarsit daca
nrSaritura = nrSaritura 1
sfarsit daca
sfarsit cat timp
Stop
ScrieSolutie(tabla, n)
pentru i = 1, n
pentru j = 1, n
scrie( tabla(i, j) )
sfarsit pentru
sfarsit pentru
Return
PoateSari(linie, coloana, tabla, n)
daca linie > 0 si linie < n + 1 si coloana > 0 si coloana < n + 1 si
tabla(linie, coloana) = 0 atunci
rezultat = adevarat
altfel
rezultat = f als
sfarsit daca
P oateSari = rezultat
Return
(b) Varianta recursiva
Cal(i, j, n, nrSaritura, tabla)
daca nrSaritura = n n atunci
ScrieSolutie(tabla, n)
altfel
pentru indiceSaritura = 1, 8
iInainte = i + sarituraI[indiceSaritura]
jInainte = j + sarituraJ[indiceSaritura]
daca PoateSari(iU rmator, jU rmator, tabla, n)=adev atunci
tabla[iInainte, jInainte] = nrSaritura + 1
Cal(iInainte, jInainte, n, nrSaritura + 1, tabla)
tabla[iInainte, jInainte] = 0
sfarsit daca
sfarsit pentru
sfarsit daca
Return
177
citeste( m, n, k )
vf (0) = vb(0) = 0
pentru p = k, m
pentru q = 0, n
GenerareDelegatii(p, 1, m, q, 1, n, vf, vb)
sfarsit pentru
sfarsit pentru
Stop
Pentru o varianta iterativa, a se citi capitolul 7, sectiunea 7 (generare
de combinari).
4. Problema 4.
Vom da mai jos o varianta recursiva. Consideram rest ca fiind restul
care mai este de platit, solutie un vector cu n elemente, 0 solutie(i)
b(i), bc tipul bancnotei curente.
Plata(rest, n, solutie, bc)
daca rest = 0 atunci
scrie( (solutie(i), i = 1, bc 1) )
altfel
daca rest > 0 si bc n atunci
pentru k = 0, b(bc)
solutie(bc) = k
Plata(rest k v(bc), solutie, bc + 1)
sfarsit pentru
sfarsit daca
sfarsit daca
Return
Start PlataSuma
citeste( n, S )
citeste( (b(i), i = 1, n) )
citeste( (v(i), i = 1, n) )
Plata(S, n, solutie, 1)
Stop
Observatie Orice problema cu datele ca mai sus poate fi transformata
ntruna n care vectorul b contine doar valoarea 1, prin crearea unui
vector v 0 care va contine valoarea v(i) de b(i) ori.
Pentru o varianta iterativa n cazul b(i) = 1, i = 1, n a se vedea
capitolul 7, sectiunea 7
181
Rezolv
ari pentru capitolul 7
1. Problema 1.
(a) Produs cartezian:
CartezianRecursiv(m, n, k, x)
daca k = n + 1 atunci
Afisare(x, m){sau orice subalgoritm care prelucreaza informatia
obtinuta}
altfel
pentru p = 1, n(k)
x(k) = p
CartezianRecursiv(m, n, k + 1, x)
sfarsit pentru
sfarsit daca
Return
Subalgoritmul Afisare este:
Afisare(a, l)
pentru i = 1, l
scrie( a(i) )
sfarsit pentru
Return
Programul principal este:
Start Cartezian
citeste( m, (n(i), i = 1, m) )
CartezianRecrsiv(m, n, 1, x)
Stop
Subalgoritmul recursiv genereaza solutiile n ordine lexicografica.
(b) Submultimi: vom folosi un subalgoritm recursiv cu forma: SubmultimiRecursiv(n, k, cate, x) unde n este data de intrare reprezentand
numarul elementelor multimii initiale, k este indicele curent pentru care se ncearca generarea, cate reprezinta numarul de elemente ale submultimii care se genereaza (1 k cate, pentru
cate > 0), iar x este vectorul solutie. Vom considera ca x are un
elemente de indice 0 care va avea permanent valoarea 0.
SumbultimiRecursiv(n, k, cate, x)
daca k > cate atunci
182
Rezolv
ari pentru capitolul 8
1. Problema 1. Vom proceda n felul urmator: daca trebuie sa umplem
un vector al carui capat stang este st si cel drept este dr, atunci la
mijloc se pune perechea (i, j), dupa care se completeaza jumatatea
stanga a vectorului, urmata de cea dreapta. Varianta de mai jos este
iterativa, folosind o stiva pentru simularea recursivitatii. Solutiile vor
fi memorate ntro matrice cu 2n 1 linii si 2 coloane.
Start Hanoi
citeste( n )
i=1
184
j=2
st = 1
dr = 2n 1
S = {S este stiva}
(i, j, st, dr) S
cat timp S 6=
(i, j, st, dr) S
, 1) = i
sol( st+dr
2
st+dr
sol( 2 , 2) = j
daca st st+dr
1 atunci
2
1) S
(i, 6 i j, st, st+dr
2
sfarsit daca
+ 1, dr atunci
daca st+dr
2
+ 1, dr) S
(6 i j, j, st+dr
2
sfarsit daca
sfarsit cat timp
scrie( (sol(i, 1), sol(1, 2), i = 1, 2n 1) )
Stop
Adancimea stivei S este (log2 (2n 1)) = (n), iar complexitatea este
evident (2n ).
2. Problema 2. A se vedea rezolvarea de la varianta 4b, pagina 160. Complexitatea obtinuta este T (n) = (log n).
3. Problema 3. A se vedea rezolvarea de la varianta 4a, pagina 159, pentru
care se obtinea o complexitate liniara.
4. Problema 4. La fiecare pas din ciclul repet
a al algoritmului de partitionare
i creste cu o unitate, iar j scade cu o unitate. Partitionarea se opreste
atunci cand j este la jumatatea sirului, deci mpartirea este echilibrata.
Ca atare, complexitatea este data de T (n) = T (n/2) + c n, de unde
T (n) = (n logn).
5. Problema 5. Vom crea o functie care determina daca un numar este
sau nu situat ntre alte doua numere:
Intre(x, y, z){returneaza adevarat daca x este ntre y si z, fals altfel}
daca (y x si x z) sau (z x si x y) atunci
Intre = adevarat
altfel
Return fals
sfarsit daca
185
Rezolv
ari pentru capitolul 9
1. Problema 1. Rezolvarea problemei se face n doi pasi:
(a) Se determina mediana sirului initial x = (x(1), . . . , x(n)); fie ea
186
medx
(b) Se calculeaza sirul auxiliar y = (y(i), i = 1, n) unde y(i) = |x(i)
medx |
(c) Se determina a k-a statistica de ordine a sirului y; fie ea medy
(d) Se determina k elemente mai mici sau egale cu medy ; indicii acestor elemente sunt indicii elementelor cautate din sirul x.
Complexitatea fiecarui pas de mai sus este (n), deci complexitatea
algoritumului schitat este (n). Subalgoritmul n pseudocod este:
Start Apropiate
citeste( n, k, (x(i), i = 1, n) )
Selectie(x, 1, n, k, medx )
ObtineY(x, n, y, medx )
Selectie(x, 1, n, k, marginey )
Vecini(x, y, n, marginey , z)
Stop
Subalgoritmul Selectie este cel care determina a k-a statistica de ordine
a unui sir (cu complexitate liniara pentru cazul cel mai defavorabil);
subalgoritmul ObtineY este:
ObtineY(x, n, y, medx )
pentru i = 1, n
y(i) = |x(i) medx |
sfarsit pentru
Return
Subalgoritmul Vecini va determina cei mai apropiati k vecini ai sirului
x fata de mediana sa:
Vecini(x, y, n, marginey , z)
lungZ = 0
pentru i = 1, n
daca y(i) marginey atunci
lungZ = lungZ + 1
z(lungZ) = x(i)
sfarsit daca
sfarsit pentru
Return
187
iB = k B
sfarsit daca
sfarsit cat timp
scrie( max(A(iA ), B(iB )) )
Stop
unde max returneaza maximul a doua numere. Complexitatea este:
T (n) = T (n/2) + (1) = (log n)
3. Problema 3. Se poate proceda n mai multe moduri:
(a) Sortam numerele si alegem cele mai mari i numere
(b) Construim un (max)heap si extragem din el de i ori maximul
(c) Prin utilizarea unui algoritm de statistica de ordine
Lasam n seama cititorului scrierea algoritmulor pentru cazurile 3a si
3b. Pentru situatia 3c determinam a n i + 1a statistica de ordine
(fie ea x) si apoi printro parcurgere a sirului numerele mai mari sau
egale cu x. Complexitatea este de (n + i log i). Sugeram cititorului
compararea acestei complexitati cu cea obtinuta la punctele 3a si 3b.
4. Problema 4, pagina 4. Ideea este de a deplasa elementele dea lungul axei astfel ncat sa devina toate pozitive (caz pentru care algoritmul Countsort functioneaza). Aceasta deplasare se obtine adunand o
cantitate potrivita, care poate fi luata ca min {x(i)}!. Dupa ce se
i=1,n
efectueaza sortarea, se revine la valorile originale prin scaderea cantitatii adunate anterior.
Start CountSortModificat
citeste( n, (x(i), i = 1, n) )
min = M inimSir(x, n){Functie care determina minimul unui sir}
pentru i = 1, n
x(i) = x(i) + (min)
sfarsit pentru
{Aici apare algoritmul CountSort}
pentru i = 1, n
y(i) = y(i) (min)
sfarsit pentru
Stop
189
Rezolv
ari pentru capitolul 10
1. Problema 1. Pentru fiecare element x(i) se determina daca apare si n
sirul y. In caz afirmativ, x(i) va face parte si din multimea intersectie.
Faptul ca n acest fel se obtine multimea intersectie este (speram) evident. Complexitatea algoritmului este dependenta de modul n care se
face cautarea lui x(i) n y.
Daca se face cautare liniara (vezi rezolvarea 3, pagina 158), atunci
complexitatea este (mn)
Daca se cauta binar (vezi rezolvarea de la 4b, pagina 160), atunci:
y trebuie sortat n prealabil, deci avem o complexitate (m log2 m)
x(i) este cautat binar n y, pentru i = 1, n, deci se mai
adauga complexitatea (n log2 m)
In total se obtine o complexitate de ((m + n) log m). Este mai
avantajos ca sa se faca sortarea si cautarea n sirul cel mai scurt.
Lasam cititorul sa conceapa algotritmul pentru rezolvarea acestei probleme. In ceea ce priveste functia care se optimizeaza, este vorba de:
max f (x, y) = |{a : a n x si a n y}|
(|A| reprezinta numarul de elemente ale multimii A), care se regaseste
si n algoritmul prezentat (cu toate ca nu este evident).
2. Problema 2. Ideea algoritmului este de a calcula pentru fiecare obiect
valoarea lui unitara, i.e. cat se castiga din transportarea unei unitati
din obiectul respectiv. Suntem mai interesati n a considera obiectele
profitabile (pret mare, greutate mica) naintea celor pentru care raportul pret/greutate este mic. Algoritmul dat mai jos sintetizeaza aceasta
strategie.
Start RucsacContinuu
citeste( n, G, (c(i), g(i), i = 1, n) )
pentru i = 1, n
c(i)
v(i) = g(i)
sfarsit pentru
Sortare(c, g, v, n)
gc = 0{gc reprezinta greutatea curenta pusa n rucsac}
190
n
P
i=1
Avem ca:
S=
n
X
f (i)c(i)
(B.2)
f (i)g(i)
(B.3)
i=1
si
G=
k
X
i=1
(B.4)
S =
n
X
f 0 (i)c(i)
(B.5)
f 0 (i)g(i)
(B.6)
i=1
Avem:
0
G =
n
X
i=1
(B.7)
i6=p,t
0
(B.8)
(B.9)
k=1
pentru i = 2, n
daca st(i) sf (k) atunci
scrie( Spectacol care ncepe la , st(i), si se sfarseste la,
sf (i) )
k=i
sfarsit daca
sfarsit pentru
Stop
Algoritmul Sortarest, sf, n interschimba elementele din st, respectiv sf
astfel ncat sf (1) sf (2) sf (n).
Complexitate: avem o sortare care se poate face n timp (n log n) si
o parcurgere a sirului st, deci n total (n log n).
4. Problema 4. Desi problema are un caracter teoretic pronuntat, rezultatul obtinut poate fi folosit n numeroase cazuri. In [6] se arata ca
strategia pentru aceasta problema este:
(a) Se sorteaza elementele celor doua multimi
(b) Daca A = {a1 , . . . , am } are k elemente negative si p elemente
pozitive, se aleg primele k elemente ale lui B si ultimele p elemente
ale lui B.
Algoritmul este:
Start Maxim
citeste( m, n, (a(i), i = 1, m), (b(i), i = 1, n) )
Sortare(a, m)
Sortare(b, n)
k=1
cat timp k m si a(k) < 0
k =k+1
sfarsit cat timp
k =k1
suma = 0
pentru i = 1, k
suma = suma + a(i) b(i)
sfarsit pentru
pentru i = k + 1, m
suma = suma + a(i) b(n m + i)
194
sfarsit pentru
Stop
5. Problema 5. Timpul de asteptare pentru un client este compus din
timpii de servire ai clientilor care sunt procesati naintea lui adunat cu
timpul de servire pentru el nsusi. De aceea timpul total de asteptare
nu este indiferent fata de ordinea de servire. De exemplu, daca avem
doi clienti, cu t1 = 1 si t2 = 2, atunci servirea n ordinea 1, 2 duce la
un timp de asteptare de 1 + (1 + 2) = 4, pe cand daca ar fi procesati
invers, sar ajunge la timpul 2 + (2 + 1) = 5. Intuitiv, este mai bine
daca clientii sunt procesati n ordinea crescatoare a timpilor de servire
(n acest fel un timp de servire mare are un impact mai tarziu).
Fie p o permutare a indicilor clientilor (corespunzand unei ordini de
intrare); clientii sunt preluati n ordinea p1 , p2 , . . . , pn . Pentru aceasa
ordine, timpul total de servire este:
n
X
(n i + 1)tpk
i=1
(B.10)
Fie p permutarea care determina T din (B.10) minim. Fie p0 permutarea care se obtine din p printro inversiune: p0 (i) = p(j), p0 (j) =
p(i) (i < j), p0 (k) = p(k), k 6= i, j. Atunci avem ca T (p) T (p0 ), ceea
ce este echivalent cu (j i)(tpi tpj ) 0, ceea ce conduce la tpi < tpj ,
ceea ce confirma valabilitatea strategiei.
Algoritmul este:
Start Procesare
citeste( n, (t(i), i = 1, n) )
pentru i = 1, n
p(i) = i
sfarsit pentru
Sortaret, p, n
pentru i = 1, n
scrie( p(i) )
sfarsit pentru
Stop
Algoritmul Sortare(t, p, n) sorteaza elementele lui t, n paralel facand
aceleasi interschimbari n vectorul de permutare p.
195
Rezolv
ari pentru capitolul 11
1. Problema 1. Tablourile m si s, analoage celor din Tabelul 11.2, pagina
120, au valorile:
1 2
3
4
0 150 330 405
0 0 360 330
0 0
0 180
0 0
0
0
0 0
0
0
0 0
0
0
5
1655
2430
930
3000
0
0
6
2010
1950
1770
1860
1500
0
1
0
0
0
0
0
0
2
1
0
0
0
0
0
3
2
2
0
0
0
0
4
2
2
3
0
0
0
5
4
2
4
4
0
0
6
2
2
4
4
5
0
(B.11)
Fie . m(, ) este accesat n cadrul (B.11) fie ca m(i, k), fie
ca m(k + 1, j). Fie t(, ) numarul de apeluri ce se face pentru
m(, ) n primul caz si T (, ) numarul de apeluri ce rezulta
din al doilea caz.
Pentru primul caz, avem i = , k = . Putem avea j { +1, n},
deci T (, ) = n . Pentru U (, ) se poate arata analog ca
U (, ) = n . Obtinem:
n X
n
X
n3 n
R(, ) = T (, ) =
(n + 1) = =
3
=1 =
(d) Problema 4. Fie a = (a(i, j)) matricea triunghiulara data la intrare (1 i n, 1 j i). Observam ca avem 2n1 posibilitati
de a forma drumuri de la a(1, 1) la ultima linie (demonstratia se
face prin inductie matematica dupa n). Ca atare, o metoda de
abordare de tip brute force este de la nceput sortita esecului.
De asemenea se observa ca o strategie de tipul coboara la numarul
cel mai mare aflat pe linia imediat urmatoare nu duce la solutie
optima. Vom da un algoritm bazat pe programare dinamica.
Fie D drumul de la a(1, 1) la ultima linie care da suma maixima.
Fie S(i, j) un element de pe acest drum, aflat pe linia i si coloana
j. Atunci S(i, j) este valoarea maxima care se poate obtine
plecand din (i, j) pana la ultima linie. Daca nu ar fi asa, atunci
nseamna ca ar exista un alt drum de la (i, j) la ultima linie care
da valoare mai mare, iar prin modificarea lui D astfel ncat sa includa acest ultim drum am obtine o valoare mai mare, contradictie
cu alegerea lui D. Ca atare, putem deduce ca:
S(n, j)
daca i = n
S(i, j) =
a(i, j) + max{S(i + 1, j), S(i + 1, j + 1)} daca i < n
(B.12)
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 triunghiulara.
Pentru reconstituirea solutiei se foloseste o matrice triunghilara
sol
Start Triunghi
197
k = s(i, j)
scrie( ( )
Inmultiri(s, i, k)
scrie( )*( )
Inmultiri(s, k + 1, j)
scrie( ) )
Return
Propunem cititorului interesat crearea unui algoritm care sa determine
toate modalitatile de nmultire a matricilor astfel ncat sa se ajunga la
un numar de nmultiri scalare minim. (Indicatie: nu se mai foloseste
informatiua din s, ci doar cea din m)
199
200
Bibliografie
[1] Ciurea,Eleonor, Tabarca,Sabin, and Tabarca,Tatiana. Algoritmi. Metode
de elaborare. Ed. Univ. Transilvania Brasov, 1997.
[2] Cormen, Thomas H, Leiserson, E Charles, and Rivest, Ronald R. Introducere n algoritmi. Computer Libris Agora, 2000.
[3] Iacob,Paul and Marinescu,Daniela. Upon a cutting problem with two optimization criteria. Conference on functional analysis, equations, approximation and convexity. Cluj-Napoca, 1999. pp 84-88.
[4] Iacob,Paul, Marinescu,Daniela, and Luca, Cristina. Covering a rectangle
with rectangular pieces. Proceedings of the 17th International Logistic
Congress on Logistics from to : Strategies and applications. Salonic,
Greece, 2001. pp 506-513.
[5] Livovschi, Leon and Georgescu, Horia. Sinteza si analiza algoritmilor.
Editura Stiintifica si Enciclopedica, 1986.
[6] Tudor, Sorin. Tehnici de programare (Manual pentru clasa a X-a). L&S
Infomat, 1996.
201