Documente Academic
Documente Profesional
Documente Cultură
Note de curs
(draft v1.1)
Prefat a
Cand dorim s a reprezentam obiectele din lumea real a ntr-un program pe
calculator, trebuie s a avemn vedere:
modelarea obiectelor din lumea real a sub forma unor entit at i matematice
abstracte si tipuri de date,
operat iile pentru nregistrarea, accesul si utilizarea acestor entit at i,
reprezentarea acestor entitat i n memoria calculatorului, si
algoritmii pentru efectuarea acestor operat ii.
Primele doua elemente sunt n esent a de natur a matematica si se refera la
ce structuri de date si operat ii trebuie s a folosim, iar ultimile dou a elemente
implic a faza de implementare si se refera la cum sa realiz am structurile de date
si operat iile. Algoritmica si structurile de date nu pot separate. Desi algoritmica
si programarea pot separate, noi nu vom face acest lucru, ci vom implementa
algoritmii ntr-un limbaj de programare (Pascal, C/C++, Java). Din aceast a cauza
acest curs este si o init iere n algoritmic a si programare.
Scopul cursului este subordonat scopului specializarii (informatica, n cazul
nostru) care este sa preg ateasc a specialisti competent i, cu nalta calicare n
domeniul informaticii, cadre didactice competente n acest domeniu (profesor de
informatica n gimnaziu si liceu), informaticieni n diverse domenii cu prol tehnic,
economic, etc. ce pot ncepe lucrul imediat dup a absolvirea facult at ii.Dezideratul
nal este deci competent a. Competent a ntr-un domeniu de activitate implica
experient a n rezolvarea problemelor din acel domeniu de activitate. At at
competent a cat si experient a n rezolvarea problemelor se pot obt ine numai dac a
permanent se ntreprind eforturi pentru nsusirea de noi cunostint e. De exemplu,
orice informatician (programator sau profesor) care elaboreaza programe pentru
rezolvarea unor probleme diverse, trebuie sa aib a competent e conform schemei
1
:
PROBLEMA
(model fizic)
ALGORITMICA
(model virtual)
PROGRAMARE
Gandire algoritmica Experienta
(rezolvarea de probleme)
Cursul de Algoritmi si structuri de date este util (si chiar necesar) pentru
formarea competent elor si abilitat ilor unui bun programator sau profesor de
informatica. Pentru a vedea care sunt aceste competent e si abilit at i putem, de
1
M. Vlada; E-Learning si Software educat ional; Conferint a Nat ionala de
Inv at amant Virtual,
Bucuresti, 2003
exemplu, s a citim Programa pentru informatic a - Concursul nat ional unic pentru
ocuparea posturilor didactice declarate vacante n nv at am antul preuniversitar.
2
In exemplul de mai sus sunt declarate trei variabile (x, y si z) de tip int
(adic a de tip ntreg cu semn). Spat iul alocat variabilelor de tip int este de 4 octet i
(32 bit i). Aceasta nseamna ca o astfel de variabil a poate avea valori ntre 2
63
si
2
63
1. Valoarea maxima este de aproximativ 2 miliarde.
In programul anterior x are valoarea 200.000 iar y are valoarea 300.000, deci
produsul are valoarea 60 miliarde care dep aseste cu mult valoarea maxima de 2
miliarde.
_
a
1,1
a
1,2
a
1,n
a
2,1
a
2,2
a
2,n
.
.
.
a
m,1
a
m,1
a
m,n
_
_
poate reprezentata ca o dat a structurata (tablou bidimensional) specicnd ecare
element prin doi indici (de linie si de coloana).
O structur a de date este deci o colect ie de date, eventual de tipuri diferite,
pe care s-a denit o anumit a organizare si careia i este specic un anumit mod de
identicare a elementelor componente. Componetele unei structuri de date pot
identicate prin nume sau prin ordinea pe care o ocup a n cadrul structurii.
Daca accesul la o anumita component a a structurii de date se poate face far a
sa t inem seama de celelalte componente, vom spune ca structura de date este cu
acces direct.
In schimb, dac a accesul la o componenta a structurii de date se poate
face numai t in and cont de alte c ampuri ale structurii (n conformitate cu ordinea
structurii, printr-un proces de traversare) atunci vom spune c a structura este cu
acces secvent ial.
Structurile de date pot create pentru a depozitate n memoria intern a
(aceste structuri de date se numesc structuri interne) sau n memoria extern a (se
numesc structuri externe, sau siere). Structurile interne au un caracter de date
temporare (ele dispar odat a cu ncetarea activitat ii de prelucrare) iar cele externe
au un caracter de date permanente (mai bine spus, de lung a durat a).
Daca pe l ang a componentele structurii se nregistreaz a pe suport si alte date
suplimentare care s a materializeze relat ia de ordonare, atunci structura de date
respectiv a este explicit a, n caz contrar este implicita. De exemplu, structura de
date de tip tablou este o structura implicita de date iar structura de date de tip
list a liniar a este o structura explicit a de date.
Asupra structurilor de date se pot efectua operat ii care se refera structura
respectiv a sau la valorile datelor componente. Cele mai importante operat ii sunt:
operat ia de creare, care consta n memorarea pe suportul de memorie a
structurii de date n forma sa init ial a,
10 CAPITOLUL 2. STRUCTURI DE DATE
operat ia de consultare, care consta n accesul la elementele structurii n
vederea prelucr arii valorilor acestora, si
operat ia de actualizare, care consta n adaugarea de noi elemente, sau
eliminarea elementelor care nu mai sunt necesare, sau modicarea valorilor unor
componente ale structurii.
Toate structurile de date la fel organizate si pe care s-au denit aceleasi
operat ii, poart a numele de tip de structur a de date. Daca analizamns a operat iile
care se efectueaz a asupra unei structuri de date, vom putea vedea c a toate acestea
se reduc la executarea, eventual repetat a, a unui grup de operat ii specice numite
operat ii de baz a.
2.2 Structuri si tipuri de date abstracte
2.2.1 Structuri de date abstracte
Abstractizarea datelor reprezint a de fapt concentrarea asupra esent ialului,
ignor and detaliile (sau altfel spus, conteaz a ce nu cum).
St ap anirea aplicat iilor complexe se obt ine prin descompunerea n module.
Un modul trebuie sa e simplu, cu complexitatea ascunsan interiorul lui, si sa
aib a o interfat a simpl a care sa permit a folosirea lui f ar a a cunoaste implementarea.
O structur a de date abstracta este un modul constand din date si operat ii.
Datele sunt ascunse n interiorul modulului si pot accesate prin intermediul
operat iilor. Structura de date este abstract a deoarece este cunoscuta numai interfat a
structurii, nu si implementarea (operat iile sunt date explicit, valorile sunt denite
implicit, prin intermediul operat iilor).
2.2.2 Tipuri de date abstracte
Procesul de abstractizare se refera la dou a aspecte:
abstractizarea procedural a, care separa propriet at ile logice ale unei act iuni de
detaliile implementarii acesteia
abstractizarea datelor, care separa propriet at ile logice ale datelor de detaliile
reprezentarii lor
O structur a de date abstracte are un singur exemplar (o singura instant a).
Pentru a crea mai multe exemplare ale structurii de date abstracte se deneste un
tip de date abstract.
In Java, de exemplu, clasa asigur a un mod direct de denire
a oric arui tip de date abstract.
2.3. STRUCTURI DE DATE ELEMENTARE 11
2.3 Structuri de date elementare
2.3.1 Liste
O list a este o colect ie de elemente de informat ie (noduri) aranjate ntr-o
anumit a ordine. Lungimea unei liste este numarul de noduri din lista. Structura
corespunzatoare de date trebuie s a ne permit a sa determinam ecient care este
primul/ultimul nod n structur a si care este predecesorul/succesorul unui nod dat
(dac a exista). Iat a cum arat a cea mai simpla lista, lista liniar a:
capul
listei
coada
listei
Figura 2.1: List a liniar a
O list a circular a este o lista n care, dup a ultimul nod, urmeaza primul nod,
deci ecare nod are succesor si predecesor.
Cateva dintre operat iile care se efectueaz a asupra listelor sunt: inserarea
(ad augarea) unui nod, extragerea (stergerea) unui nod, concatenarea unor liste,
num ararea elementelor unei liste etc.
Implementarea unei liste se realizeaza n dou a moduri: secvent ial si n ant uit.
Implementarea secvential a se caracterizeaza prin plasarea nodurilor n locat ii
succesive de memorie, n conformitate cu ordinea lor n lista. Avantajele acestui
mod de implementare sunt accesul rapid la predecesorul/succesorul unui nod si
g asirea rapid a a primului/ultimului nod. Dezavantajele sunt modalit at ile relativ
complicate de inserarea/stergere a unui nod si faptul c a, n general, nu se foloseste
ntreaga memorie alocata listei.
Implementarea nl ant uit a se caracterizeaza prin faptul c a ecare nod cont ine
dou a p art i: informat ia propriu-zis a si adresa nodului succesor. Alocarea memoriei
pentru ecare nod se poate face n mod dinamic, n timpul rul arii programului.
Accesul la un nod necesita parcurgerea tuturor predecesorilor s ai, ceea ce conduce
la un consum mai mare de timp pentru aceast a operat ie.
In schimb, operat iile
de inserare/stergere sunt foarte rapide. Se consuma exact atat spat iu de memorie
cat este necesar dar, evident, apare un consum suplimentar de memorie pentru
nregistrarea leg aturii c atre nodul succesor. Se pot folosi dou a adrese n loc de
una, astfel nc at un nod s a cont in a pe lang a adresa nodului succesor si adresa
nodului predecesor. Obt inem astfel o list a dublu inl ant uit a, care poate traversat a
n ambele direct ii.
Listele nl ant uite pot reprezentate prin tablouri.
In acest caz, adresele
nodurilor sunt de fapt indici ai tabloului.
12 CAPITOLUL 2. STRUCTURI DE DATE
O alternativ a este sa folosim dou a tablouri val si next astfel: sa memoram
informat ia ecarui nod i n locat ia val[i], iar adresa nodului s au succesor n locat ia
next[i]. Indicele locat iei primului nod este memorat n variabila p. Vom conveni ca,
pentru cazul listei vide, s a avem p = 0 si next[u] = 0 unde u reprezint a ultimul nod
din lista. Atunci, val[p] va cont ine informat ia primului nod al listei, next[p] adresa
celui de-al doilea nod, val[next[p]] informat ia din al doilea nod, next[next[p]]
adresa celui de-al treilea nod, etc. Acest mod de reprezentare este simplu dar
apare problema gestion arii locat iilor libere. O solut ie este sa reprezentam locat iile
libere tot sub forma unei liste nlant uite. Atunci, stergerea unui nod din lista
init ial a implic a inserarea sa n lista cu locat ii libere, iar inserarea unui nod n lista
init ial a implic a stergerea sa din lista cu locat ii libere. Pentru implementarea listei
de locat ii libere, putem folosi aceleasi tablouri dar avem nevoie de o alt a variabil a,
freehead, care sa cont in a indicele primei locat ii libere din val si next. Folosim
aceleasi convent ii: dac a freehead = 0 nseamna ca nu mai avem locat ii libere, iar
next[ul] = 0 unde ul reprezint a ultima locat ie liber a.
Vom descrie in continuare doua tipuri de liste particulare foarte des folosite.
2.3.2 Stive si cozi
O stiv a este o lista liniar a cu proprietatea ca operat iile de inserare/extragere
a nodurilor se fac n/din coada listei. Daca nodurile A, B, C sunt inserate ntr-o
stiv a n aceasta ordine, atunci primul nod care poate sters/extras este C.
In mod
echivalent, spunem c a ultimul nod inserat este singurul care poate sters/extras.
Din acest motiv, stivele se mai numesc si liste LIFO (Last In First Out).
Cel mai natural mod de reprezentare pentru o stiv a este implementarea
secvent ial a ntr-un tablou S[1..n], unde n este numarul maxim de noduri. Primul
nod va memorat n S[1], al doilea n S[2], iar ultimul n S[top], unde top este
o variabil a care cont ine adresa (indicele) ultimului nod inserat. Init ial, cand stiva
este vida, avem (prin convent ie) top = 0.
O coada este o lista liniar a n care inser arile se fac doar n capul listei, iar
stergerile/extragerile se fac doar din coada listei. Din acest motiv, cozile se mai
numesc si liste FIFO (First In First Out).
O reprezentare secvent ial a pentru o coad a se obt ine prin utilizarea unui
tablou C[0..n1], pe care l trat am ca si cum ar circular: dup a locat ia C[n 1]
urmeaza locat ia C[0]. Fie tail variabila care cont ine indicele locat iei predecesoare
primei locat ii ocupate si e head variabila care cont ine indicele locat iei ocupate
ultima oar a. Variabilele head si tail au aceeasi valoare atunci si numai atunci c and
coada este vid a. Init ial, avem head = tail = 0.
Trebuie sa observam faptul c a testul de coad a vid a este acelasi cu testul de
coad a plin a. Dac a am folosi toate cele n locat ii la un moment dat, atunci nu am
putea distinge ntre situat ia de coad a plin a si cea de coada vid a, deoarece n
ambele situat ii am avea head = tail.
In consecint a, vom folosi efectiv, n orice
moment, cel mult n 1 locat ii din cele n ale tabloului C.
2.3. STRUCTURI DE DATE ELEMENTARE 13
2.3.3 Grafuri
Un graf este o pereche G =< V, M >, unde V este o mult ime de varfuri, iar
M V V este o mult ime de muchii. O muchie de la v arful a la v arful b este
notat a cu perechea ordonat a (a, b), dac a graful este orientat, si cu multimea a, b,
daca graful este neorientat.
Dou a v arfuri unite printr-o muchie se numesc adiacente. Un v arf care este
extremitatea unei singure muchii se numeste varf terminal.
Un drum este o succesiune de muchii de forma
(a
1
, a
2
), (a
2
, a
3
), ..., (a
n1
, a
n
)
sau de forma
a
1
, a
2
, a
2
, a
3
, ..., a
n1
, a
n
dup a cum graful este orientat sau neorientat. Lungimea drumului este egala cu
num arul muchiilor care l constituie. Un drum simplu este un drumn care nici un
v arf nu se repeta. Un ciclu este un drum care este simplu, cu except ia primului si
ultimului v arf, care coincid. Un graf aciclic este un graf f ar a cicluri.
Un graf neorientat este conex, dac a ntre oricare dou a v arfuri exist a un drum.
Pentru grafuri orientate, aceast a notiune este nt arit a: un graf orientat este tare
conex, dac a ntre oricare dou a v arfuri i si j exista un drum de la i la j si un drum
de la j la i.
V arfurilor unui graf li se pot atasa informat ii (numite valori), iar muchiilor
li se pot atasa informat ii numite uneori lungimi sau costuri.
Exist a cel put in trei moduri de reprezentare ale unui graf:
Printr-o matrice de adiacent a A, n care A[i, j] = true daca v arfurile i si j
sunt adiacente, iar A[i, j] = false n caz contrar. O alt a variant a este sa-i d am lui
A[i, j] valoarea lungimii muchiei dintre v arfurile i si j, considerand A[i, j] = +
atunci c and cele dou a v arfuri nu sunt adiacente. Cu aceast a reprezentare, putem
verica usor daca dou a v arfuri sunt adiacente. Pe de alt a parte, dac a dorim s a
a am toate varfurile adiacente unui v arf dat, trebuie s a analizam o ntreag a linie
din matrice. Aceasta necesita n operat ii (unde n este numarul de v arfuri n graf),
independent de num arul de muchii care conecteaza v arful respectiv.
Prin liste de adiacent a, adic a prin atasarea la ecare v arf i a listei de v arfuri
adiacente (pentru grafuri orientate, este necesar ca muchia sa plece din i).
Intr-
un graf cu m muchii, suma lungimilor listelor de adiacenta este 2m, dac a graful
este neorientat, respectiv m, dac a graful este orientat. Daca num arul muchiilor n
graf este mic, aceasta reprezentare este preferabila din punct de vedere al memoriei
necesare. Totusi, pentru a determina dac a dou a v arfuri i si j sunt adiacente, trebuie
sa analizam lista de adiacenta a lui i (si, posibil, lista de adiacenta a lui j), ceea ce
este mai put in ecient dec at consultarea unei valori logicen matricea de adiacent a.
Printr-o list a de muchii. Aceasta reprezentare este ecienta atunci c and
avem de examinat toate muchiile grafului.
14 CAPITOLUL 2. STRUCTURI DE DATE
2.3.4 Arbori binari
Un arbore este un graf neorientat, aciclic si conex. Sau, echivalent, un arbore
este un graf neorientat n care exista exact un drum ntre oricare dou a v arfuri.
Un arbore reprezentat pe niveluri se numeste arbore cu r adacina. V arful
plasat pe nivelul 0 se numeste r adacina arborelui. Pe ecare nivel i > 0 sunt
plasate v arfurile pentru care lungimea drumurilor care le leag a de r ad acin a este i.
Varfurile de pe un nivel i > 0 legate de acelasi varf j de pe nivelul i 1 se
numesc descendent ii direct i (ii) v arfului j iar v arful j se numeste ascendent direct
(tata) al acestor v arfuri.
Daca exista un drum de la un v arf i de pe nivelul ni la un v arf j de pe nivelul
nj > ni, atunci v arful i se numeste ascendent al lui j, iar v arful j se numeste
descendent al lui i.
Un v arf terminal (sau frunz a) este un varf f ar a descendent i. Varfurile care
nu sunt terminale se numesc neterminale.
Un arbore n care orice v arf are cel mult doi descendent i se numeste arbore
binar.
Intr-un arbore binar, num arul maxim de varfuri aate pe nivelul k este 2
k
.
Un arbore binar de n alt ime k are cel mult 2
k+1
1 v arfuri, iar dac a are exact
2
k+1
1 v arfuri, se numeste arbore plin.
Varfurile unui arbore plin se numeroteazan ordinea nivelurilor. Pentru acelasi
nivel, numerotarea se face n arbore de la st anga la dreapta.
nivelul 0
nivelul 1
nivelul 2
nivelul 3
1
2 3
4
5
6 7
8
9 10
11 12 13 14 15
Figura 2.2: Arbore binar plin
Un arbore binar cu n v arfuri si de n alt ime k este complet, dac a se obt ine
din arborele binar plin de n alt ime k, prin eliminarea, dac a este cazul, a varfurilor
numerotate cu n + 1, n + 2, ..., 2
k+1
1.
2.3. STRUCTURI DE DATE ELEMENTARE 15
Acest tip de arbore se poate reprezenta secvent ial folosind un tablou T,
pun and v arfurile de ad ancime k, de la st anga la dreapta, n pozit iile T[2
k
], T[2
k
+1],
..., T[2
k+1
1] (cu posibila except ie a ultimului nivel care poate incomplet).
nivelul 0
nivelul 1
nivelul 2
nivelul 3
1
2 3
4
5
6 7
8
9 10
11 12
Figura 2.3: Arbore binar complet
Tatal unui v arf reprezentat n T[i], i > 0, se aa n T[i/2]. Fiii unui v arf
reprezentat n T[i] se aa, dac a exista, n T[2i] si T[2i + 1].
2.3.5 Heap-uri
Un max-heap (heap=gramad a ordonat a, n traducere aproximativ a) este
un arbore binar complet, cu urm atoarea proprietate: valoarea ecarui varf este
mai mare sau egal a cu valoarea ecarui u al s au.
Un min-heap este un arbore binar complet n care valoarea ecarui v arf este
mai mica sau egal a cu valoarea ecarui u al s au.
nivelul 0
nivelul 1
nivelul 2
nivelul 3
2
3
4 5 6
7
9
10
11
7
7
7
Figura 2.4: Max-heap
16 CAPITOLUL 2. STRUCTURI DE DATE
Acelasi heap poate reprezentat secvent ial prin urm atorul tablou:
11 7 10 7 7 9 2 4 6 5 7 3
Caracteristica de baza a acestei structuri de date este ca modicarea valorii
unui v arf se face foarte ecient, pastr andu-se proprietatea de heap.
De exemplu, ntr-un max-heap, daca valoarea unui varf creste, astfel nc at
depaseste valoarea tatalui, este sucient s a schimbam ntre ele aceste doua valori
si sa continu am procedeul n mod ascendent, pan a cand proprietatea de heap este
restabilit a. Dac a, dimpotriv a, valoarea varfului scade, astfel nc at devine mai mica
dec at valoarea cel put in a unui u, este sucient sa schimbam intre ele valoarea
modicata cu cea mai mare valoare a iilor, apoi s a continu am procesul n mod
descendent, pan a cand proprietatea de heap este restabilita.
Heap-ul este structura de date ideala pentru extragerea maximului/minimului
dintr-o mult ime, pentru inserarea unui varf, pentru modicarea valorii unui v arf.
Sunt exact operat iile de care avem nevoie pentru a implementa o list a dinamica
de priorit at i: valoarea unui varf va da prioritatea evenimentului corespunzator.
Evenimentul cu prioritatea cea mai mare/mica se va aa mereu la radacina
heap-ului, iar prioritatea unui eveniment poate modicat a n mod dinamic.
2.3.6 Structuri de mult imi disjuncte
S a presupunem c a avem N elemente, numerotate de la 1 la N. Numerele care
identic a elementele pot , de exemplu, indici intr-un tablou unde sunt memorate
valorile elementelor. Fie o partitie a acestor N elemente, formata din submult imi
dou a cate dou a disjuncte: S1, S2, ... . Presupunem c a ne intereseaza reuniunea a
dou a submult imi, S
i
S
j
.
Deoarece submult imile sunt doua cate dou a disjuncte, putem alege ca etichet a
pentru o submult ime oricare element al ei. Vom conveni ca elementul minim al unei
mult imi sa e eticheta mult imii respective. Astfel, multimea 3, 5, 2, 8 va numit a
multimea 2.
Vom aloca tabloul set[1..N], n care ecarei locatii set[i] i se atribuie eticheta
submult imii care cont ine elementul i. Avem atunci proprietatea: set[i] i, pentru
1 i N. Reuniunea submult imilor etichetate cu a si b se poate realiza astfel:
procedure reuniune(a, b)
i a;
j b
if i > j
then interschimb a i si j
for k j to N do
if set[k] = j
then set[k] i
Capitolul 3
Algoritmi
3.1 Etape n rezolvarea problemelor
Principalele etape care se parcurg n rezolvarea unei probleme sunt:
(a) Stabilirea datelor init iale si a obiectivului (ce trebuie determinat).
(b) Alegerea metodei de rezolvare.
(c) Aplicarea metodei pentru date concrete.
Exemplu.
S a presupunem c a problema este rezolvarea, n R, a ecuat iei x
2
3x+2 = 0.
(a) Datele init iale sunt reprezentate de catre coecient ii ecuat iei iar
obiectivul este determinarea rad acinilor reale ale ecuat iei.
(b) Vom folosi metoda de rezolvare a ecuat iei de gradul al doilea av and
forma general a ax
2
+ bx + c = 0. Aceasta metoda poate descris a
astfel:
Pasul 1. Se calculeaza discriminantul: = b
2
4ac.
Pasul 2. Daca > 0
atunci ecuat ia are dou a r ad acini reale distincte: x
1,2
=
b
2a
altfel, daca = 0
atunci ecuat ia are o r ad acina real a dubl a: x
1,2
=
b
2a
altfel ecuat ia nu are r ad acini reale.
(c) Aplicarea metodei pentru datele problemei (a = 1, b = 3, c = 2)
conduce la rezultatul: x
1
= 1, x
2
= 2.
17
18 CAPITOLUL 3. ALGORITMI
3.2 Algoritmi
3.2.1 Ce este un algoritm?
Un algoritm este o succesiune de operat ii aritmetice si/sau logice care,
aplicate asupra unor date, permit obt inerea rezultatului unei probleme
din clasa celor pentru care a fost conceput.
S a observam ca nu apare n denit ie cuvantul calculator; algoritmii nu au
neap arat leg atur a cu calculatorul. Totusi, n acest curs ne vom concentra aproape
exclusiv pe algoritmi care pot implementat i rezonabil pe calculator. Altfel spus,
ecare pas din algoritm trebuie astfel g andit nc at ori este suportat direct de catre
limbajul de programare favorit (operat ii aritmetice, cicluri, recursivitate, etc) ori
este aseman ator cu ceva nv at at mai nainte (sortare, cautare binar a, parcurgere
n ad ancime, etc).
Secvent a de pasi prin care este descrisa metoda de rezolvare a ecuat iei de
gradul al doilea (prezentata n sect iunea anterioar a) este un exemplu de algoritm.
Calculul efectuat la Pasul 1 este un exemplu de operat ie aritmetica, iar analiza
semnului discriminantului (Pasul 2) este un exemplu de operat ie logica.
Descrierea unui algoritm presupune precizarea datelor init iale si descrierea
prelucrarilor efectuate asupra acestora. Astfel, se poate spune ca:
algoritm = date + prelucrari
Al-Khwarizmi a fost cel care a folosit pentru prima dat a reguli precise si clare
pentru a descrie procese de calcul (operat ii aritmetice fundamentale) n lucrarea
sa Scurt a carte despre calcul algebric. Mai t arziu, aceasta descriere apare sub
denumirea de algoritm n Elementele lui Euclid. Algoritmul lui Euclid pentru
calculul celui mai mare divizor comun a doua numere naturale este, se pare, primul
algoritm cunoscut n matematic a.
In matematic a not iunea de algoritm a primit mai multe denit ii: algoritmul
normal al lui A. A. Markov, algoritmul operat ional al lui A. A. Leapunov, masina
Turing, funct ii recursive, sisteme POST. S-a demonstrat ca aceste denit ii sunt
echivalente din punct de vedere matematic.
In momentul execut iei, atat timp cat condit ia este adevarat a, se va executa
instruct iunea. Daca condit ia nu este la nceput satisf acut a, atunci instruct iunea
nu se efectueaz a niciodata.
Prelucrarea repetitiv a cu test nal se descrie prin:
do <prel> while <condit ie>;
Prelucrarea se repeta p an a cand condit ia specicata devine fals a.
In acest caz
prelucrarea se efectueaza cel put in o dat a, chiar dac a condit ia nu este satisfacuta
la nceput.
Prelucrarea repetitiv a cu contor se caracterizeaza prin repetarea prelucr arii
de un num ar prestabilit de ori si este descrisa prin:
for i = i
1
, i
2
, ..., i
n
<prel>;
sau
for i = i
1
, i
2
, ..., i
n
do <prel>;
unde i este variabila contor care ia, pe r and, valorile i
1
, i
2
, ..., i
n
n aceasta
ordine, prelucrarea ind efectuat a pentru ecare valoare a contorului.
Alte forme utilizate sunt:
for i = v
i
to v
f
do <prel>;
n care contorul ia valori consecutive cresc atoare ntre v
i
si v
f
, si
for i = v
i
downto v
f
do <prel>;
n care contorul ia valori consecutive descrescatoare ntre v
i
si v
f
.
26 CAPITOLUL 3. ALGORITMI
3.4.6 Subalgoritm
...
/* prelucrari specice subalgoritmului */
...
return <nume rezultat>;
unde <nume subalg> reprezint a numele subalgoritmului iar nume p1, nume p2,
... reprezinta numele parametrilor. Ultimul enunt , prin care se returneaz a rezultatul
calculat n cadrul subalgoritmului, este optional.
Modul de apel depinde de modul n care subalgoritmul returneaz a rezultatele
sale. Daca subalgoritmul returneaz a efectiv un rezultat, printr-un enunt de forma
return <nume rezultat>;
atunci subalgoritmul se va apela n felul urm ator:
v=<nume subalg>(nume p1, nume p2, ...);
Acesti subalgoritmi corespund subprogramelor de tip funct ie.
Daca n subalgoritm nu apare un astfel de enunt , atunci el se va apela prin:
<nume subalg>(nume p1, nume p2, ...);
variant a care corespunde subprogramelor de tip procedur a.
Observat ie. Prelucr arile care nu sunt detaliate n cadrul algoritmului sunt
descrise n limbaj natural sau limbaj matematic. Comentariile suplimentare vor
cuprinse ntre /* si */. Daca pe o linie a descrierii algoritmului apare simbolul //
atunci tot ce urmeaza dup a acest simbol, pe aceeasi linie cu el, este interpretat ca
ind un comentariu (deci, nu reprezinta o prelucrare a algoritmului).
3.4. LIMBAJ ALGORITMIC 27
3.4.7 Probleme rezolvate
1. Algoritmului lui Euclid.
Descrierea n pseudocod a algoritmului lui Euclid este urmatoarea:
int a, b, d, i, r;
read a, b;
if (a<b) d=a; i=b; else d=b; i=a; ;
r = d % i;
while (r ! = 0) d=i; i=r; r=d % i; ;
write i;
2. Schema lui Horner.
Descrierea n pseudocod a schemei lui Horner este urmatoarea:
int n, a, b, i;
read n, a, b;
int a[0..n], c[0..n-1];
for i=n,0,-1 read a[i];
c[n-1]=b*a[n];
for i=1,n-1 c[n-i-1]=b*c[n-i]+a[n-i];
val:=b*c[1]+a[1];
write val;
3. Conversia unui num ar natural din baza 10 n baza 2.
Fie n un num ar ntreg pozitiv. Pentru a determina cifrele reprezentarii n
baza doi a acestui num ar se poate folosi urm atoarea metoda:
Se mparte n la 2, iar restul va reprezenta cifra de rang 0. C atul obt inut la
mpartirea anterioar a se mparte din nou la 2, iar restul obt inut va reprezenta cifra
de ordin 1 s.a.m.d. Secvent a de mp art iri continu a pn a la obt inerea unui c at nul.
Descrierea n pseudocod a acestui algoritm este:
int n, d, c, r;
read n;
d = n;
c = d / 2; /* catul mp art irii ntregi a lui d la 2 */
r = d % 2; /* restul mp art irii ntregi a lui d la 2 */
write r;
while (c != 0)
d = c;
c = d / 2; /* catul mp art irii ntregi a lui d la 2 */
r = d % 2; /* restul mp art irii ntregi a lui d la 2 */
write r;
int i, p;
p = 1;
for i=2,n p = p*a;
return p;
k=0
1
k!
.
Rezolvare. Calculul aproximativ (cu precizia ) al limitei sirului s
n
consta n
calculul sumei nite s
k
, unde ultimul termen al sumei, t
k
=
1
k!
, are proprietatea
t
k
< .
Intrucat t
k+1
=
t
k
k+1
, aceasta relat ie va folosit a pentru calculul valorii
termenului curent (permit and micsorarea num arului de calcule).
double eps, t, s;
int k;
k=1; /* init ializare indice */
t=1; /* init ializare termen */
s=1; /* init ializare suma */
do
s=s+t; /* ad augarea termenului curent */
k=k+1;
t=t/k; /* calculul urm atorului termen */
while (t eps);
s=s+t; (* ad augarea ultimului termen *)
write s;
8. Fie A o matrice cu m linii si n coloane, iar B o matrice cu n linii si p coloane,
ambele avand elemente reale. S a se determine matricea produs C = A B.
Rezolvare. Matricea C va avea m linii si p coloane, iar ecare element se
determin a efectuand suma:
c
i,j
=
n
k=1
a
i,k
b
k,j
, 1 i m, 1 j p.
write c;
3.4.8 Probleme propuse
1. Fie D o dreapt a de ecuat ie ax+by+c = 0 si (C) un cerc de centru O(x
0
, y
0
)
si raza r. S a se stabileasca pozit ia dreptei fat a de cerc.
Indicat ie. Se calculeaza distant a de la centrul cercului la dreapta D utiliz and
formula:
d =
[ax
0
+by
0
+c[
a
2
+b
2
.
Daca d r+ atunci dreapta este exterioar a cercului, daca d r atunci dreapta
este secanta, iar dac a r < d < r + atunci este tangent a (la implementarea
egalitatea ntre dou a numere reale ...).
2. S a se genereze primele n elemente ale sirurilor a
k
si b
k
date prin relat iile
de recurent a:
a
k+1
=
5a
k
+ 3
a
k
+ 3
, b
k
=
a
k
+ 3
a
k
+ 1
, k 0, a
0
= 1.
3. S a se determine r ad acina p atrat a a unui num ur real pozitiv a cu precizia
= 0.001, folosind relat ia de recurent a:
x
n+1
=
1
2
_
x
n
+
a
x
n
_
, x
1
= a.
Precizia se consider a atins a cand [x
n+1
x
n
[ < .
4. Fie A o matrice patratica de dimensiune n. S a se transforme matricea A,
prin interrschimb ari de linii si de coloane, astfel nc at elementele de pe diagonala
principal a sa e ordonate cresc ator.
3.4. LIMBAJ ALGORITMIC 31
5. S a se determine cel mai mare divizor comun al unui sir de numere ntregi.
6. S a se calculeze coecient ii polinomului
P[X] = (aX +b)
n
, a, b Z, n N.
7. Fie A o matrice patratica. S a se calculeze suma elementelor din ecare zona
(diagonala principal a, diagonala secundar a, etc.) marcata n gura urm atoare:
a. b. c.
Figura 3.1: Zone n matrice patratica
8. Fie x
1
, x
2
, ..., x
n
Z r ad acinile unui polinom cu coecient i ntregi:
P[X] = a
n
X
n
+a
n1
X
n1
+... +a
1
X +a
0
.
S a se determine coecient ii polinomului.
9. S a se determine toate rad acinile rat ionale ale polinomului P[X] care are
coecient i ntregi.
140. Fie [P
1
, P
2
, ..., P
n
] un poligon convex dat prin coordonatele carteziene
ale v arfurilor sale (n ordine trigonometrica). S a se calculeze aria poligonului.
11. Fie f : [a, b] R o funct ie continu a cu proprietatea ca exista un unic
(a, b) care are proprietatea ca f() = 0. S a se aproximeze cu precizia = 0.001
utiliz and metoda bisect iei.
12. Fie P si Qpolinoame cu coecient i ntregi. S a se determine toate rad acinile
rat ionale comune celor dou a polinoame.
13. S a se determine toate numerele prime cu maxim 6 cifre care raman prime
si dup a r asturnarea lor (r asturnatul num arului abcdef este fedcba).
32 CAPITOLUL 3. ALGORITMI
3.5 Instruct iuni corespondente limbajului algorit-
mic
3.5.1 Declararea datelor
Datele simple se declara sub forma:
<tip> <nume>;
sau
<tip> <nume>= literal;
unde <tip> poate lua una dintre urm atoarele valori: byte, short, int, long, oat,
double, boolean, char.
In exemplul urmator sunt prezentate c ateva modalitat i
de declarare pentru diferite tipuri de variabile.
class Literali
{
public static void main(String args[])
{
long l1 = 5L;
long l2 = 12l;
int i1hexa = 0x1;
int i2hexa = 0X1aF;
int i3octal = 01;
long i4octal = 012L;
long i5LongHexa = 0xAL;
float f1 = 5.40F;
float f2 = 5.40f;
float f3 = 5.40e2f;
float f4 = 5.40e+12f;
float f5 = 5.40; // da eroare, trebuie cast
double d1 = 5.40; // implicit este double !
double d2 = 5.40d;
double d3 = 5.40D;
double d4 = 5.40e2;
double d5 = 5.40e+12d;
char c1 = r;
char c2 = \u4567;
}
}
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 33
Java deneste mai multe tipuri primitive de date. Fiecare tip are o anumit a
dimensiune, care este independenta de caracteristicile masinii gazda. Astfel, spre
deosebire de C/C++, unde unntreg poate reprezentat pe 16, 32 sau 64 de bit i, n
funct ie de arhitectura masinii, o valoare de tipntreg n Java va ocupa ntotdeauna
32 de bit i, indiferent de masina pe care ruleaz a. Aceast a consecvent a este esent ial a
deoarece o aceeasi aplicat ie va trebui s a ruleze pe masini cu arhitectur a pe 16, 32
sau 64 de bit i si sa produc a acelasi rezultat pe ecare masina n parte.
Tip Dimensiune Valoare Valoare Valoare Cifre
(octet i) minima maxima initiala semnicative
byte 1 2
7
2
7
1 0
short 2 2
15
2
15
1 0
int 4 2
31
2
31
1 0
long 8 2
63
2
63
1 0
oat 4 +-1.4E-45 +-3.4E+38 0 6-7
double 8 +-4.94E-324 +-1.79E+308 0 14-15
boolean 1 false
char 2 null
Tabelul 3.1: Tipurile primitive de date n Java
Variabilele pot init ializate la declararea lor sau n momentul utiliz arii lor
efective. Daca valoarea nu este specicata explicit atunci variabila se init ializeaza
cu o valoare init ial a implicit a. Tabelul anterior prezint a cateva exemple n acest
sens.
Conversiile ntre diferitele tipuri sunt permise (acolo unde au semnicat ie).
Se vede din tabel c a unele tipuri de variabile au posibilitatea s a reprezinte un
spectru mai mare de numere dec at altele.
In momentul execut iei, atat timp cat condit ia este adevarat a, se va executa
prelucrarea. Daca condit ia nu este la nceput satisf acut a, atunci prelucrarea nu se
efectueaza niciodata.
Prelucrarea repetitiv a cu test nal se descrie prin:
36 CAPITOLUL 3. ALGORITMI
do <instr> while (<condit ie>);
Instruct iunea se repet a p an a cand condit ia specicata devine fals a.
In acest
caz prelucrarea se efectueaza cel put in o dat a, chiar dac a condit ia nu este satisfa-
cuta la nceput.
Prelucrarea repetitiv a cu contor se caracterizeaza prin repetarea prelucr arii
de un num ar prestabilit de ori si este descrisa prin:
for(<instr1> ; <conditie>; <instr2>) <instr3>;
...
/* prelucrari specice subprogramului */
...
return <nume rezultat>;
k=0
1
2
k
C
n
n+k
net in and cont de faptul c a f(n) are o expresie mult mai simpl a, si anume 2
n
. Suma
trebuie calculata simul and operat iile de adunare, nmult ire si mp art ire la 2, cu
numere mari.
Rezolvare: Funct ia se pune sub forma:
f(n) =
1
2
n
n
k=0
2
nk
C
n
n+k
Se calculeaza suma, si apoi se fac n mp art iri succesive la 2.
class e05
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 45
{
public static void main (String[]args)
{
int n, k;
int[] s;
int[] p;
for(n=10;n<=12;n++)
{
s=nrv(0);
for(k=0;k<=n;k++)
{
p=inm(comb(n+k,n),putere(2,n-k));
s=suma(s,p);
}
afisv(s);
for(k=1;k<=n;k++) s=impartLa2(s);
System.out.print(n+" : ");
afisv(s);
fcifre(s);
}
System.out.println("GATA");
}//main()
static int[] impartLa2(int[] a)
{
int na,nb,k,t=0;
na=a.length-1;
if(a[na]==1) nb=na-1; else nb=na;
int[] b=new int[nb+1];
if(na==nb)
for(k=na;k>=0;k--) {a[k]+=10*t; b[k]=a[k]/2; t=a[k]%2;}
else
{
t=a[na];
for(k=na-1;k>=0;k--){a[k]+=10*t; b[k]=a[k]/2; t=a[k]%2;}
}
return b;
}
static void fcifre(int[] x)
{
int i;
int[] f=new int[10];
for(i=0;i<x.length;i++) f[x[i]]++;
46 CAPITOLUL 3. ALGORITMI
System.out.println();
for(i=0;i<=9;i++) System.out.println(i+" : "+f[i]);
System.out.println();
}
static int[] suma(int[] x, int[] y)
{
int i, j, t, ncx=x.length, ncy=y.length, ncz;
if(ncx>ncy) ncz=ncx+1; else ncz=ncy+1;
int[] xx=new int[ncz];
int[] yy=new int[ncz];
int[] z=new int[ncz];
for(i=0;i<ncx;i++) xx[i]=x[i];
for(j=0;j<ncy;j++) yy[j]=y[j];
t=0;
for(i=0;i<ncz;i++){z[i]=xx[i]+yy[i]+t; t=z[i]/10; z[i]=z[i]%10;}
if(z[ncz-1]!= 0) return z;
else
{
int[]zz=new int[ncz-1];
for(i=0;i<=ncz-2;i++) zz[i]=z[i];
return zz;
}
}
static int[] inm(int[]x,int[]y)
{
int t, n=x.length, m=y.length, i, j;
int[][]a=new int[m][n+m];
int[]z=new int[m+n];
for(j=0;j<m;j++)
{
t=0;
for(i=0;i<n;i++)
{
a[j][i+j]=y[j]*x[i]+t;
t=a[j][i+j]/10;
a[j][i+j]=a[j][i+j]%10;
}
a[j][i+j]=t;
}
t=0;
for(j=0;j<m+n;j++)
{
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 47
z[j]=0;
for(i=0;i<m;i++) z[j]=z[j]+a[i][j];
z[j]=z[j]+t;
t=z[j]/10;
z[j]=z[j]%10;
}
if(z[m+n-1]!= 0) return z;
else
{
int[]zz=new int[m+n-1];
for(i=0;i<=m+n-2;i++)
zz[i]=z[i];
return zz;
}
}
static void afisv(int[]x)
{
int i;
for(i=x.length-1;i>=0;i--) System.out.print(x[i]);
System.out.print(" *** "+x.length);
System.out.println();
}
static int[] nrv(int nr)
{
int nrrez=nr, nc=0;
while(nr!=0) {nc++; nr=nr/10;}
int[]x=new int [nc];
nr=nrrez;
nc=0;
while(nr!=0){x[nc]=nr%10; nc++; nr=nr/10;}
return x;
}
static int[] putere (int a, int n)
{
int[] rez;
int k;
rez=nrv(1);
for(k=1;k<=n;k++) rez=inm(rez,nrv(a));
return rez;
}
48 CAPITOLUL 3. ALGORITMI
static int[] comb (int n, int k)
{
int[] rez;
int i, j, d;
int[]x=new int[k+1];
int[]y=new int[k+1];
for(i=1;i<=k;i++) x[i]=n-k+i;
for(j=1;j<=k;j++) y[j]=j;
for(j=2;j<=k;j++)
{
for(i=1;i<=k;i++)
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
}
rez=nrv(1);
for(i=1;i<=k;i++) rez=inm(rez,nrv(x[i]));
return rez;
}
static int cmmdc (int a,int b)
{
int d,i,c,r;
if (a>b) {d=a;i=b;} else{d=b;i=a;}
while (i!=0){c=d/i; r=d%i; d=i; i=r;}
return d;
}
}// class
6. S a se aseze S(n, 1), S(n, 2), ..., S(n, m) (inclusiv suma cifrelor si num arul
cifrelor pentru ecare num a) stiind c a
S(n + 1, m) = S(n, m1) +mS(n, m)
si
S(n, 1) = S(n, n) = 1, n m.
Se vor implementa operat iile cu numere mari.
Rezolvare: Matricea de calcul este subdiagonal a. Se completeaz a cu 1 prima
coloan a si diagonala principal a, iar apoi se determin a celelalte elemente ale matricei
folosind relat ia dat a (aranjat a put in altfel!). Matricea de calcul va avea de fapt trei
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 49
dimensiuni (numerele devin foarte mari, asa ca elementul S
i,j
trebuie sa cont in a
vectorul cifrelor valorii sale).
class e06
{
public static void main(String[] args)
{
int n=50, m=40, i, j;
int[][][] s=new int[n+1][m+1][1];
for(i=1;i<=n;i++)
{
if(i<=m) s[i][i]=nr2v(1);
s[i][1]=nr2v(1);
for(j=2;j<=min(i,m);j++)
s[i][j]=suma(s[i-1][j-1],inm(nr2v(j),s[i-1][j]));
if(i<=m) s[i][i]=nr2v(1);
}
for(i=1;i<=m;i++)
{
System.out.print("\n"+i+" : "+s[n][i].length+" ");
afissumac(s[n][i]);
afisv(s[n][i]);
}
}
static int[] suma(int[] x,int[] y){...}
static int[] nr2v(int nr){...}
static int[] inm(int[]x, int[]y){...}
static void afisv(int[]x){...}
static void afissumac(int[]x)
{
int i,s=0;
for(i=x.length-1;i>=0;i--) s+=x[i];
System.out.print(s+" ");
}
static int min(int a, int b) { return (a<b)?a:b; }
}// class
Pe ecran apar urm atoarele valori (numerele devin foarte mari!):
1 : 1 1 1
2 : 15 64 562949953421311
50 CAPITOLUL 3. ALGORITMI
3 : 24 102 119649664052358811373730
4 : 29 138 52818655359845224561907882505
5 : 33 150 740095864368253016271188139587625
6 : 37 170 1121872763094011987454778237712816687
7 : 39 172 355716059292752464797065038013137686280
8 : 41 163 35041731132610098771332691525663865902850
9 : 43 189 1385022509795956184601907089700730509680195
10 : 44 205 26154716515862881292012777396577993781727011
11 : 45 177 267235754090021618651175277046931371050194780
12 : 46 205 1619330944936279779154381745816428036441286410
13 : 46 232 6238901276275784811492861794826737563889288230
14 : 47 205 16132809270066494376125322988035691981158490930
15 : 47 162 29226457001965139089793853213126510270024300000
16 : 47 216 38400825365495544823847807988536071815780050940
17 : 47 198 37645241791600906804871080818625037726247519045
18 : 47 225 28189332813493454141899976735501798322277536165
19 : 47 165 16443993651925074352512402220900950019217097000
20 : 46 237 7597921606860986900454469394099277146998755300
21 : 46 198 2820255028563506149657952954637813048172723380
22 : 45 189 851221883077356634241622276646259170751626380
23 : 45 198 211092494149947371195608696099645107168146400
24 : 44 192 43397743800247894833556570977432285162431400
25 : 43 168 7453802153273200083379626234837625465912500
26 : 43 186 1076689601597672801650712654209772574328212
27 : 42 189 131546627365808405813814858256465369456080
28 : 41 155 13660054661277961013613328658015172843800
29 : 40 165 1210546686654900169010588840430963387720
30 : 38 185 91860943867630642501164254978867961752
31 : 37 155 5985123385551625085090007793831362560
32 : 36 164 335506079163614744581488648870187520
33 : 35 153 16204251384884158932677856617905110
34 : 33 144 674833416425711522482381379544960
35 : 32 126 24235536318546124501501767693750
36 : 30 135 750135688292101886770568010795
37 : 29 141 19983209983507514547524896035
38 : 27 132 457149347489175573737344245
39 : 25 114 8951779743496412314947000
40 : 24 93 149377949042637543000150
7. S a se aseze B
1
, B
2
, ..., B
n
stiind c a
B
n+1
=
n
k=0
C
k
n
B
k
, B
0
= 1.
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 51
Se vor implementa operat iile cu numere mari.
Rezolvare: Vectorul de calcul va avea de fapt dou a dimensiuni (numerele devin
foarte mari, asa ca elementul B
i
trebuie sa cont in a vectorul cifrelor valorii sale).
class e07
{
public static void main(String[] args)
{
int n=71; // n=25 ultimul care incape pe long
int k,i;
int[][] b=new int[n+1][1];
int[] prod={1};
b[0]=nr2v(1);
for(i=1;i<=n;i++)
{
b[i]=nr2v(0);;
for(k=0;k<=i-1;k++)
{
prod=inm(comb(i-1,k),b[k]);
b[i]=suma(b[i],prod);
}
System.out.print(i+" : ");
afisv(b[i]);
}
System.out.println(" "+Long.MAX_VALUE);
System.out.println("... Gata ...");
}
static int[] suma(int[] x,int[] y){...}
static int[] nr2v(int nr){...}
static int[] inm(int[]x, int[]y){...}
static void afisv(int[]x){...}
static int[] comb(int n,int k)
{
int i,j,d;
int[] rez;
int[] x=new int[k+1];
int[] y=new int[k+1];
for(i=1;i<=k;i++) x[i]=n-k+i;
for(j=1;j<=k;j++) y[j]=j;
for(j=2;j<=k;j++)
for(i=1;i<=k;i++)
52 CAPITOLUL 3. ALGORITMI
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
rez=nr2v(1);
for(i=1;i<=k;i++) rez=inm(rez,nr2v(x[i]));
return rez;
}
static int cmmdc(int a,int b) {...}
}
3.5.8 Probleme propuse
1. Fie S
n
= x
n
1
+x
n
2
+x
n
3
unde x
1
, x
2
si x
3
sunt r ad acinile ecuat iei cu coecient i
ntregi ax
3
+bx
2
+cx+d = 0 (vom considera a = 1!). S a se aseze primii 10 termeni
ai sirului S
n
si sa se precizeze n dreptul ec arui termen dac a este numar prim, iar
daca nu este num ar prim s a se aseze descompunerea n factori.
2. S a se aseze frecvent a cifrelor care apar n
f(n) =
n1
k=0
C
k
n1
n
n1k
(k + 1)!
net in and cont de faptul c a f(n) are o expresie mult mai simpl a, si anume n
n
. Suma
trebuie calculata simul and operat iile cu numere mari.
3. S a se aseze frecvent a cifrelor care apar n
f(n) = n
n1
+
n1
k=1
C
k
n
k
k1
(n k)
nk
net in and cont de faptul c a f(n) are o expresie mult mai simpl a, si anume n
n
. Suma
trebuie calculata simul and operat iile cu numere mari.
4. S a se calculeze
f(n) = n
_
1
1
p
1
__
1
1
p
2
_
...
_
1
1
p
m
_
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 53
unde n = p
i1
1
p
i2
2
...p
im
m
reprezint a descompunerea n factori primi a lui n.
5. S a se calculeze
(n) = card k N/1 k n, cmmdc(k, n) = 1 .
6. S a se calculeze
f(n) =
d|n
(n)
unde este funct ia de la exercit iul anterior, net in and cont de faptul c a f(n) are o
expresie mult mai simpla, si anume n.
7. S a se calculeze
f(n) = n!
_
1
1
1!
+
1
2!
... +
(1)
n
n!
_
.
8. S a se calculeze
f(m, n,
1
,
2
, ...,
n
) =
m
k=1
(1)
mk
C
k
m
_
C
1
k
_
1
_
C
2
k+1
_
2
...
_
C
n
k+n1
_
n
.
9. S a se calculeze
g(m, n,
1
,
2
, ...,
n
) =
_
C
1
m
_
1
_
C
2
m+1
_
2
...
_
C
n
m+n1
_
n
implementand operat iile cu numere mari.
10. S a se calculeze
f(n) =
1
2
n
_
(2n)! C
1
n
2(2n 1)! +C
2
n
2
2
(2n 2)! ... + (1)
n
2
n
n!
_
.
11. S a se calculeze
C
n
=
1
n + 1
C
n
2n
implementand operat iile cu numere mari.
12. S a se aseze P(100, 50) (inclusiv suma cifrelor si num arul cifrelor) stiind
ca
P(n +k, k) = P(n, 1) +P(n, 2) +... +P(n, k)
54 CAPITOLUL 3. ALGORITMI
si
P(n, 1) = P(n, n) = 1, n k 1.
Se vor implementa operat iile cu numere mari.
13. S a se determine cel mai mic numar natural r, astfel nc at p
r
= e, unde p
este o permutare data si e este permutarea identic a.
14. S a se aseze C
100
stiind c a
C
n
=
n
k=1
C
k1
C
nk
, C
0
= 1.
Se vor implementa operat iile cu numere mari.
15. S a se aseze E
100
stiind c a
En = E
2
E
n1
+E
3
E
n2
+... + E
n1
E
2
, E
1
= E
2
= 1.
Se vor implementa operat iile cu numere mari.
16. S a se calculeze
S(n, m) =
1
m!
m1
k=0
(1)
k
C
k
m
(mk)
n
17. S a se aseze C
100
stiind c a
C
n
=
n
k=1
C
k
n
F
k
.
unde F
k
este termen Fibonacci. Se vor implementa operat iile cu numere mari.
18. S a se aseze C
100
stiind c a
C
n
=
n
k=1
C
k
n
2
k
F
k
.
unde F
k
este termen Fibonacci. Se vor implementa operat iile cu numere mari.
19. S a se determine puterea a zecea a unui polinom dat.
Capitolul 4
Analiza complexit at ii
algoritmilor
4.1 Scopul analizei complexitat ii
In general exist a mai multi algoritmi care rezolv a aceeasi problem a. Dorim
sa exprim am ecient a algoritmilor sub forma unui criteriu care sa ne permit a sa
alegem din mai mult i algoritmi pe cel optim. Exist a mai multe moduri n care
putem exprima ecient a: prin timpul necesar pentru execut ia algoritmului sau
prin alte resurse necesare (de exemplu memoria).
In ambele cazuri ns a, avem o
dependent a de dimensiunea cazului studiat.
Se pune problema de alegere a unei unit at i de masur a pentru a exprima
ecient a teoretica a unui algoritm. O important a deosebita n rezolvarea acestei
probleme o are principiul invariant ei. Acesta ne arat a ca nu este necesar s a folosim
o astfel de unitate.
Principiul invariant ei: dou a implement ari diferite ale aceluiasi algoritm
nu difer a n ecient a cu mai mult de o constant a multiplicativa.
Implementarea unui algoritm presupune elementele legate de calculatorul
folosit, de limbajul de programare si ndemanarea programatorului (cu condit ia ca
acesta sa nu modice algoritmul). Datorit a principiului invariant ei vom exprima
ecient a unui algoritmn limitele unei constante multiplicative.
Un algoritm este compus din mai multe instruct iuni, care la r andul lor sunt
compuse din mai multe operat ii elementare. Datorit a principiului invariant ei nu
ne intereseaza timpul de execut ie a unei operat ii elementare, ci numai numarul lor,
dar ne intereseaza care si ce sunt operat iile elementare.
Denit ia 1 O operat ie elementar a este o operat ie al c arui timp de execut ie poate
m arginit superior de o constant a care depinde numai de particularitatea imple-
mentarii (calculator, limbaj de programare etc).
55
56 CAPITOLUL 4. ANALIZA COMPLEXIT
ATII ALGORITMILOR
Deoarece ne intereseaza timpul de executie n limita unei constante multi-
plicative, vom considera doar num arul operat iilor elementare executate ntr-un
algoritm, nu si timpul exact de execut ie al operat iilor respective.
Este foarte important ce anume denim ca operat ie elementar a. Este adunarea
o operat ie elementara? Teoretic nu este, pentru ca depinde de lungimea celor doi
operanzi. Practic, pentru operanzi de lungime rezonabil a putem s a consider am ca
adunarea este o operat ie elementar a. Vom considera n continuare c a adun arile,
scaderile, nmult irile, mp art irile, operat iile modulo (restul mp aart irii ntregi),
operat iile booleene, comparat iile si atribuirile sunt operat ii elementare.
Uneori ecient a difer a daca t inem cont numai de unele operat ii elementare si
le ignor am pe celelalte (de exemplu la sortare: comparat ia si interschimbarea). De
aceea n analiza unor algoritmi vom considera o anumit a operat ie elementara, care
este caracteristica algoritmului, ca operat ie barometru, neglij andu-le pe celelalte.
De multe ori, timpul de executie al unui algoritm poate varia pentru cazuri
de marime identic a. De exemplu la sortare, dac a introducem un sir de n numere
gata sortat, timpul necesar va cel mai mic dintre timpii necesari pentru sortarea
oricarui alt sir format din n numere. Spunem c a avem de-a face cu cazul cel mai
favorabil. Daca sirul este introdus n ordine invers a, avem cazul cel mai defavorabil
si timpul va cel mai mare dintre timpii de sortare a sirului de n numere.
Exist a algoritmi n care timpul de execut ie nu depinde de cazul considerat.
Daca dimensiunea problemei este mare, mbun at at irea ordinului algoritmului
este esent iala, n timp ce pentru timpi mici este sufcient a performant a hardware.
Elaborarea unor algoritmi ecient i presupune cunostint e din diverse domenii
(informatic a, matematica si cunostiint e din domeniul c aruia i apart ine problema
practic a a carui model este studiat, atunci cand este cazul).
Exemplul 1 Elaborat i un algoritm care returneaz a cel mai mare divizor comun
(cmmdc) a doi termeni de rang oarecare din sirul lui Fibonacci.
Sirul lui Fibonacci, f
n
= f
n1
+ f
n2
, este un exemplu de recursivitate n
cascada si calcularea efectiv a a celor doi termeni f
m
f
n
, urmat a de calculul celui
mai mare divizor al lor, este total neindicata. Un algoritm mai bun poate obt inut
daca t inem seama de rezultatul descoperit de Lucas n 1876:
cmmdc(f
m
, f
n
) = f
cmmdc(m,n)
Deci putem rezolva problema calculand un singur termen al sirului lui Fibonacci.
Exist a mai mult i algoritmi de rezolvare a unei probleme date. Prin urmare,
se impune o analiz a a acestora, n scopul determinarii ecient ei algoritmilor de
rezolvare a problemei si pe cat posibil a optimalit at ii lor. Criteriile n funct ie
de care vom stabili ecient a unui algoritm sunt complexitatea spat iu (memorie
utilizat a) si complexitatea timp (numarul de operat iilor elementare).
4.1. SCOPUL ANALIZEI COMPLEXIT
ATII 57
4.1.1 Complexitatea spat iu
Prin complexitate spat iu nt elegem dimensiunea spat iului de memorie utilizat
de program.
Un program necesita un spat iu de memorie constant, independent de datele
de intrare, pentru memorarea codului, a constantelor, a variabilelor si a structurilor
de date de dimensiune constanta alocate static si un spat iu de memorie variabil,
a carui dimensiune depinde de datele de intrare, const and din spat iul necesar
pentru structurile de date alocate dinamic, a c aror dimensiune depinde de instant a
problemei de rezolvat si din spat iul de memorie necesar apelurilor de proceduri si
funct ii.
Progresele tehnologice fac ca important a criteriului spat iu de memorie utilizat
sa scada, prioritar devenind criteriul timp.
4.1.2 Complexitatea timp
Prin complexitate timp nt elegem timpul necesar execut iei programului.
ATII ALGORITMILOR
4.2 Notat ia asimptotica
4.2.1 Denire si proprietat i
Denit ia 2 Numim ordinul lui f, mult imea de funct ii
O(f) = t : N R
+
[c > 0, n
0
N a.. t(n) cf(n), n > n
0
(4.2.1)
Rezulta ca O(f) este mult imea tuturor funct iilor m arginite superior de un
multiplu real pozitiv al lui f, pentru valori sucient de mari ale argumentului.
Daca t(n) O(f) vom spune ca t este de ordinul lui f sau n ordinul lui f.
Fie un algoritm dat si o funct ie t : N R
+
, astfel nc at o anumit a imple-
mentare a algoritmului s a necesite cel mult t(n) unit at i de timp pentru a rezolva
un caz de marime n.
Principiul invariant ei ne asigura ca orice implementare a algoritmului necesit a
un timp n ordinul lui t. Mai mult, acest algoritm necesit a un timp n ordinul lui
f pentru orice functie f : N R
+
pentru care t O(f).
In particular t O(t).
Vom cauta s a g asim cea mai simpla funct ie astfel nc at t O(f).
Pentru calculul ordinului unei funct ii sunt utile urm atoarele proprietat i:
Proprietatea 1 O(f) = O(g) f O(g) si g O(f)
Proprietatea 2 O(f) O(g) f O(g) si g / O(f)
Proprietatea 3 O(f +g) = O(max(f, g))
Pentru calculul mult imilor O(f) si O(g) este util a proprietatea urmatoare:
Proprietatea 4 Fie f, g : N R
+
. Atunci
lim
n
f(n)
g(n)
R
+
O(f) = O(g)
lim
n
f(n)
g(n)
= 0 O(f) O(g).
Reciproca nu este n general valabil a.
Fie de exemplu, t(n) = n
2
+ 3n + 2, atunci
lim
n
n
2
+ 3n + 2
n
2
= 1 = O(n
2
+ 3n + 2) = O(n
2
)
lim
n
n
2
+ 3n + 2
n
3
= 0 = O(n
2
+ 3n + 2) O(n
3
)
4.2. NOTAT IA ASIMPTOTIC
A 59
lim
n
ln(n)
n
= lim
n
1
n
1
2
n
= lim
n
2
n
= 0 = O(ln(n)) O(
n)
dar O(
n) , O(ln(n)
Daca p este un polinom de gradul mn variabila n, atunci O(p) = O(n
m
).
Notat ia asimptotic a deneste o relat ie de ordine part ial a ntre funct ii.
Pentru f, g : N R
+
o funct ie arbitrar a. Spunem c a algoritmul este de ordinul
lui f(n) (si not am T
A
(n) O(f(n))), dac a si numai dac a exista c > 0 si n
0
N,
astfel nc at T
A
(n) c f(n), n n
0
.
De exemplu:
a) Daca T
A
(n) = 3n+2, atunci T
A
(n) O(n), pentru c a 3n+2 4n, n 2.
Mai general, dac a T
A
(n) = a n + b, a > 0, atunci T
A
(n) O(n) pentru c a
exista c = a + 1 > 0 si n
0
= b N, astfel nc at a n +b (a + 1) n, n b.
b) Dac a T
A
(n) = 10n
2
+ 4n + 2, atunci T
A
(n) O(n
2
), pentru c a 10n
2
+
4n + 2 11n
2
, n 5.
Mai general, dac a TA(n) = an
2
+ bn + c, a > 0, atunci TA(n) O(n
2
),
pentru c a an
2
+bn +c (a + 1)n
2
, n max(b, c) + 1.
c) Daca T
A
(n) = 6 2
n
+ n
2
, atunci TA(n) O(2
n
), pentru c a T
A
(n)
7 2
n
,n 4.
Daca T
A
(n) = a
k
n
k
+ a
k1
n
k1
+ ... + a
1
n + a
0
, atunci TA(n) O(n
k
).
Aceasta rezulta din: T
A
(n) = [T
A
(n)[ = [a
k
n
k
+ a
k1
n
k1
+ ... + a
1
n + a
0
[
[a
k
[n
k
+[a
k1
[n
k1
+... +[a
1
[n+[a
0
[ ([a
k
[ +[a
k1
[ +... +[a
1
[ +[a
0
[)n
k
, n 1
si alegand c = [a
k
[ +[a
k1
[ +... +[a
1
[ +[a
0
[ si n = 1 rezult a T
A
(n) O(n
k
).
60 CAPITOLUL 4. ANALIZA COMPLEXIT
ATII ALGORITMILOR
4.2.2 Clase de complexitate
Notat ia O ofer a o limit a superioar a a timpului de execut ie a unui algoritm.
Un algoritm cu T
A
(n) O(1) necesita un timp de execut ie constant. Un
algoritm cu T
A
(n) O(n) se numeste liniar. Daca T
A
(n) O(n
2
) algoritmul se
numeste patratic, iar dac a T
A
(n) O(n
3
), cubic. Un algoritm cu T
A
(n) O(n
k
)
se numeste polinomial, iar dac a T
A
(n) O(2
n
) algoritmul se numeste exponent ial.
Tabelul urm ator ilustreaz a comportarea a cinci din cele mai importante
funct ii de complexitate.
O(log(n)) O(n) O(n.log(n)) O(n
2
) O(n
3
) O(2
n
)
(logaritmic) (liniar) (log-liniar) (p atratic) cubic (exponent ial)
0 1 0 1 1 2
1 2 2 4 8 4
2 4 8 16 64 16
3 8 24 64 512 256
4 16 64 256 4096 65536
5 32 160 1024 32768 4294967296
Tabelul 4.1: Funct ii de complexitate
Daca T
A
(n) O(2
n
), pentru n = 40, pe un calculator care face 10
9
de operat ii
pe secunda, sunt necesare aproximativ 18 minute. Pentru n = 50, acelasi program
va rula 13 zile pe acest calculator, pentru n = 60, vor necesari peste 310 ani, iar
pentru n = 100 aproximativ 4.10
13
ani.
Utilitatea algoritmilor polinomiali de grad mare este de asemenea limitat a.
De exemplu, pentru O(n
10
), pe un calculator care executa 10
9
operat ii pe secunda
sunt necesare 10 secunde pentru n = 10, aproximativ 3 ani pentru n = 100 si circa
3.10
13
ani pentru n = 1000.
Uneori este util s a determinam si o limit a inferioar a pentru timpul de execut ie
a unui algoritm. Notat ia matematica este .
Denit ie: Spunem c a T
A
(n) (f(n)) dac a si numai dac a c > 0 si n
0
N
astfel nc at T
A
(n) c f(n), n n
0
.
De exemplu:
a) dac a T
A
(n) = 3n+2, atunci T
A
(n) (n), pentru c a 3n+2 3n, n 1;
b) dac a T
A
(n) = 10n
2
+4n+2, atunci T
A
(n) (n), pentru c a 10n
2
+4n+2
n2, n 1;
c) daca T
A
(n) = 6 2
n
+n
2
, atunci T
A
(n) (2
n
), pentru c a 6 2
n
+n
2
2
n
,
n 1.
Exist a funct ii f care constituie at at o limit a superioar a cat si o limit a infe-
rioar a a timpului de execut ie a algoritmului. De exemplu, daca T
A
(n) = a
k
n
k
+
a
k1
n
k1
+... + a
1
n +a
0
, a
k
> 0 atunci T
A
(n) (n
k
).
Denit ie : Spunem c a T
A
(n) (f(n)) dac a si numai dac a c
1
, c
2
> 0 si
n
0
N astfel nc at c
1
f(n) T
A
(n) c
2
f(n), n n
0
.
4.2. NOTAT IA ASIMPTOTIC
A 61
dD
p(d) T
A
(d).
62 CAPITOLUL 4. ANALIZA COMPLEXIT
ATII ALGORITMILOR
4.2.4 Analiza asimptotica a structurilor fundamentale
Consider am problema determin arii ordinului de complexitate n cazul cel mai
defavorabil pentru structurile algoritmice: secvent iala, alternativ a si repetitiv a.
Presupunem c a structura secvent ial a este constituit a din prelucr arile A
1
, A
2
,
..., A
k
si ecare dintre acestea are ordinul de complexitate O(g
i
(n)), 1 i n.
Atunci structura va avea ordinul de complexitate O(maxg
1
(n), ..., g
k
(n)).
Daca condit ia unei structuri alternative are cost constant iar prelucr arile
celor doua variante au ordinele de complexitate O(g
1
(n)) respectiv O(g
2
(n)) atunci
costul structurii alternative va O(maxg
1
(n), g
2
(n)).
k=1
1
i
(k 1) =
1
i
i
k=1
(k 1) =
1
i
_
i(i + 1)
2
i
_
=
i + 1
2
1 =
i 1
2
64 CAPITOLUL 4. ANALIZA COMPLEXIT
ATII ALGORITMILOR
Pentru a sorta cele n elemente sunt necesare
n
i=2
i 1
2
=
1
2
_
n(n + 1)
2
1 (n 1)
_
=
n
2
_
(n + 1)
2
1
_
=
n(n 1)
4
operat ii elementare. Deci complexitatea algoritmului n medie este tot O(n
2
).
4.3.4 Sortarea rapida (quicksort)
Acest algoritm a fost elaborat de C.A.R. Hoare n 1960 si este unul dintre cei
mai utilizat i algoritmi de sortare.
void quicksort(int st, int dr)
{
int m;
if st<dr
{
m=divide(st, dr);
quicksort(st, m-1);
quicksort(m+1, dr);
}
}
Init ial apel am quicksort(1,n).
Funct ia divide are rolul de aplasa primul element (a[st]) pe pozit ia sa corecta
n sirul ordonat.
In st anga sa se vor gasi numai elemente mai mici, iar n dreapta
numai elemente mai mari decat el.
int divide(int st, int dr)
{
int i, j, val;
val=a[st];
i=st; j=dr;
while(i<j)
{
while((i<j) && (a[j] >= val)) j=j-1;
a[i]=a[j];
while((i<j) && (a[i] <= val)) i=i+1;
a[j]=a[i];
}
a[i]=val;
return i;
}
Observat ie : Vectorul a este considerat variabila global a.
4.3. EXEMPLE 65
In cazul cel mai defavorabil, cand vectorul a era init ial ordonat, se fac n 1
apeluri succesive ale procedurii quicksort, cu parametrii (1, n), (1, n1), ..., (1, 2)
(dac a vectorul a era init ial ordonat descrescator) sau (1, n), (2, n), ..., (n 1, n)
(dac a vectorul a era ordonat cresc ator).
La ecare apel al procedurii quicksort este apelata funct ia divide(1,i)
(respectiv divide(i, n)) care efectueaza i 1, (respectiv n i 1) operat ii
elementare.
n
k=1
(T
k1
+T
nk
) + (n 1), daca n > 1
(pentru a ordona crescator n elemente, determinam pozit ia k n vectorul ordonat a
primului element, ceea ce necesita n1 operat ii elementare, sortam elementele din
stanga, ceea ce necesit a T
k1
operat ii elementare, apoi cele din dreapta, necesit and
T
nk
operat ii elementare).
Problema se reduce la a rezolva relat ia de recurent a de mai sus. Mai nt ai
observ am ca
T
0
+T
1
+... +T
n1
= Tn 1 +... +T
1
+T
0
.
Deci,
T
n
= n 1 +
2
n
n
k=1
T
k1
k=1
T
k1
Scazand din aceast a relat ie, relat ia obt inut a pentru n 1, adic a
(n 1)T
n1
= (n 1)(n 2) + 2
n1
k=1
T
k1
obt inem
nT
n
(n 1)T
n1
= n(n 1) (n 1)(n 2) + 2T
n1
de unde rezult a
nT
n
= 2(n 1) + (n + 1)T
n1
66 CAPITOLUL 4. ANALIZA COMPLEXIT
ATII ALGORITMILOR
k=3
k 1
k(k + 1)
Deci
T
n
n + 1
=
T
2
3
+2
n
k=3
_
1
k + 1
1
k
+
1
k + 1
_
=
T
2
3
+
2
n + 1
+2
n
k=3
1
k
2
n
k=1
1
k
2 lnn
Deci, n medie, complexitatea algoritmului este de O(nlog n).
4.3.5 Problema celebritat ii
Numim celebritate o persoan a care este cunoscuta de toat a lumea, dar nu
cunoaste pe nimeni. Se pune problema de a identica o celebritate, daca exista,
ntr-un grup de n persoane pentru care relat iile dintre persoane sunt cunoscute.
Putem reformula problema n limbaj de grafuri astfel: ind dat un digraf cu
n v arfuri, vericat i daca exista un v arf cu gradul exterior 0 si gradul interior n1.
Reprezentam graful asociat problemei prin matricea de adiacent a a
nn
a
i,j
=
_
1, daca persoana i cunoaste persoana j;
0, altfel.
O prim a solut ie ar sa calculam pentru ecare persoan a p din grup num arul
de persoane pe care p le cunoaste (out) si num arul de persoane care cunosc per-
soana p (in). Cu alte cuvinte, pentru ecare v arf din digraf calcul am gradul interior
si gradul exterior. Dac a gasim o persoan a pentru care out = 0 si in = n1, aceasta
va celebritatea cautat a.
celebritate=0;
for p=1,2,...,n
{
in=0; out=0;
for j=1,2,...,n
{
in=in+a[j][p];
out=out+a[p][j];
}
if (in=n-1) and (out = 0) celebritate=p;
}
if celebritate=0 writeln(Nu exista celebritati !)
else writeln(p, este o celebritate.);
4.4. PROBLEME 67
Se poate observa cu usurint a ca algoritmul este de O(n
2
). Putemmbun at at i
algoritmul f acand observat ia ca atunci c and test am relat iile dintre persoanele x si
y apar urm atoarele posibilit at ii:
a[x, y] = 0 si n acest caz y nu are nici o sansa sa e celebritate, sau
a[x, y] = 1 si n acest caz x nu poate celebritate.
Deci la un test elimin am o persoan a care nu are sanse sa e celebritate.
F acand succesiv n 1 teste, n nal vom avea o singur a persoan a candidat
la celebritate. Ramane sa calculam num arul de persoane cunoscute si num arul de
persoane care l cunosc pe acest candidat, singura celebritate posibila.
candidat=1;
for i=2,n
if a[candidat][i]=1 candidat=i;
out=0;
in=0;
for i=1,n
{
in=in+a[i][candidat];
out=out+a[candidat][i];
}
if (out=0) and (in=n-1) write(candidat, este o celebritate .)
else write(Nu exista celebritati.);
, f O(n) = f
2
O(n
2
)
f ) f : N R
, f O(n) = 2
f
O(2
n
)
Rezolvare:
a) Armat ia este adevarata pentru c a: lim
n
n
2
n
3
= 0 = n
2
O(n
3
).
b) Armat ia este falsa pentru c a: lim
n
n
3
n
2
=
68 CAPITOLUL 4. ANALIZA COMPLEXIT
ATII ALGORITMILOR
c) Armat ia este adevarata pentru c a: lim
n
2
n+1
2
n
= 2 = O(2
n+1
) =
O(2
n
).
d) Armat ia este falsa pentru c a: lim
n
(n+1)!
n!
= lim
n
n+1
1
=
e) Armat ia este adevarata pentru c a: f O(n) = c > 0 si n
0
N
astfel nc at f(n) < c n, n > n
0
. Rezult a ca c
1
= c
2
astfel nc a f
2
(n) < c
1
n
2
,
n > n
0
, deci f
2
O(n
2
).
e) Armat ia este adevarata pentru c a: f O(n) = c > 0 si n
0
N astfel
nc at f(n) < c n, n > n
0
. Rezult a ca c
1
= 2
c
astfel nc a 2
f(n)
< 2
cn
= 2
c
2
n
=
c
1
2
n
, n > n
0
, deci 2
f
O(2
n
).
Problema 2 Aratat i c a log n O(
n) dar
n / O(log n).
Indicat ie: Prelungim domeniile funct iilor pe R
+
, pe care sunt derivabile, si aplic am
relula lui LH ospital pentru log n/
n.
Problema 3 Demonstrat i urm atoarele armat ii:
i) log
a
(log
b
n), pentru oricare a, b > 1
ii)
n
i=1
i
k
(n
k+1
), pentru oricare k N
iii)
n
i=1
1
i
(nlog n)
iv) log n! (nlog n)
Indicat ii: La punctul iii) se t ine cont de relat ia
i=1
1
i
+ ln n
unde 0.5772 este constanta lui Euler.
La punctul iv) din n! < n
n
, rezult a log n! < nlog n, deci log n! O(nlog n).
Trebuie sa g asim si o margine inferioar a. Pentru 0 i n 1 este adevarat a
relat ia
(n i)(i + 1) n
Deoarece
(n!)
2
= (n 1)((n 1) 2)((n 2) 3)...(2 (n 1))(1 n) n
n
rezult a 2 log n! nlog n, adic a log n! 0.5nlog n, deci log n! (nlog n).
Relat ia se poate demonstra si folosind aproximarea lui Stirling
n!
2n
_
n
e
_
n
(1 + (1/n))
4.4. PROBLEME 69
4.4.2 Probleme propuse
1. Ar atat i ca:
a) n
3
+ 10
6
n
(n
3
)
b) n
2
n
+ 6 2
n
()(n
2
n
)
c) 2n
2
+nlog n (n
2
)
d) n
k
+n + n
k
log n (n
k
log n), k 1
e) log
a
n (log
b
n), a, b > 0, a ,= 1, b ,= 1.
2. Pentru oricare doua functii f, g : N R
demonstrat i ca
O(f +g) = O(max(f, g)) (4.4.1)
unde suma si maximul se iau punctual.
3. Fie f, g : N R
+
Demonstrat i ca:
i) lim
n
f(n)
g(n)
R
+
O(f) = O(g), ii) lim
n
f(n)
g(n)
= 0 O(f) O(g)
Observat ie: Implicat iile inverse nu sunt n general adev arate, deoarece se
poate ntampla ca limitele s a nu existe.
4. Demonstrat i prin induct ie ca pentru a determina maximul a n numere sunt
necesare n 1 comparat ii.
5. Care este timpul de execut ie a algoritmului quicksort pentru un vector
cu n componente egale?
6. S a consideram urm atorul algoritm de sortare a unui vector a cu n compo-
nente:
do
{
ok=true;
for i=1,n-1
if a[i]>a[i+1] { aux=a[i]; a[i]=a[i+1]; a[i+1]= aux; }
ok=false;
} while !ok;
Analizat i algoritmul n medie si n cazul cel mai defavorabil.
7. Analizat i complexitatea algoritmului de interclasare a doi vectori ordonat i,
a cu n componente, respectiv b cu m componente :
i=1; j=1; k=0;
while (i <= n) and (j <= m)
{
k=k+1;
if a[i] < b[j] { c[k]=a[i]; i=i+1; }
else { c[k]=b[j]; j=j+1; }
70 CAPITOLUL 4. ANALIZA COMPLEXIT
ATII ALGORITMILOR
}
for t=i,n { k=k+1; c[k]=a[t]; }
for t=j,m { k=k+1; c[k]=b[t]; }
8. Fiind dat a, un vector cu n componente distincte, vericat i daca o valoare
dat a x se gaseste sau nu n vector. Evaluat i complexitatea algoritmului n cazul
cel mai defavorabil si n medie.
9. Se d a a un vector cu n componente. Scriet i un algoritm liniar care s a
determine cea mai lunga secvent a de elemente consecutive de valori egale.
10. Fie T un text. Vericat i n timp liniar dac a un text dat T
este o permutare
circular a a lui T.
11. Fie X = (x
1
, x
2
, ..., x
n
) o secvent a de numere ntregi. Fiind dat x, vom
numi multiplicitate a lui x n X num arul de aparit ii ale lui x n X. Un element se
numeste majoritar daca multiplicitatea sa este mai mare decat n/2. Descriet i un
algoritm liniar care s a determine elementul majoritar dintr-un sir, dac a un astfel
de element exista.
12. Fie a
1
, a
2
, ..., a
n
si b
1
, b
2
, ..., b
m
, dou a mult imi de numere ntregi,
nenule (m < n). S a se determine x
1
, x
2
, ..., x
m
, o submult ime a mult imii a
1
, a
2
, ..., a
n
n
= R
n
+1, obt inem ca R
n
= R
n1
+R
n2
pentru n > 1,
si R
1
= R
0
= 2. Rezult a R
n
= 2 fibo(n) si de aici obt inem ca R
n
= 2 fibo(n)1.
Num arul de apeluri recursive este foarte mare! Exista o metoda iterativ a simpl a
care permite calculul lui fibo(n) mult mai repede.
_
fibo(n)
fibo(n 1)
_
=
_
1 1
1 0
_
_
fibo(n 1)
fibo(n 2)
_
= ... =
_
1 1
1 0
_
n
_
1
0
_
_
u
v
_
=
_
1 1
1 0
_
_
u0
v0
_
static int fibo(int n) {
int u, v;
int u0, v0;
int i;
u = 1; v = 1;
for (i = 2; i <= n; ++i) {
u0 = u; v0 = v;
u = u0 + v0;
v = v0;
}
return u;
}
74 CAPITOLUL 5. RECURSIVITATE
Se poate calcula si mai repede folosind ultima form a si calcul and puterea
matricei ...
Pentru a rezuma, o regul a bun a este sa nu ncercam sa intr am n meandrele
detaliilor apelurilor recursive pentru a nt elege sensul unei funct ii recursive.
In
general este sufucient s a nt elegem sintetic funct ia. Funct ia lui Fibonacci este un
caz particular n care calculul recursiv este foarte lung. Cam la fel se nt ampl a
(dac a nu chiar mai r au!) si cu triunghiul lui Pascal. Dar nu aceasta este situat ia
n general. Nu numai c a scrierea recursiva se poate dovedi ecace, dar ea este
totdeauna natural a si deci cea mai estetic a. Ea nu face dec at sa respecte denit ia
matematica prin recurent a. Este o metoda de programare foarte puternica.
5.1.2 Funct ia lui Ackerman
Sirul lui Fibonacci are o crestere exponent ial a. Exist a funct ii recursive care au
o crestere mult mai rapid a. Prototipul este funct ia lui Ackerman.
In loc sa denim
matematic aceasta funct ie, este de asemenea simplu sa d am denit ia recursiva n
Java.
static int ack(int m, int n) {
if (m == 0)
return n+1;
else
if (n == 0)
return ack (m-1, 1);
else
return ack(m-1, ack(m, n-1));
}
Se poate verica ca ack(0, n) = n + 1, ack(1, n) = n + 2, ack(2, n) 2n,
ack(3, n) 2
n
, ack(5, 1) ack(4, 4) 2
65536
> 10
80
, adic a num arul atomilor din
univers [11].
5.1.3 Recursii imbricate
Funct ia lui Ackerman cont ine dou a apeluri recursive imbricate ceea ce deter-
min a o crestere rapid a. Un alt exemplu este funct ia 91 a lui MacCarty [11]:
static int f(int n) {
if (n > 100)
return n-10;
else
return f(f(n+11));
}
5.2. PROCEDURI RECURSIVE 75
Pentru aceast a funct ie, calculul lui f(96) d a
f(96) = f(f(107)) = f(97) = ... = f(100) = f(f(111)) = f(101) = 91.
Se poate ar ata ca aceasta funct ie va returna 91 dac a n 100 si n 10 daca
n > 100. Aceasta funct ie anecdotica, care foloseste recursivitatea imbricata, este
interesant a pentru c nu este evident ca o astfel de denit ie da d a acelasi rezultat.
Un alt exemplu este funct ia lui Morris [11] care are urm atoarea forma:
static int g(int m, int n) {
if (m == 0)
return 1;
else
return g(m-1, g(m, n));
}
Ce valoare are g(1, 0)? Efectul acestui apel de funct ie se poate observa din
denit ia ei: g(1, 0) = g(0, g(1, 0)). Se declanseaza la nesf arsit apelul g(1, 0). Deci,
calculul nu se va termina niciodat a!
5.2 Proceduri recursive
Procedurile, la fel ca si funct iile, pot recursive si pot suporta apeluri recur-
sive. Exemplul clasic este cel al turnurilor din Hanoi. Pe 3 tije din fat a noastra,
numerotate 1, 2 si 3 de la stanga la dreapta, sunt n discuri de dimensiuni diferite
plasate pe tija 1 form and un con cu discul cel mai mare la baz a si cel mai mic n
v arf. Se doreste mutarea discurilor pe tija 3, mut and numai c ate un singur disc si
neplas and niciodat a un disc mai mare peste unul mai mic. Un rat ionament recur-
siv permite scrierea solut iei n c ateva r anduri. Dac a n 1, problema este triviala.
Presupunem problema rezolvat a pentru mutarea a n 1 discuri de pe tija i pe
tija j (1 i, j 3). Atunci, exista o solut ie foarte simpla pentru mutarea celor n
discuri de pe tija i pe tija j:
1. se muta primele n1 discuri (cele mai mici) de pe tija i pe tija k = 6ij,
2. se muta cel mai mare disc de pe tija i pe tija j,
3. se muta cele n 1 discuri de pe tija k pe tija j.
static void hanoi(int n, int i, int j) {
if (n > 0) {
hanoi (n-1, i, 6-(i+j));
System.out.println (i + " -> " + j);
hanoi (n-1, 6-(i+j), j);
}
}
76 CAPITOLUL 5. RECURSIVITATE
Aceste cateva linii de program arat a foarte bine cum generalizand problema,
adic a mutarea de pe oricare tija i pe oricare tij aj, un program recursiv de cateva
linii poate rezolva o problem a apriori complicat a. Aceasta este fort a recursivit at ii
si a rat ionamentului prin recurent a.
1 2 3 1 2 3
1 2 3 1 2 3
a) b)
c) d)
pasul 1
pasul 2
pasul 3
Capitolul 6
Analiza algoritmilor
recursivi
Am vazut n capitolul precedent cat de puternic a si util a este recursivitatea
n elaborarea unui algoritm. Cel mai important c astig al exprimarii recursive este
faptul c a ea este naturala si compacta.
Pe de alt a parte, apelurile recursive trebuie folosite cu discern amant, deoarece
solicit a si ele resursele calculatorului (timp si memorie).
Analiza unui algoritm recursiv implic a rezolvarea unui sistem de recurent e.
Vom vedea n continuare cum pot rezolvate astfel de recurent e.
6.1 Relat ii de recurent a
O ecuat ie n care necunoscutele sunt termenii x
n
, x
n+1
, ...x
n+k
ai unui sir
de numere se numeste relat ie de recurent a de ordinul k. Aceasta ecuat ie poate
satisf acut a de o innitate de siruri. Ca s a putem rezolva ecuat ia (relat ia de
recurent a) mai avem nevoie si de condit ii init iale, adic a de valorile termenilor
x
0
, x
1
, ..., x
k1
. De exemplu relat ia de recurent a
(n + 2)C
n+1
= (4n + 2)C
n
, pentru n 0, C
0
= 1
este de ordinul 1.
Daca un sir x
n
de numere satisface o formul a de forma
a
0
x
n
+a
1
x
n+1
+... +a
k
x
n+k
= 0, k 1, a
i
R, a
0
, a
k
,= 0 (6.1.1)
atunci ea se numeste relat ie de recurent a de ordinul k cu coecient i constant i.
Coecient ii sunt constant i n sensul ca nu depind de valorile sirului x
n
.
77
78 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
O astfel de formul a este de exemplu F
n+2
= F
n+1
+ F
n
, F
0
= 0, F
1
= 1,
adic a relat ia de recurent a care deneste sirul numerelor lui Fibonacci. Ea este o
relat ie de recurent a de ordinul 2 cu coecient i constant i.
6.1.1 Ecuat ia caracteristica
G asirea expresiei lui x
n
care sa satisfaca relat ia de recurent a se numeste
rezolvarea relat iei de recurent a. F acand substitut ia
x
n
= r
n
obt inem urmatoarea ecuat ie, numit a ecuat ie caracteristic a:
a
0
+a
1
r +a
2
r
2
+... +a
k
r
k
= 0 (6.1.2)
6.1.2 Solut ia generala
Solut ia generala a relat iei de recurent a omogena de ordinul k cu coecient i
constant i este de forma
x
n
=
k
i=1
c
i
x
(i)
n
(6.1.3)
unde
_
x
(i
n
)[i 1, 2, ..., k
_
sunt solut ii liniar independente ale relat iei de recurent a
(se mai numesc si sistem fundamental de solut ii). Pentru determinarea acestor
solut ii distingem urm atoarele cazuri:
Ecuat ia caracteristica admite radacini reale si distincte
Daca r
1
, r
2
, ..., r
k
sunt r ad acini reale ale ecuat iei caracteristice, atunci r
n
i
sunt
solut ii ale relat iei de recurent a.
(x), P
(x), ..., P
(p1)
(x).
Solut ia generala este suma dintre solut ia generala corespunzatoare r ad acinilor
simple ale ecuat iei caracteristice si solut ia general a corespunzatoare r ad acinilor
multiple.
Daca ecuat ia caracteristica are r ad acinile simple r
1
, r
2
, ..., r
s
si r ad acinile mul-
tiple r
s1
, r
s+2
, ..., r
s+t
de multiplicitate p
1
, p
2
, ..., p
t
(s+p
1
+p
2
+...+p
t
= k), atunci
solut ia generala a relat iei de recurent a este
x
n
= c
1
r
n
1
+c
2
r
n
2
+... +c
s
r
n
s
+
_
c
(1)
1
+c
(1)
2
n +... +c
(1)
p11
n
p11
_
+
...
_
c
(t)
1
+c
(t)
2
n +... +c
(1)
pt1
n
pt1
_
+
unde c
1
, ..., c
s
, c
(1)
1
, ..., c
(1)
p11
, ..., c
(t)
1
, ..., c
(t)
pt1
sunt constante, care se pot determina
din condit iile init iale.
Ecuat ia caracteristica admite radacini complexe simple
Fie r = ae
ib
= a(cos b + i sinb) o r ad acin a complexa. Ecuat ia caracteristica
are coecient i reali, deci si conjugata r = ae
ib
= a(cos b i sin b) este rad acin a
pentru ecuat ia caracteristica. Atunci solut iile corespunzatoare acestora n sistemul
fundamental de solut ii pentru recurent a liniar a si omogena sunt
x
(1)
n
= a
n
cos bn, x
(2)
n
= a
n
sinbn.
Ecuat ia caracteristica admite radacini complexe multiple Daca
ecuat ia caracteristica admite perechea de r ad acini complexe
r = ae
ib
, r = ae
ib
b ,= 0
de ordin de multiplicitate k, atunci solut iile corespunzatoare acestora n sistemul
fundamental de solut ii sunt
x
(1)
n
= a
n
cos bn, x
(2)
n
= na
n
cos bn, ..., x
(k)
n
= n
k1
a
n
cos bn,
x
(k+1)
n
= a
n
sin bn, x
(k+2)
n
= na
n
sin bn, ..., x
(2k)
n
= n
k1
a
n
sin bn,
Pentru a obt ine solut ia generala a recurent ei omogene de ordinul n cu coecient i
constant i se procedeaza astfel:
1. Se determina r ad acinile ecuat iei caracteristice
2. Se scrie contribut ia ecarei rad acini la solut ia general a.
3. Se nsumeaza si se obt ine solut ia generala n funct ie de n constante
arbitrare.
4. Daca sunt precizate condit iile init iale atunci se determin a constantele
si se obt ine o solut ie unic a.
80 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
6.2 Ecuat ii recurente neomogene
6.2.1 O forma simpla
Consider am acum recurent e de urmatoarea forma mai general a
a
0
t
n
+a
1
t
n1
+... +a
k
t
nk
= b
n
p(n)
unde b este o constant a, iar p(n) este un polinom n n de grad d. Ideea generala
este sa reducem un astfel de caz la o forma omogena.
De exemplu, o astfel de recurent a poate :
t
n
2t
n1
= 3
n
j=0
a
j
Tn j = b
n
1
p
d1
(n) +b
n
2
p
d2
(n) +...
n care
p
d
(n) = n
d
+c
1
n
d1
+... +c
d
Ecuat ia caracteristica completa este:
_
_
k
j=0
a
j
r
kj
_
_
(r b
1
)
d1+1
(r b
2
)
d2+1
... = 0
Exemplul 3: T
n
= 2T(n 1) +n + 2
n
, n 1, T
0
= 0.
Acestui caz i corespund b
1
= 1, p
1
(n) = n, d
1
= 1 si b
2
= 2, p
2
(n) = 1,
d
2
= 0, iar ecuat ia caracteristica completa este:
(r 2)(r 1)
2
(r 2) = 0
82 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
cu solut ia:
T(n) = c
1
1
n
+c
2
n 2
n
+c
3
2n +c
4
n 2
n
_
_
T(0) = 0
T(1) = 2T(0) + 1 + 2
1
= 3
T(2) = 2T(1) + 2 + 2
2
= 12
T(3) = 2T(2) + 3 + 2
3
= 35
_
_
c
1
+ c
3
= 0
c
1
+c
2
+ 2c
3
+ 2c
4
= 3
c
1
+ 2c
2
+ 4c
3
+ 8c
4
= 12
c
1
+ 3c
2
+ 8c
3
+ 24c
4
= 35
_
c
1
= 2
c
2
= 1
c
3
= 2
c
4
= 1
Deci
T(n) = 2 n + 2
n+1
+n 2
n
= O(n 2
n
).
6.2.3 Teorema master
De multe ori apare relat ia de recurent a de forma
T(n) = aT(n/b) +f(n) (6.2.6)
unde a si b sunt constante iar f(n) este o funct ie (aplicarea metodei Divide et
Impera conduce de obicei la o astfel de ecuat ie recurenta). Asa numita teorema
Master d a o metoda general a pentru rezolvarea unor astfel de recurent e cand f(n)
este un simplu polinom. Solut ia dat a de teorema master este:
1. dac a f(n) = O
_
n
log
b
(a)
_
cu > 0 atunci T(n) =
_
n
log
b
a
_
2. dac a f(n) =
_
n
log
b
a
_
atunci T(n) =
_
n
log
b
a
lg n
_
3. dac a f(n) =
_
n
log
b
(a+)
_
si a f
_
n
b
_
c f(n) cu c < 1 atunci T(n) = (f(n)).
Din p acate, teorema Master nu funct ioneaza pentru toate funct iile f(n), si
multe recurent e utile nu sunt de forma (6.2.6). Din fericire ns a, aceasta este o
tehnic a de rezolvare a celor mai multe relat ii de recurent a provenite din metoda
Divide et Impera.
Pentru a rezolva astfel de ecuat ii recurente vom reprezenta arborele generat
de ecuat ia recursiva. R ad acina arborelui cont ine valoarea f(n), si ea are noduri
descendente care sunt noduri rad acin a pentru arborele provenit din T(n/b).
6.2. ECUAT II RECURENTE NEOMOGENE 83
Pe nivelul i se aa nodurile care cont in valoarea a
i
f(n/b
i
). Recursivitatea se
opreste cand se obt ine un caz de baza pentru recurent a.
Presupunem c a T(1) = f(1).
Cu aceasta reprezentare este foarte clar ca T(n) este suma valorilor din
nodurile arborelui. Presupun and c a ecare nivel este plin, obt inem
T(n) = f(n) +af(n/b) +a
2
f(n/b
2
) +a
3
f(n/b
3
) +... +a
k
f(n/b
k
)
unde k este adancimea arborelui de recursivitate. Din n/b
k
= 1 rezult a k = log
b
n.
Ultimul termen diferit de zero n suma este de forma a
k
= a
log
b
n
= n
log
b
a
(ultima
egalitate ind nt alnit a n liceu!).
Acum putem usor enunt a si demonstra teorema Master.
Teorema 1 (Teorema Master) Relat ia de recurent a T(n) = aT(n/b)+f(n) are
urm atoarea solut ie:
daca af(n/b) = f(n) unde < 1 atunci T(n) = (f(n));
daca af(n/b) = f(n) unde > 1 atunci T(n) = (n
log
b
a
);
daca af(n/b) = f(n) atunci T(n) = (f(n) log
b
n);
Demonstrat ie: Daca f(n) este un factor constant mai mare dec at f(b/n), atunci
prin induct ie se poate arata ca suma este a unei progresii geometrice descrescatoare.
Suma n acest caz este o constant a nmult it a cu primul termen care este f(n).
Daca f(n) este un factor constant mai mic dec at f(b/n), atunci prin induct ie
se poate arata ca suma este a unei progresii geometrice crescatoare. Suma n acest
caz este o constanta nmult ita cu ultimul termen care este n
log
b
a
.
84 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
Daca af(b/n) = f(n), atunci prin induct ie se poate arata ca ecare din cei
k + 1 termeni din sum a sunt egali cu f(n).
Exemple.
1. Select ia aleatoare: T(n) = T(3n/4) +n.
Aici af(n/b) = 3n/4 iar f(n) = n, rezult a = 3/4, deci T(n) = (n).
2. Algoritmul de multiplicare al lui Karatsuba: T(n) = 3T(n/2) +n.
Aici af(n/b) = 3n/2 iar f(n) = n, rezult a = 3/2, deci T(n) = (n
log
2
3
).
3. Mergesort: T(n) = 2T(n/2) +n.
Aici af(n/b) = n, iar f(n) = n, rezult a = 1 deci T(n) = (nlog
2
n).
Folosind acceasi tehnic a a arborelui recursiv, putem rezolva recurent e pentru
care nu se poate aplica teorema Master.
6.2.4 Transformarea recurent elor
La Mergesort am avut o relai e de recurent a de forma T(n) = 2T(n/2) +n si
am obt inut solut ia T(n) = O(nlog
2
n) folosind teorema Master (metoda arborelui
de recursivitate). Aceasta modalitate este corecta daca n este o putere a lui 2, dar
pentru alte valori ale lui n aceasta recurent a nu este corecta. Cand n este impar,
recurent a ne cere sa sortam un num ar elemente care nu este ntreg! Mai r au chiar,
daca n nu este o putere a lui 2, nu vom atinge niciodat a cazul de baza T(1) = 0.
Pentru a obt ine o recurent a care sa e valid a pentru orice valori ntregi ale
lui n, trebuie s a determinam cu atent ie marginile inferioar a si superioar a:
T(n) = T(n/2|) + T(n/2|) +n.
Metoda transform arii domeniului rescrie funct ia T(n) sub forma S(f(n)),
unde f(n) este o funct ie simpla si S() are o recurent a mai usoara.
Urmatoarele inegalitat i sunt evidente:
T(n) 2T(n/2|) +n 2T(n/2 + 1) +n.
Acum denim o noua funct ie S(n) = T(n + ), unde este o constant a
necunoscuta, aleas a astfel nc at sa e satisfacut a recurent a din teorema Master
S(n) S(n/2) + O(n). Pentru a obt ine valoarea corecta a lui , vom compara
dou a versiuni ale recurent ei pentru funct ia S(n +):
_
S(n) 2S(n/2) +O(n) T(n +) 2T(n/2 +) +O(n)
T(n) 2T(n/2 + 1) +n T(n +) 2T((n +)/2 + 1) +n +
Pentru ca aceste doua recurent e sa e egale, trebuie ca n/2+ = (n+)/2+1,
care implic a = 2. Teorema Master ne spune acum ca S(n) = O(nlog n), deci
T(n) = S(n 2) = O((n 2) log(n 2) = O(nlog n).
6.2. ECUAT II RECURENTE NEOMOGENE 85
Un argument similar d a o ajustare a marginii inferioare T(n) = (nlog n).
Deci, T(n) = (nlog n) este un rezultat ntemeiat desi am ignorat marginile
inferioar a si superioar a de la nceput!
Transformarea domeniului este util a pentru nl aturarea marginilor inferioar a
si superioar a, si a termenilor de ordin mic din argumentele oric arei recurent e care
se potriveste un pic cu teorema master sau metoda arborelui de recursivitate.
Exist a n geometria computat ional a o structura de date numit a arbore pliat,
pentru care costul operatiei de c autare ndeplineste relat ia de recurent a
T(n) = T(n/2) +T(n/4) + 1.
Aceasta nu se potriveste cu teorema master, pentru ca cele doua subprobleme
au dimensiuni diferite, si utiliz and metoda arborelui de recursivitate nu obt inem
dec at niste margini slabe
n << T(n) << n.
Daca nu au forma standard, ecuat iile recurente pot aduse la aceast a form a
printr-o schimbare de variabil a. O schimbare de variabil a aplicabil a pentru ecuat ii
de recurent a de tip multiplicativ este:
n = 2
k
k = log n
De exemplu, e
T(n) = 2 T(n/2) +n log n, n > 1
Facem schimbarea de variabila t(k) = T(2
k
) si obt inem:
t(k) 2 t(k 1) = k 2
k
, deci b = 2, p(k) = k, d = 1
Ecuat ia caracteristica completa este:
(r 2)
3
= 0
cu solut ia
t(k) = c
1
2
k
+c
2
k 2
k
+ c
3
k
2
2
k
Deci
T(n) = c
1
n +c
2
n log n +c
3
n log
2
n O(n log
2
n[n = 2
k
)
Uneori, printr-o schimbare de variabil a, putem rezolva recurent e mult mai
complicate.
In exemplele care urmeaza, vom nota cu T(n) termenul general al
recurent ei si cu t
k
termenul noii recurent e obt inute printr-o schimbare de variabil a.
Presupunem pentru nceput c a n este o putere a lui 2.
Un prim exemplu este recurenta
T(n) = 4T(n/2) +n, n > 1
86 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
n care nlocuim pe n cu 2
k
, notam t
k
= T(2
k
) = T(n) si obt inem
t
k
= 4t
k1
+ 2
k
Ecuat ia caracteristica a acestei recurent e liniare este
(x 4)(x 2) = 0
si deci, t
k
= c
1
4
k
+c
2
2
k
.
Inlocuim la loc pe k cu log
2
n
T(n) = c
1
n
2
+c
2
n
Rezulta
T(n) O(n
2
[n este o putere a lui 2)
Un al doilea exemplu l reprezinta ecuat ia
T(n) = 4T(n/2) +n
2
, n > 1
Proced and la fel, ajungem la recurenta
t
k
= 4t
k1
+ 4
k
cu ecuat ia caracteristica
(x 4)
2
= 0
si solut ia general a t
k
= c
1
4
2
+c
2
k4
2
.
Atunci,
T(n) = c
1
n
2
+c
2
n
2
lg n
si obt inem
T(n) O(n
2
log n[n este o putere a lui 2)
_
(n
k
), pentru a < b
k
;
(n
k
log n), pentru a = b
k
;
(n
log
b
a
), pentru a > b
k
;
6.3 Probleme rezolvate
1. S a se rezolve ecuat ia:
F
n+2
= F
n+1
+F
n
, F
0
= 0, F
1
= 1.
Ecuat aia caracteristica corespunzatoare
r
2
r 1 = 0
are solut iile
r
1
=
1 +
5
2
, r
2
=
1
5
2
.
Solut ia generala este
F
n
= c
1
_
1 +
5
2
_
n
+c
2
_
1
5
2
_
n
.
Determinam constantele c
1
si c
2
din condit iile init iale F
0
= 0 si F
1
= 1.
Rezolvand sistemul
_
c
1
+c
2
= 0
c
1
_
1+
5
2
_
+c
2
_
1
5
2
_
= 1
88 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
obt inem c
1
=
1
5
si c
1
=
1
5
.
Deci, solut ia relat iei de recurent a care deneste numerele lui Fibonacci este:
F
n
=
1
5
_
1 +
5
2
_
n
5
_
1
5
2
_
n
2. S a se rezolve relat ia de recurent a:
x
n+3
= x
n+2
+ 8x
n+1
12x
n
, x
0
= 0, x
1
= 2, x
2
= 3.
Ecuat ia caracteristica corespunzatoare este:
r
3
r
2
8r + 12 = 0
si are solut iile: r
1
= r
2
= 2 si r
3
= 3.
Solut ia generala este de forma:
x
n
= (c
1
+nc
2
)2
n
+c
3
(3)
n
.
Din condit iile init iale rezulta constantele: c
1
=
1
5
, c
2
=
1
2
si c
3
=
1
5
.
Solut ia generala este:
x
n
=
_
n
2
+
1
5
_
2
n
1
5
(3)
n
.
3. S a se rezolve relat ia de recurent a:
x
n+3
= 6x
n+2
12x
n+1
+ 8x
n
, x
0
= 0, x
1
= 2, x
2
= 4.
Ecuat ia caracteristica corespunzatoare este:
r
3
6r
2
+ 12r 8 = 0
si are solut iile: r
1
= r
2
= r
3
= 2.
Solut ia generala este de forma:
x
n
= (c
1
+c
2
n +c
3
n
2
)2
n
.
Din condit iile init iale rezulta constantele: c
1
= 0, c
2
=
3
2
si c
3
=
1
2
.
Solut ia generala este:
x
n
=
_
3
2
n
1
2
n
2
_
2
n
= (3n n
2
)2
n1
.
4. S a se rezolve relat ia de recurent a:
x
n+2
= 2x
n+1
2x
n
, x
0
= 0, x
1
= 1.
6.3. PROBLEME REZOLVATE 89
Ecuat ia caracteristica corespunzatoare este:
r
2
2r + 2 = 0
si are solut iile: r
1
= 1 +i si r
2
= 1 i care se pot scrie sub forma trigonometrica
astfel:
r
1
=
2
_
cos
4
+i sin
4
_
, r
2
=
2
_
cos
4
i sin
4
_
.
Solut iile fundamentale sunt:
x
(1)
n
=
_
2
_
n
cos
n
4
, x
(2)
n
=
_
2
_
n
sin
n
4
.
Solut ia generala este de forma:
x
n
=
_
2
_
n
_
c
1
cos
n
4
+c
2
sin
n
4
_
.
Din condit iile init iale rezulta constantele: c
1
= 0 si c
2
= 1.
Solut ia generala este:
x
n
=
_
2
_
n
sin
n
4
.
5. S a se rezolve relat ia de recurent a:
x
n+3
= 4x
n+2
6x
n+1
+ 4x
n
, x
0
= 0, x
1
= 1, x
2
= 1.
Ecuat ia caracteristica corespunzatoare este:
r
3
4r
2
+ 6r 4 = 0
si are solut iile: r
1
= 2, r
2
= 1 +i si r
3
= 1 i.
Solut ia generala este de forma:
x
n
= c
1
2
n
+c
2
_
2
_
n
cos
n
4
+c
3
_
2
_
n
sin
n
4
.
Din condit iile init iale rezulta constantele: c
1
=
1
2
, c
2
=
1
2
si c
3
=
3
2
.
Solut ia generala este:
x
n
= 2
n1
+
_
2
_
n
2
_
cos
n
4
+ 3 sin
n
4
_
.
6. S a se rezolve relat ia de recurent a:
T(n) 3T(n 1) + 4T(n 2) = 0, n 2, T(0) = 0, T(1) = 1.
90 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
Ecuat ia caracteristica r
2
3r + 4 = 0 are solut iile r
1
= 1, r
2
= 4, deci
T(n) = c
1
(1)
n
+c
2
4
n
Constantele se determina din condit iile init iale:
_
c
1
+c
2
= 0
c
1
+ 4c
2
= 1
_
c
1
=
1
5
c
2
=
1
5
Solut ia este:
T(n) =
1
5
[4
n
(1)
n
] .
7. S a se rezolve relat ia de recurent a:
T(n) = 5T(n1) 8T(n2) +4T(n3), n 3, cu T(0) = 0, T(1) = 1, T(2) = 2.
Ecuat ia caracteristica:
r
3
5r
2
+ 8r 4 = 0 r
1
= 1, r
2
= r
3
= 2
deci
T(n) = c
1
1
n
+c
2
2
n
+c
3
n2
n
Determinarea constantelor
_
_
c
1
+c
2
= 0
c
1
+ 2c
2
+ 2c
3
= 1
c
1
+ 4c
2
+ 8c
3
= 2
_
c
1
= 2
c
2
= 2
c
3
=
1
2
Deci
T(n) = 2 + 2
n+1
n
2
2
n
= 2
n+1
n2
n1
2.
8. S a se rezolve relat ia de recurent a:
T(n) = 4T(n/2) +nlg n.
In acest caz, avem af(n/b) = 2nlg n 2n, care nu este tocmai dublul lui
f(n) = nlg n. Pentru n sucient de mare, avem 2f(n) > af(n/b) > 1.9f(n).
Suma este marginit a si inferior si superior de c atre serii geometrice cresc atoare,
deci solut ia este T(n) = (n
log
2
4
) = (n
2
). Acest truc nu merge n cazurile doi si
trei ale teoremei Master.
9. S a se rezolve relat ia de recurent a:
T(n) = 2T(n/2) +nlg n.
6.3. PROBLEME REZOLVATE 91
Nu putem aplica teorema Master pentru c a af(n/b) = n/(lg n 1) nu este
egal a cu f(n) = n/ lg n, iar diferent a nu este un factor constant.
Trebuie sa calcul am suma pe ecare nivel si suma total a n alt mod. Suma
tuturor nodurilor de pe nivelul i este n/(lg n i).
In particular, aceasta nseamna
ca ad ancimea arborelui este cel mult lg n 1.
T(n) =
lg n1
i=0
n
lg n i
=
lg n
j=1
n
j
= nH
lg n
= (nlg lg n).
10. (Quicksort aleator). S a se rezolve relat ia de recurent a:
T(n) = T(3n/4) +T(n/4) +n.
n T(
n) +n.
Avem cel mult lg lg n niveluri dar acum avem nodurile de pe nivelul i care
au suma 2
i
n. Avem o serie geometrica crescatoare a sumelor nivelurilor, la fel ca
92 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
n cazul doi din teorema Master, deci T(n) este majorata de suma nivelurilor cele
mai ad anci. Se obt ine:
T(n) = (2
lg lg n
n) = (nlog n).
13. S a se rezolve relat ia de recurent a:
T(n) = 4
n T(
n) +n.
Suma nodurilor de pe nivelul i este 4
i
n. Avem o serie geometrica crescatoare,
la fel ca n cazul doi din teorema master, deci nu trebuie dec at sa avem grij a de
aceste niveluri. Se obt ine
T(n) = (4
lg lg n
n) = (nlog
2
n).
Capitolul 7
Algoritmi elementari
7.1 Operat ii cu numere
7.1.1 Minim si maxim
S a presupunem c a dorim s a determinam valorile minim a si maxima dintru-un
vector x[1..n. Proced am astfel:
vmin = x[1];
vmax = x[1];
for i=2, n
vmin = minim(vmin, x[i])
vmax = maxim(vmax, x[i])
Evident se fac 2n2 comparat ii. Se poate mai repede? Da!
Impart im sirul n
dou a si determin am vmin si vmax n cele doua zone. Comparam vmin1 cu vmin2
si stabilim vminm. La fel pentru vmax. Prelucrarea se repeta pentru cele dou a
zone (deci se foloseste recursivitatea). Apar c ate dou a comparat ii n plus de ecare
dat a. Dar c ate sunt n minus? Presupunem c a n este o putere a lui 2 si T(n) este
num arul de comparat ii. Atunci
T(n) = 2T(n/2) + 2 si T(2) = 1.
Cum rezolvam aceasta relat ie de recurent a? Banuim c a solut ia este de forma
T(n) = an +b. Atunci a si b trebuie sa satisfaca sistemul de ecuat ii
_
2a +b = 1
an +b = 2(an/2 +b) + 2
93
94 CAPITOLUL 7. ALGORITMI ELEMENTARI
care are solut ia b = 2 si a = 3/2, deci (pentru n putere a lui 2), T(n) = 3n/22,
adic a 75% din algoritmul anterior. Se poate demonstra c a num arul de comparat ii
este 3 n/2| 2 pentru a aa minimum si maximum.
O idee similar a poate aplicat a pentru varianta secvent iala. Presupunem c a
sirul are un num ar par de termeni. Atunci, algoritmul are forma:
vmin = minim(x[1],x[2])
vmax = maxim(x[1],x[2])
for(i=3;i<n;i=i+2)
cmin = minim(x[i],x[i+1])
cmax = maxim(x[i],x[i+1])
if cmin < vmin
vmin = cmin
if vmax > cmax
vmax = cmax
Fiecare iterat ie necesita trei comparat ii, iar init ializarea variabilelor necesit a
o comparat ie. Ciclul se repeta de (n 2)/2 ori, deci avem un total de 3n/2 2
comparat ii pentru n par.
7.1.2 Divizori
Fie n un num ar natural. Descompunerea n facori primi
n = p
1
1
p
2
2
... p
k
k
(7.1.1)
se numeste descompunere canonica. Daca not am prin d(n) num arul divizorilor lui
n N, atunci:
d(n) = (1 +
1
)(1 +
2
)...(1 +
k
) (7.1.2)
Pentru calculul lui d(n) putem folosi urm atorul algoritm:
static int ndiv(int n)
{
int d,p=1,nd;
d=2;nd=0;
while(n%d==0){nd++;n=n/d;}
p=p*(1+nd);
d=3;
while(d*d<=n)
{
nd=0;
while(n%d==0){nd++;n=n/d;}
7.2. ALGORITMUL LUI EUCLID 95
p=p*(1+nd);
d=d+2;
}
if(n!=1) p=p*2;
return p;
}
7.1.3 Numere prime
Pentru testarea primalit at i unui num ar putem folosi urm atorul algoritm:
static boolean estePrim(int nr)
{
int d;
if(nr<=1) return false;
if(nr==2) return true;
if(nr%2==0) return false;
d=3;
while((d*d<=nr)&&(nr%d!=0)) d=d+2;
if(d*d>nr) return true; else return false;
}
7.2 Algoritmul lui Euclid
7.2.1 Algoritmul clasic
Un algoritm pentru calculul celui mai mare divizor comun (cmmdc) a dou a
numere naturale poate descompunerea lor n factori si calculul produsului tuturor
divizorilor comuni. De exemplu dac a a = 1134 = 2 3 3 3 3 7 si b = 308 =
2 2 7 11 atunci cmmdc(a, b) = 2 7 = 14.
Descompunerea n factori a unui num ar natural n poate necesita ncercarea
tuturor numerelor naturale din intervalul [2,
n].
Un algoritm ecient pentru calculul cmmdc(a, b) este algoritmul lui Euclid.
static int cmmdc(int a, int b}
{
int c;
if (a < b) { c = a; a = b; b = c; }
while((c=a%b) != 0) { a = b; b = c;}
return b;
}
96 CAPITOLUL 7. ALGORITMI ELEMENTARI
Pentru a = 1134 si b = 308 se obt ine:
a
0
= 1134, b
0
= 308;
a
1
= 308, b
1
= 210;
a
2
= 210, b
2
= 98;
a
3
= 98, b
3
= 14.
Lema 1 cmmdc(a x b, b) = cmmdc(a, b).
Demonstrat ie: Pentru nceput ar at am ca cmmdc(a x b, b) >= cmmdc(a, b).
Presupunem c a d divide a s b, deci a = c
1
d si b = c
2
d. Atunci d divide a x b
pentru c a a x b = (c
1
x c
2
) d.
Demonstram si inegalitatea contrar a cmmdc(a x b, b) <= cmmdc(a, b).
Presupunem c a d divide a x b si b, deci a x b = c
3
d si b = c
2
d. Atunci d
divide a pentru c a a = (a x b) +x b = (c
3
+x c
2
) d. De aici rezult a ca
cmmdc(b, c) = cmmdc(c, b) = cmmdc(a mod b, b) = gcd(a, b).
Prin induct ie rezulta ca cel mai mare divizor comun al ultimelor dou a numere
este egal cu cel mai mare divizor comun al primelor dou a numere. Dar pentru cele
dou a numere a si b din nal, cmmdc(a, b) = b, pentru c a b divide a.
7.2.2 Algoritmul lui Euclid extins
Pentru orice dou a numere intregi pozitive, exist a x si y (unul negativ) astfel
nc at xa+y b = cmmdc(a, b). Aceste numere pot calculate parcurgandnapoi
algoritmul clasic al lui Euclid.
Fie a
k
si b
k
valorile lui a si b dup a k iterat ii ale buclei din algoritm. Fie x
k
si y
k
numerele care indeplinesc relat ia x
k
a
k
+y
k
b
k
= cmmdc(a
k
, b
k
) = cmmdc(a, b).
Prin inductie presupunem c a x
k
s y
k
exista, pentru c a la sf arsit, cand b
k
divide
a
k
, putem lua x
k
= 0 s y
k
= 1.
Presupun and c a x
k
si y
k
sunt cunoscute, putem calcula x
k1
si y
k1
.
a
k
= b
k1
si b
k
= a
k1
mod b
k1
= a
k1
d
k1
b
k1
, unde
d
k1
= a
k1
/b
k1
(mp art ire ntreag a).
Substituind aceste expresii pentru a
k
si b
k
obt inem
cmmdc(a, b) = x
k
a
k
+y
k
b
k
= x
k
b
k1
+y
k
(a
k1
d
k1
b
k1
)
= y
k
a
k1
+ (x
k
y
k
d
k1
) b
k1
.
Astfel, t in and cont de relat ia x
k1
a
k1
+y
k1
b
k1
= cmmdc(a, b), obt inem
x
k1
= y
k
,
y
k1
= x
k
y
k
d
k1
.
Pentru 1134 si 308, obt inem:
a
0
= 1134, b
0
= 308, d
0
= 3;
a
1
= 308, b
1
= 210, d
1
= 1;
a
2
= 210, b
2
= 98, d
2
= 2;
a
3
= 98, b
3
= 14, d
3
= 7.
7.3. OPERATII CU POLINOAME 97
si de asemenea, valorile pentru x
k
si y
k
:
x
3
= 0, y
3
= 1;
x
2
= 1, y
2
= 0 1 2 = 2;
x
1
= 2, y
1
= 1 + 2 1 = 3;
x
0
= 3, y
1
= 2 3 3 = 11.
Desigur relat ia 3 1134 11 308 = 14 este corecta. Solut ia nu este unic a.
S a observam ca (3 + k 308) 1134 (11 + k 1134) 308 = 14, pentru orice k,
ceea ce arata ca valorile calculate pentru x = x
0
si y = y
0
nu sunt unice.
7.3 Operat ii cu polinoame
Toate operat iile cu polinoame obisnuite se fac utiliz and siruri de numere
care reprezint a coecient ii polinomului. Not am cu a si b vectorii coecient ilor
polinoamelor cu care se opereaz a si cu m si n gradele lor. Deci
a(X) = a
m
X
m
+... +a
1
X + a
0
si b(X) = b
n
X
n
+... +b
1
X +b
0
.
7.3.1 Adunarea a doua polinoame
Este aseman atoare cu adunarea numerelor mari.
static int[] sumap(int[] a, int[] b)
{
int m,n,k,i,j,minmn;
int[] s;
m=a.length-1;
n=b.length-1;
if(m<n) {k=n; minmn=m;} else {k=m; minmn=n;}
s=new int[k+1];
for(i=0;i<=minmn;i++) s[i]=a[i]+b[i];
if(minmn<m) for(i=minmn+1;i<=k;i++) s[i]=a[i];
else for(i=minmn+1;i<=k;i++) s[i]=b[i];
i=k;
while((s[i]==0)&&(i>=1)) i--;
if(i==k) return s;
else
{
int[] ss=new int[i+1];
for(j=0;j<=i;j++) ss[j]=s[j];
return ss;
}
}
98 CAPITOLUL 7. ALGORITMI ELEMENTARI
7.3.2
Inmult irea a doua polinoame
Evident, gradul polinomului produs p = a b este m+n iar coecientul p
k
este
suma tuturor produselor de forma a
i
b
j
unde i +j = k, 0 i m si 0 j n.
static int[] prodp(int[] a, int[] b)
{
int m,n,i,j;
int[] p;
m=a.length-1;
n=b.length-1;
p=new int[m+n+1];
for(i=0;i<=m;i++)
for(j=0;j<=n;j++)
p[i+j]+=a[i]*b[j];
return p;
}
7.3.3 Calculul valorii unui polinom
Valoarea unui polinom se calculeaza ecient cu schema lui Horner:
a(x) = (...((a
n
x +a
n1
) x +a
n2
) x +... + a
1
) x +a
0
static int valp(int[] a, int x)
{
int m,i,val;
m=a.length-1;
val=a[m];
for(i=m-1;i>=0;i--)
val=val*x+a[i];
return val;
}
7.3.4 Calculul derivatelor unui polinom
Fie
b(X) = b
n
X
n
+b
n1
X
n1
+... +b
1
X +b
0
derivata de ordinul 1 a polinomului
a(X) = a
m
X
m
+a
m1
X
m1
+... +a
1
X +a
0
.
7.3. OPERATII CU POLINOAME 99
Dar
a
(X) = m a
m
X
m1
+ (m1) a
m1
X
m2
+... + 2 a
2
X +a
1
.
Rezulta ca
n = m1
si
b
i
= (i + 1) a
i+1
pentru 0 i n.
static int[] derivp(int[] a)
{
int m,n,i;
int[] b;
m=a.length-1;
n=m-1;
b=new int[n+1];
for(i=0;i<=n;i++)
b[i]=(i+1)*a[i+1];
return b;
}
Pentru calculul valorii v = a
i=0,bi=1
2
i
.
Deci,
x
n
=
k
i=0,bi=1
x
2
i
.
int exponent_1(int x, int n)
{
int c, z;
for (c = x, z = 1; n != 0; n = n / 2)
{
if (n & 1) /* n este impar */
z *= c;
c *= c;
}
return z;
}
int exponent_2(int x, int n)
{
if (n == 0)
return 1;
if (n & 1) /* n este impar */
return x * exponent_2(x, n - 1);
return exponent_2(x, n / 2) * exponent_2(x, n / 2);
}
7.6. OPERATII CU MATRICE 107
int exponent_3(int x, int n)
{
int y;
if (n == 0)
return 1;
if (n & 1) /* n este impar */
return x * exponent_3(x, n - 1);
y = exponent_3(x, n / 2);
return y * y;
}
7.6 Operat ii cu matrice
7.6.1
Inmult irea
O funct ie scrisa n C/C++:
void matrix_product(int** A, int** B, int** C)
{
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
{
C[i][j] = 0;
for (k = 0; k < n; k++)
C[i][j] += A[i][k] * B[k][j];
}
}
7.6.2 Inversa unei matrice
O posibilitate este cea din scoal a. Aceasta presupune calculul unor determinant i.
Determinantul det(A) se deneste recursiv astfel:
det(A) =
n1
i=0
(1)
i+j
a
i,j
det(A
i,j
).
unde a
i,j
este element al matricei iar A
i,j
este submatricea obt inut a prin eliminarea
liniei i si a coloanei j.
108 CAPITOLUL 7. ALGORITMI ELEMENTARI
int determinant(int n, int[][] a)
{
if (n == 1)
return a[0][0];
int det = 0;
int sign = 1;
int[][] b = new int[n - 1][n - 1];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
for (int k = 1; k < n; k++)
b[j][k - 1] = a[j][k];
for (int j = i + 1; j < n; j++)
for (int k = 1; k < n; k++)
b[j - 1][k - 1] = a[j][k];
det += sign * a[i][0] * determinant(n - 1, b);
sign *= -1;
}
}
Folosind determinant i, inversa matricei se poate calcula folosind regula lui
Cramer. Presupunem c a A este inversabila si e B = (b
i,j
) matricea denit a prin
b
i,j
= (1)
i+j
det(A
i,j
)/ det(A).
Atunci A
1
= B
T
, unde B
T
este transpusa matricei B.
Capitolul 8
Algoritmi combinatoriali
8.1 Principiul includerii si al excluderii si aplicat ii
8.1.1 Principiul includerii si al excluderii
Fie A si B dou a mult imi nite. Notam prin [A[ cardinalul mult imii A. Se
deduce usor ca:
[A B[ = [A[ +[B[ [A B[.
Fie A o mult ime nita si A
1
, A
2
, ..., A
n
submult imi ale sale. Atunci num arul
elementelor lui A care nu apar n nici una din submult imile A
i
(i = 1, 2, ..., n) este
egal cu:
[A[
n
i=1
[A
i
[+
1i<jn
[A
i
A
j
[
1i<j<kn
[A
i
A
j
A
k
[+...+(1)
n
[A
1
A
2
...A
n
[
Se pot demonstra prin induct ie matematica urm atoarele formule:
[
n
_
i=1
A
i
[ =
n
i=1
[A
i
[
1i<jn
[A
i
A
j
[+
1i<j<kn
[A
i
A
j
A
j
[...+(1)
n+1
[
n
i=1
A
i
[
[
n
i=1
A
i
[ =
n
i=1
[A
i
[
1i<jn
[A
i
A
j
[+
1i<j<kn
[A
i
A
j
A
j
[...+(1)
n+1
[
n
_
i=1
A
i
[
109
110 CAPITOLUL 8. ALGORITMI COMBINATORIALI
8.1.2 Determinarea funct iei lui Euler
Funct ia (n) a lui Euler ne d a num arul numerelor naturale mai mici ca n si
prime cu n.
Num arul n poate descompus n factori primi sub forma:
n = p
1
1
p
2
2
...p
m
m
Not am cu A
i
mult imea numerelor naturale mai mici ca n care sunt multipli de p
i
.
Atunci avem:
[A
i
[ =
n
p
i
, [A
i
A
j
[ =
n
p
i
p
j
, [A
i
A
j
A
k
[ =
n
p
i
p
j
p
k
, ...
Rezulta:
(n) = n
m
i=1
n
p
i
+
1i<jm
n
p
i
p
j
1i<j<km
n
p
i
p
j
p
k
+... + (1)
m
n
p
1
p
2
...p
m
care este tocmai dezvoltarea produsului
(n) = n
_
1
1
p
1
__
1
1
p
2
_
...
_
1
1
p
m
_
class Euler
{
static long[] fact;
public static void main (String[]args)
{
long n=36L; // Long.MAX_VALUE=9.223.372.036.854.775.807;
long nrez=n;
long[] pfact=factori(n);
// afisv(fact);
// afisv(pfact);
int k,m=fact.length-1;
for(k=1;k<=m;k++) n/=fact[k];
for(k=1;k<=m;k++) n*=fact[k]-1;
System.out.println("f("+nrez+") = "+n);
}
static long[] factori(long nr)
{
long d, nrrez=nr;
int nfd=0; // nr. factori distincti
boolean gasit=false;
8.1. PRINCIPIUL INCLUDERII SI AL EXCLUDERII SI APLICAT II 111
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; }
if(gasit) {nfd++;gasit=false;}
d=3;
while(nr!=1)
{
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; }
if(gasit) {nfd++;gasit=false;}
d=d+2;
}
nr=nrrez;
fact=new long[nfd+1];
long[] pf=new long[nfd+1];
int pfc=0; // puterea factorului curent
nfd=0; // nr. factori distincti
gasit=false;
d=2;
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; pfc++; }
if(gasit) {fact[++nfd]=d;pf[nfd]=pfc;gasit=false;pfc=0;}
d=3;
while(nr!=1)
{
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; pfc++; }
if(gasit) {fact[++nfd]=d;pf[nfd]=pfc;gasit=false;pfc=0;}
d=d+2;
}
return pf;
}//descfact
static void afisv(long[] a)
{
for(int i=1;i<a.length;i++) System.out.print(a[i]+" ");
System.out.println();
}
}
8.1.3 Numarul funct iilor surjective
Se dau mult imile X = x
1
, x
2
, ..., x
m
si Y = y
1
, y
2
, ..., y
n
.
Fie S
m,n
num arul funct iilor surjective f : X Y .
Fie A = f[f : X Y (mult imea tuturor funct iilor denite pe X cu valori
n Y ) si A
i
= f[f : X Y, y
i
/ f(X) (mult imea funct iilor pentru care y
i
nu
este imaginea nici unui element din X).
112 CAPITOLUL 8. ALGORITMI COMBINATORIALI
Atunci
S
m,n
= [A[ [
n
_
i=1
A
i
[
Folosind principiul includerii si al excluderii, obt inem
S
m,n
= [A[
n
i=1
[A
i
[ +
1i<jn
[A
i
A
j
[ ... + (1)
n
[A
1
A
2
... A
n
[
Se poate observa usor ca [A[ = n
m
, [A
i
[ = (n 1)
m
, [A
i
A
j
[ = (n 2)
m
,
etc.
Din Y putem elimina k elemente n C
k
n
moduri, deci
1i1<i2<...<i
k
n
[
k
j=1
A
ij
[ = C
k
n
(n k)
m
Rezulta:
S
m,n
= n
m
C
1
n
(n 1)
m
+C
2
n
(n 2)
m
+... + (1)
n1
C
n1
n
Observat ii:
1. Deoarece A
1
A
2
... A
n
= si pentru c a nu poate exista o funct ie care
sa nu ia nici o valoare, ultimul termen lipseste.
2. Daca n = matunci num arul funct iilor surjective este egal cu cel al funct iilor
injective, deci S
m,n
= n! si se obt ine o formul a interesanta:
n! =
n1
k=0
(1)
k
C
k
n
(n k)
n
class Surjectii
{
public static void main (String[]args)
{
int m, n=5, k, s;
for(m=2;m<=10;m++)
{
s=0;
for(k=0;k<=n-1;k++)
s=s+comb(n,k)*putere(-1,k)*putere(n-k,m);
System.out.println(m+" : "+s);
}
System.out.println("GATA");
}
8.1. PRINCIPIUL INCLUDERII SI AL EXCLUDERII SI APLICAT II 113
static int putere (int a, int n)
{
int rez=1, k;
for(k=1;k<=n;k++) rez=rez*a;
return rez;
}
static int comb (int n, int k)
{
int rez, i, j, d;
int[] x=new int[k+1];
int[] y=new int[k+1];
for(i=1;i<=k;i++) x[i]=n-k+i;
for(j=1;j<=k;j++) y[j]=j;
for(j=2;j<=k;j++)
for(i=1;i<=k;i++)
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
rez=1;
for(i=1;i<=k;i++) rez=rez*x[i];
return rez;
}
static int cmmdc (int a,int b)
{
int d,i,c,r;
if (a>b) {d=a;i=b;} else{d=b;i=a;}
while (i!=0) { c=d/i; r=d%i; d=i; i=r; }
return d;
}
}
8.1.4 Numarul permutarilor fara puncte xe
Fie X = 1, 2, ..., n. Daca p este o permutare a elementelor mult imii X,
spunem ca num arul i este un punct x al permutarii p, dac a p(i) = i (1 i n).
Se cere sa se determine numarul D(n) al permut arilor f ar a puncte xe, ale
mult imii X. S a not am cu A
i
mult imea celor (n1)! permut ari care admit un punct
x n i (dar nu obligatoriu numai acest punct x!). Folosind principiul includerii
114 CAPITOLUL 8. ALGORITMI COMBINATORIALI
si al excluderii, num arul permut arilor care admit cel put in un punct x este egal
cu:
[A
1
A
2
... A
n
[ =
n
i=1
[A
i
[
1i<jn
[A
i
A
j
[ +... + (1)
n1
[
n
i=1
A
i
[.
Dar
[A
i1
A
i2
... A
i
k
[ = (n k)!
deoarece o permutare din mult imea A
i1
A
i2
... A
i
k
are puncte xe n pozit iile
i
1
, i
2
, ..., i
k
, celelalte pozit ii cont in and o permutare a celor n k elemente r amase
(care pot avea sau nu puncte xe!). Cele k pozit ii i
1
, i
2
, ..., i
k
pot alese n C
k
n
moduri, deci
[A
1
A
2
... A
n
[ = C
1
n
(n 1)! C
2
n
(n 2)! +... + (1)
n1
C
n
n
.
Atunci
D(n) = n! [A
1
A
2
... A
n
[ =
= n! C
1
n
(n 1)! +C
2
n
(n 2)! +... + (1)
n
C
n
n
= n!
_
1
1
1!
+
1
2!
1
3!
+... +
(1)
n
n!
_
.
De aici rezulta ca
lim
n
D(n)
n!
= e
1
,
deci, pentru n mare, probabilitatea ca o permutare a n elemente, aleasa aleator,
sa nu aib a puncte xe, este de e
1
0.3678.
Se poate demonstra usor ca:
D(n + 1) = (n + 1)D(n) + (1)
n+1
D(n + 1) = n(D(n) +D(n 1)) .
class PermutariFixe
{
public static void main(String [] args)
{
long n=10,k,s=0L,xv,xn; // n=22 maxim pe long !
if((n&1)==1) xv=-1L; else xv=1L;
s=xv;
for(k=n;k>=3;k--) { xn=-k*xv; s+=xn; xv=xn; }
System.out.println("f("+n+") = "+s);
}
}
8.2. PRINCIPIUL CUTIEI LUI DIRICHLET SI APLICAT II 115
8.2 Principiul cutiei lui Dirichlet si aplicat ii
Acest principiu a fost formulat prima data de Dirichle (1805-1859).
bB
[f
1
(b)[ < r
n
r
= n
ceea ce este o contradict ie.
8.2.1 Problema subsecvent ei
Se d a un sir nit a
1
, a
2
, ..., a
n
de numerentregi. Exista o subsecvent a a
i
, a
i+1
, ..., a
j
cu proprietatea ca a
i
+a
i+1
+... +a
j
este un multiplu de n.
S a consider am urm atoarele sume:
s
1
= a
1
,
s
2
= a
1
+a
2
,
...
s
n
= a
1
+a
2
+... +a
n
.
Daca exista un k astfel s
k
este multiplu de n atunci i = 1 si j = k.
116 CAPITOLUL 8. ALGORITMI COMBINATORIALI
Daca nici o suma part ial a s
k
nu este multiplu de n, atunci resturile mp art irii
acestor sume part iale la n nu pot dec at n mult imea 1, 2, ..., n 1. Pentru c a
avem n sume part iale si numai n 1 resturi, nseamna ca exista cel put in dou a
sume part iale (s
k1
si s
k2
, unde k
1
< k
2
) cu acelasi rest. Atunci subsecvent a cautat a
se obt ine lu and i = k
1
+ 1 si j = k
2
.
8.2.2 Problema subsirurilor strict monotone
Se d a sirul de numere reale distincte a
1
, a
2
, ..., a
mn+1
. Atunci, sirul cont ine
un subsir crescator de m+ 1 elemente:
a
i1
< a
i2
< ... < a
im+1
unde 1 i
1
< i
2
< ... < i
m+1
mn + 1,
sau un subsir descrescator de n + 1 elemente
a
j1
< a
j2
< ... < a
jn+1
unde 1 j
1
< j
2
< ... < j
n+1
mn + 1,
sau ambele tipuri de subsiruri.
Fiec arui element al sirului i asociem perechea de numere naturale (x
i
, y
i
)
unde x
i
este lungimea maxima a subsirurilor cresc atoare care ncep cu a
i
iar y
i
este lungimea maxima a subsirurilor descrescatoare care ncep n a
i
.
Presupunem c a armat ia problemei nu este adevarat a, adic a: pentru toate
numerele naturale x
i
si y
i
avem 1 x
i
m si 1 y
i
n. Atunci perechile de
numere (x
i
, y
i
) pot avea mn elemente distincte.
Deoarece sirul are mn+1 termeni, exista un a
i
si un a
j
pentru care perechile
de numere (x
i
, y
i
) si (x
j
, y
j
) sunt identice (x
i
= x
j
, y
i
= y
j
), dar acest lucru este
imposibil (cei doi termeni a
i
si a
j
ar trebui s a coincid a), ceea ce este o contradit ie.
Deci exista un subsir crescator cu m + 1 termeni sau un subsir descrescator
cu n + 1 termeni.
8.3 Numere remarcabile
8.3.1 Numerele lui Fibonacci
Numerele lui Fibonacci se pot deni recursiv prin:
F
0
= 0, F
1
= 1, F
n
= F
n1
+F
n2
pentru n 2. (8.3.1)
Primele numere Fibonacci sunt:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, ...
8.3. NUMERE REMARCABILE 117
Se poate ar ata ca
F
n
=
1
2
n
5
__
1 +
5
_
n
_
1
5
_
n
_
.
Numerele lui Fibonacci satisfac multe identit at i interesante, ca de exemplu:
_
1 1
1 0
_
n
=
_
F
n+1
F
n
F
n
F
n1
_
(8.3.2)
F
n+1
F
n1
F
2
n
= (1)
n
(8.3.3)
F
n+m
= F
m
F
n+1
+F
m1
F
n
(8.3.4)
F
nk
= multiplu de F
k
(8.3.5)
(8.3.6)
si
F
2
+ F
4
+... +F
2n
= F
2n+1
1 (8.3.7)
F
1
+F
3
+... +F
2n1
= F
2n
(8.3.8)
F
2
1
+F
2
2
+... +F
2
n
= F
n
F
n+1
(8.3.9)
F
1
F
2
+F
2
F
3
+... +F
2n1
F
2n
= F
2
2n
(8.3.10)
F
1
F
2
+F
2
F
3
+... +F
2n
F
2n+1
= F
2
2n+1
1 (8.3.11)
Teorema 2 Orice num ar natural n se poate descompune ntr-o sum a de numere
Fibonacci. Dac a nu se folosesc n descompunere numerele F
0
si F
1
si nici dou a
numere Fibonacci consecutive, atunci aceast a descompunere este unic a abstract ie
f acand de ordinea termenilor.
Folosind aceasta descompunere, numerele naturale pot reprezentate aseman ator
reprezentarii n baza 2. De exemplu
19 = 1 13 + 0 8 + 1 5 + 0 3 + 0 2 + 1 1 = (101001)
F
k=0
b
k
b
n1k
Se obt ine
b
n
=
1
n + 1
C
n
2n
2. Care este num arul permut arilor a n obiecte cu p puncte xe?
Rezolvare: Deoarece cele p puncte xe pot alese n C
p
n
moduri, si cele np
puncte ramase nu mai sunt puncte xe pentru permutare, rezulta ca num arul
permut arilor cu p puncte xe este egal cu
C
p
n
D(n p)
deoarece pentru ecare alegere a celor p puncte xe exista D(np) permut ari ale
obiectelor r amase, far a puncte xe.
122 CAPITOLUL 8. ALGORITMI COMBINATORIALI
3. Fie X = 1, 2, ..., n. Daca E(n) reprezint a num arul permut arilor pare
1
ale mult imii X f ar a puncte xe, atunci
E(n) =
1
2
_
D(n) + (1)
n1
(n 1)
_
.
Rezolvare: Fie A
i
mult imea permutarilor pare p astfel nc at p(i) = i. Deoarece
num arul permut arilor pare este
1
2
n!, rezult a ca
E(n) =
1
2
n! [A
1
... A
n
[ =
=
1
2
n! C
1
n
(n 1)! +C
2
n
(n 2)! +... + (1)
n
C
n
n
.
T in and cont c a
[A
i1
A
i2
... A
i
k
[ = (n k)!
rezult a formula ceruta.
1
Daca i < j, si n permutare i apare dupa j, spunem ca avem o inversiune. O permutare este
par a daca are un numar par de inversiuni
Capitolul 9
Algoritmi de cautare
9.1 Problema cautarii
Problema caut arii este: se da un vector a cu n elemente si o valoare x de
acelasi tip cu elementele din a. S a se determine p astfel nc at x = a[p] sau 1 daca
nu exist a un element cu valoarea v n a.
O tabel a cu campuri care nu sunt de acelasi tip se poate organiza cu ajutorul
vectorilor daca num arul de coloane este sucient de mic. De exemplu, o tabela cu
trei informat ii: num ar curent, nume, telefon poate organizat a cu ajutorul a doi
vectori (nume si telefon) iar num arul curent este indicele din vector.
9.2 Cautarea secvent iala
static int cauta (String x) {
for (int i = 0; i < N; ++i)
if (x.equals(nume[i])) return telefon[i];
return 1;
}
se poate scrie si sub forma:
static int cauta (String x) {
int i = 0;
while (i < N && !x.equals(nume[i])) ++i;
if (i < N) return telefon[i];
else return 1;
}
123
124 CAPITOLUL 9. ALGORITMI DE C
AUTARE
O alt a posibilitate este de a pune o santinel a n cap atul tabelei.
static int cauta (String x) {
int i = 0;
nume[N] = x; telefon[N] = 1;
while (! x.equals(nume[i])) ++i;
return tel[i];
}
Scrierea procedurii de c autare ntr-un tabel de nume este n acest caz mai
ecienta, pentru c a nu se face decat un singur test n plus aici (n loc de dou a teste).
Cautarea secvent ial a se mai numeste si cautare linear a, pentru c a se executa N/2
operat ii n medie, si N operat ii n cazul cel mai defavorabil.
Intr-un tablou cu
10.000 elemente, cautarea executa 5.000 operat ii n medie, ceea ce nseamna un
consum de timp de aproximativ 0.005 ms (milisecunde).
Iat a un program complet care utilizeaza cautarea linear a ntr-o tabel a.
class Tabela {
final static int N = 6;
static String nume[] = new String[N+1];
static int telefon[] = new int[N+1];
static void initializare() {
nume[0] = "Paul"; telefon[0] = 2811;
nume[1] = "Robert"; telefon[1] = 4501;
nume[2] = "Laura"; telefon[2] = 2701;
nume[3] = "Ana"; telefon[3] = 2702;
nume[4] = "Tudor"; telefon[4] = 2805;
nume[5] = "Marius"; telefon[5] = 2806;
}
static int cauta(String x) {
for (int i = 0; i < N; ++i)
if (x.equals(nume[i]))
return tel[i];
return 1;
}
public static void main (String args[]) {
initializare();
if (args.length == 1)
System.out.println(cauta(args[0]));
}
}
9.3. C
AUTARE BINAR
A 125
Cel mai simplu algoritm care rezolv a aceasta problem a este cautarea liniar a,
care testeaz a elementele vectorului unul dupa altul, ncepand cu primul.
p=-1;
for(i=0;i<n;i++)
if(x==a[i]) { p=i; break; }
S a analizam complexitatea acestui algoritm pe cazul cel mai defavorabil, acela
n care v nu se gaseste n a.
In acest caz se va face o parcurgere completa a lui a.
Consider and ca operat ie elementara testul v == a[i], num arul de astfel de operat ii
elementare efectuate va egal cu numarul de elemente al lui a, adic a n. Deci
complexitatea algoritmului pentru cazul cel mai defavorabil este O(n).
9.3 Cautare binara
O alt a tehnic a de cautare n tabele este cautarea binar a. Presupunem c a
tabela de nume este sortata n ordine alfabetic a (cum este cartea de telefoane).
AUTARE
Desigur cautarea secvent ial a este foarte usor de programat, si ea se poate
folosi pentru tabele de dimensiuni mici. Pentru tabele de dimensiuni mari, cautarea
binar a este mult mai interesanta.
Se poate obt ine un timp sub-logaritmic dac a se cunoaste distribut ia obiectelor.
De exemplu, n cartea de telefon, sau n dict ionar, se stie apriori ca un nume care
ncepe cu litera V se aa catre sf arsit. Presupunand distribut ia uniforma, putem
face o regula de trei simpl a pentru g asirea indicelui elementului de referint a
pentru comparare, n loc s a alegem mijlocul, si sa urm am restul algoritmului de
cautare binar a. Aceast a metoda se numeste cautare prin interpolare. Timpul de
cautare este O(log log N), ceea ce nseamna cam 4 operat ii pentru o tabel a de
10.000 elemente si 5 operat ii pentru 10
9
elemente n tabel a. Este spectaculos!
O implementare iterativ a a caut arii binare ntr-un vector ordonat (cresc ator
sau descrescator) este:
int st, dr, m;
boolean gasit;
st=0;
dr=n-1;
gasit=false;
while((st < dr) && !gasit)
{
m=(st+dr)/2;
if(am]==x)
gasit=true;
else if(a[m] > x)
dr=m-1;
else st=m+1;
}
if(gasit) p=m; else p=-1;
Algoritmul poate descris, foarte elegant, recursiv.
9.4 Inserare n tabela
La cautarea secvent ial a sau binar a nu am fost preocupat i de inserarea n
tabel a a unui nou element. Aceasta este destul de rar a n cazul cart ii de telefon
dar n alte aplicat ii, de exemplu lista utilizatorilor unui sistem informatic, este
frecvent utilizat a.
Vom vedea cum se realizeaza inserarea unui element ntr-o tabel a, n cazul
caut arii secvent iale si binare.
Pentru cazul secvent ial este sucient sa ad aug am la cap atul tabelei noul
element, daca are loc. Daca nu are loc, se apeleaz a o procedura error care aseaza
un mesaj de eroare.
9.5. DISPERSIA 127
void inserare(String x, int val) {
++n;
if (n >= N)
error ("Depasire tabela");
numem[n] = x;
telefon[n] = val;
}
Inserarea se face deci n timp constant, de ordinul O(1).
In cazul caut arii binare, trebuie ment inut tabelul ordonat. Pentru inserarea
unui element nou n tabel a, trebuie gasit a pozit ia sa printr-o c autare binar a (sau
secvent ial a), apoi se deplaseaza toate elementele din spatele ei spre dreapta cu o
pozit ie pentru a putea insera noul element n locul corect. Aceasta necesita log n+n
operet ii. Inserarea ntr-o tabel a ordonat a cu n elemente este deci de ordinul O(n).
9.5 Dispersia
O alt a metoda de cautare n tabele este dispersia. Se utilizeaz a o funct ie h
de grupare a cheilor (adesea siruri de caractere) ntr-un interval de numere ntregi.
Pentru o cheie x, h(x) este locul unde se af a x n tabel a. Totul este n ordine dac a
h este o aplicat ie injectiva. De asemenea, este bine ca funct ia aleasa sa permit a
evaluarea cu un num ar mic de operat ii. O astfel de funct ie este
h(x) =
_
x
1
B
m1
+x
2
B
m2
+... +x
m1
B +x
m
_
mod N.
De obicei se ia B = 128 sau B = 256 si se presupune c a dimensiunea tabelei
N este un numar prim. De ce? Pentru c a nmult irea puterilor lui 2 se poate face
foarte usor prin operat ii de deplasare pe bit i, numerele ind reprezentate n binar.
In general, la masinile (calculatoarele) moderne, aceste operat ii sunt net mai rapide
dec at nmult irea numerelor oarecare. C at despre alegerea lui N, aceasta se face
pentru a evita orice interferent a ntre ntre nmult irile prin B si mp art irile prin
N.
Intr-adev ar, dac a de exemplu B = N = 256, atunci h(x) = x(m) este funct ia h
care nu depinde dec at de ultimul caracter al lui x. Scopul este de a avea o funct ie h,
de dispersie, simplu de calculat si avand o bun a distribut ie pe intervalul [0, N1].
Calculul funct iei h se face prin funct ia h(x,m), unde m este lungimea sirului x,
static int h(String x){
int r = 0;
for (int i = 0; i < x.length(); ++i)
r = ((r * B) + x.charAt(i)) % N;
return r;
}
128 CAPITOLUL 9. ALGORITMI DE C
AUTARE
Deci funct ia h d a pentru toate cheile x o intrare posibil a n tabel a. Se poate
apoi verica dac a x = nume[h(x)]. Dac a da, c autarea este terminata. Daca nu,
nseamna ca tabela cont ine o alt a cheie astfel nc at h(x
dat a de i
0 1 2 3 4 5 6 7
3 12 4 30 7 4 23 14
i m
0 1 2 3 4 5 6 7
3 12 4 30 7 4 23 14
i m
0 1 2 3 4 5 6 7
3 4 12 30 7 4 23 14
i m
0 1 2 3 4 5 6 7
3 4 12 30 7 4 23 14
i m
0 1 2 3 4 5 6 7
3 4 4 30 7 12 23 14
i m
0 1 2 3 4 5 6 7
3 4 4 30 7 12 23 14
i m
0 1 2 3 4 5 6 7
3 4 4 7 30 12 23 14
i m
0 1 2 3 4 5 6 7
3 4 4 7 30 12 23 14
i m
0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14
i m
0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14
i m
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
i m
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
im
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
im
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
Este usor de calculat numarul de operat ii necesare. La ecare iterat ie, se
pleaca de la elementul a
i
si se compara succesiv cu a
i+1
, a
i+2
, ..., a
N
. Se fac deci
N i comparat ii. Se ncepe cu i = 1 si se termin a cu i = N 1. Deci, se fac
(N 1) +(N 2) +... +2 +1 = N(N 1)/2 comparat ii si N 1 interschimb ari.
Sortarea prin select ie executa un num ar de comparat ii de ordinul N
2
.
O variant a a sort arii prin select ie este metoda bulelor. Principiul ei este de
a parcurge sirul (a
1
, a
2
, ..., a
N
) invers and toate perechile de elemente consecutive
(a
j
1, a
j
) neordonate. Dup a prima parcurgere, elementul maxim se va aa pe
pozit ia N. Se rencepe cu prexul (a
,
a
2
, ..., a
N1
), ..., (a
1
, a
2
).
Procedura corespunzatoare utilizeaza un indice i care marcheaz a sf arsitul
prexului n sortare, si un indice j care permite deplasarea catre marginea i.
10.2. SORTARE PRIN SELECTIE 133
Se poate calcula de asemenea foarte usor numarul de operat ii si se obt ine un
num ar de ordinul O(N
2
) comparat ii si, eventual interschimb ari (daca, de exemplu,
tabloul este init ial n ordine descrescatoare).
static void sortBule() {
int t;
for (int i = N1; i >= 0; i)
for (int j = 1; j <= i; ++j)
if (a[j1] > a[j]) {
t = a[j1]; a[j1] = a[j]; a[j] = t;
}
}
0 1 2 3 4 5 6 7
7 12 4 30 3 4 23 14
j i
0 1 2 3 4 5 6 7
7 4 12 30 3 4 23 14
j i
0 1 2 3 4 5 6 7
7 4 12 30 3 4 23 14
j i
0 1 2 3 4 5 6 7
7 4 12 3 30 4 23 14
j i
0 1 2 3 4 5 6 7
7 4 12 3 30 4 23 14
j i
0 1 2 3 4 5 6 7
7 4 12 3 4 30 23 14
j i
0 1 2 3 4 5 6 7
7 4 12 3 4 30 23 14
j i
0 1 2 3 4 5 6 7
7 4 12 3 4 23 30 14
j i
0 1 2 3 4 5 6 7
7 4 12 3 4 23 30 14
ji
0 1 2 3 4 5 6 7
7 4 12 3 4 23 14 30
ji
Dup a prima parcurgere 30 a ajuns pe locul s au (ultimul loc n vector).
0 1 2 3 4 5 6 7
7 4 12 3 4 23 14 30
j i
0 1 2 3 4 5 6 7
4 7 12 3 4 23 14 30
j i
0 1 2 3 4 5 6 7
4 7 12 3 4 23 14 30
j i
0 1 2 3 4 5 6 7
4 7 3 12 4 23 14 30
j i
134 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
0 1 2 3 4 5 6 7
4 7 3 12 4 23 14 30
j i
0 1 2 3 4 5 6 7
4 7 3 4 12 23 14 30
j i
0 1 2 3 4 5 6 7
4 7 3 4 12 23 14 30
ji
0 1 2 3 4 5 6 7
4 7 3 4 12 14 23 30
ji
Dup a a doua parcurgere 23 a ajuns pe locul s au (penultimul loc n vector).
0 1 2 3 4 5 6 7
4 7 3 4 12 14 23 30
j i
0 1 2 3 4 5 6 7
4 3 7 4 12 14 23 30
j i
0 1 2 3 4 5 6 7
4 3 7 4 12 14 23 30
j i
0 1 2 3 4 5 6 7
4 3 4 7 12 14 23 30
j i
Dup a a treia parcurgere 14 a ajuns pe locul s au (de fapt era deja pe locul
sau de la nceputul acestei parcurgeri; s-au mai aranjat totusi cateva elemente!).
0 1 2 3 4 5 6 7
4 3 4 7 12 14 23 30
j i
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
j i
Dup a a patra parcurgere 12 a ajuns pe locul s au (de fapt era deja pe locul s au
de la nceputul acestei parcurgeri; oricum, la aceasta parcurgere s-au mai aranjat
cateva elemente!).
La urmatoarea parcurgere nu se efectueaza nici o interschimbare de elemente.
Vectorul este deja sortat, asa ca urm atoarele parcurgeri se fac, de asemenea, far a
sa se execute nici o interschimbare de elemente. O idee buna este introducerea unei
variabile care s a contorizeze numarul de interschimb ari din cadrul unei parcurgeri.
Daca nu s-a efectuat nici o interschimbare atunci vectorul este deja sortat asa ca
se poate ntrerupe execut ia urm atoarelor parcurgeri. Programul modicat este:
static void sortBule() {
int t, k;
for (int i = N1; i >= 0; i)
{
k=0;
for (int j = 1; j <= i; ++j)
if (a[j1] > a[j]) {t = a[j1]; a[j1] = a[j]; a[j] = t; k++;}
if(k==0) break;
}
}
10.3. SORTARE PRIN INSERT IE 135
10.3 Sortare prin insert ie
O metod a complet diferit a este sortarea prin insert ie. Aceasta este metoda
utilizat a pentru sortarea unui pachet de c art i de joc. Se ia prima carte, apoi a
doua si se aranjeaza n ordine crescatoare. Se ia a treia carte si se plaseaza pe
pozit ia corespunzatoare fat a de primele dou a cart i, si asa mai departe. Pentru
cazul general, sa presupunem c a primele i 1 cart i sun deja sortate crescator. Se
ia a i-a carte si se plaseaza pe pozit ia corespunzatoare relativ la primele i 1 cart i
deja sortate. Se continu a p an a la i = N.
10.3.1 Insert ie directa
Daca determinarea pozit iei corespunz atoare, n subsirul format de primele
i 1 elemente, se face secvent ial, atunci sortarea se numeste sortare prin insert ie
direct a. Procedura care realizeaz a aceastsortare este:
static void sortInsertie() {
int j, v;
for (int i = 1; i < N; ++i) {
v = a[i]; j = i;
while (j > 0 && a[j-1] > v) {
a[j] = a[j-1];
j;
}
a[j] = v;
}
}
Pentru plasarea elementului a
i
din vectorul nesortat (elementele de pe pozit iile
din stanga ind deja sortate) se parcurge vectorul spre st anga plec and de la pozit ia
i 1. Elementele vizitate se deplaseaza cu o pozit ie spre dreapta pentru a permite
plasarea elementului a
i
(a carui valoare a fost salvat a n variabila temporar a v) pe
pozit ia corespunzatoare. Procedura cont ine o mica eroare, daca a
i
este cel mai mic
element din tablou, caci va iesi din tablou spre st anga. Se poate remedia aceasta
situat ie plasand un element a
0
de valoare max int. Se spune c a a fost plasat a
o santinela la stanga tabloului a. Aceasta nu este ntotdeauna posibil, si atunci
trebuie adaugat un test asupra indicelui j n bucla while. Un exemplu numeric
este cel care urmeaza:
Inserarea lui 12 nu necesita deplas ari de elemente.
0 1 2 3 4 5 6 7
7 12 4 30 3 4 23 14
j i
0 1 2 3 4 5 6 7
4 7 12 30 3 4 23 14
j i
136 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
Inserarea lui 30 nu necesita deplas ari de elemente.
0 1 2 3 4 5 6 7
4 7 12 30 3 4 23 14
j i
0 1 2 3 4 5 6 7
3 4 7 12 30 4 23 14
j i
0 1 2 3 4 5 6 7
3 4 7 12 30 4 23 14
j i
0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14
j i
0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14
j i
0 1 2 3 4 5 6 7
3 4 4 7 12 23 30 14
j i
0 1 2 3 4 5 6 7
3 4 4 7 12 23 30 14
j i
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
j i
Num arul de comparat ii pentru inserarea unui element n secvent a deja sortat a
este egal cu numarul de inversiuni plus 1.
Fie c
i
num arul de comparat ii. Atunci
c
i
= 1 + carda
j
[a
j
> a
i
, j < i
Pentru o permutare corespunz atoare unui sir de sortari, unde num arul de
inversiuni este inv(), num arul total de comparat ii pentru sortarea prin insert ie
este
C
=
N
i=2
c
i
= N 1 +inv().
Deci, num arul mediu de comparat ii este
C
N
=
1
N!
SN
C
= N 1 +
N(N 1)
4
=
N(N + 3)
4
1
unde S
n
reprezint a grupul permut arilor de n elemente.
Desi ordinul de crestere este tot N
2
, aceasta metoda de sortare este mai
ecienta decat sortarea prin select ie. Cea mai bun a metoda de sortare necesit a
nlog
2
n comparat ii.
In plus, ea are o proprietate foarte buna: num arul de operat ii depinde puternic
de ordinea init ial a din tablou.
In cele ce urmeaza sunt prezentate toate operat iile n Java utiliz and tehnicile
urm atoare: se reatribuie numarul 0 unui nou client atunci c and se dep aseste un
anumit prag pentru valoarea sfarsit. Se spune c a este un tablou (sau tampon)
circular, sau coad a circular a.
class Coada {
final static int MaxC = 100;
int inceput;
int sfarsit;
boolean plina, vida;
int continut[];
Coada () {
inceput = 0; sfarsit = 0;
plina= false; vida = true;
info = new int[MaxC];
}
static Coada vida(){
return new Coada();
}
static void facVida (Coada c) {
c.inceput = 0; c.sfarsit = 0;
c.plina = false; c.vida = true;
}
static boolean esteVida(Coada c) {
return c.vida;
}
11.2. COZI 149
static boolean estePlina(Coada c) {
return c.plina;
}
static int valoare(Coada c) {
if (c.vida)
erreur ("Coada Vida.");
return c.info[f.inceput];
}
private static int succesor(int i) {
return (i+1) % MaxC;
}
static void adaug(int x, Coada c) {
if (c.plina)
erreur ("Coada Plina.");
c.info[c.sfarsit] = x;
c.sfarsit = succesor(c.sfarsit);
c.vida = false;
c.plina = c.sfarsit == c.inceput;
}
static void elimina (Coada c) {
if (c.vida)
erreur ("Coada Vida.");
c.inceput = succesor(c.inceput);
c.vida = c.sfarsit == c.inceput;
c.plina = false;
}
}
e1 e2 en
inceput sfarsit
...
Figura 11.3: Coad a de asteptare implementat a ca lista
O alt a modalitate de gestionare a cozilor const a n utilizarea listelor nl ant uite cu
150 CAPITOLUL 11. LISTE
gardan care se cunosc adresele primului si ultimului element. Aceasta d a operat iile
urm atoare:
class Coada {
Lista inceput;
Lista sfarsit;
Coada (Lista a, Lista b) {
inceput = a;
sfarsit = b;
}
static Coada vida() {
Lista garda = new Lista();
return new Coada (garda, garda);
}
static void facVida (Coada c) {
Lista garda = new Lista();
c.inceput = c.sfarsit = garda;
}
static boolean esteVida (Coada c) {
return c.inceput == c.sfarsit;
}
static int valoare (Coada c) {
Lista b = c.inceput.next;
return b.info;
}
static void adauga (int x, Coada c) {
Lista a = new Lista (x, null);
c.sfarsit.next = a;
f.sfarsit = a;
}
static void elimina (Coada c) {
if (esteVida(c))
erreur ("Coada Vida.");
c.inceput = c.inceput.next;
}
}
Cozile se pot implementa e cu ajutorul tablourilor e cu ajutorul listelor sim-
11.3. STIVE 151
plu nl ant uite. Scrierea programelor consta n primul r and n alegerea structurilor
de date pentru reprezentarea cozilor. Ansamblul funct iilor care opereaza asupra
cozilor se pot plasa ntr-un modul care manipuleaz a tablouri sau liste. Utilizarea
cozilor se face cu ajutorul funct iilor vida, adauga, valoare, elimina. Aceasta este
deci interfat a cozilor care are important a n programe complexe.
11.3 Stive
Not iunea de stiv a intervine n mod curent n programare, rolul sau principal
ind la implementarea apelurilor de proceduri. O stiv a se poate imagina ca o cutie
n care sunt plasate obiecte si din care se scot n ordinea invers a fat a de cum au
fost introduse: obiectele sunt puse unul peste altul n cutie si se poate avea acces
numai la obiectul situat n v arful stivei.
In acest caz, este utila prezentarea unui program principal care utilizeaz a
aceste funct ii.
public static void main (String args[]) {
Element exp[] = new Element [args.length];
for (int i = 0; i < args.length; ++i) {
String s = args[i];
if (s.equals("+") || s.equals("*"))
exp[i] = new Element (true, 0, s.charAt(0));
else
exp[i] = new Element (false, Integer.parseInt(s), );
}
try { System.out.println(calcul(exp)); }
catch (ExceptionStiva x) {
System.err.println("Stiva " + x.nom);
}
}
11.5. OPERATII ASUPRA LISTELOR 155
11.5 Operat ii asupra listelor
In aceasta sect iune sunt prezentat i cat iva algoritmi de manipulare a listelor.
Acestia sunt utilizat i n limbajele de programare care au listele ca structuri de
baza. Funct ia Tail este o primitiv a clasica; ea suprim a primul element al listei.
e1
e2 e3 e4
a Tail(a)
e1
e2 e3 e4
Figura 11.4: Suprimarea primului element din list a
class Lista {
Object info;
Lista next;
Lista(Object x, Lista a) {
info = x;
next = a;
}
static Lista cons (Object x, Lista a) {
return new Lista (x, a);
}
static Object head (Lista a) {
if (a == null)
erreur ("Head dune liste vide.");
return a.info;
}
static Lista tail (Lista a) {
if (a == null)
erreur ("Tail dune liste vide.");
return a.next;
}
Procedurile asupra listelor construiesc o lista plec and de la alte dou a liste,
pun o lista la cap atul celeilalte liste.
In prima procedur a append, cele doua liste
nu sunt modicate; n a doua procedur a, concat, prima lista este transformata
pentru a ret ine rezultatul. Totusi, se poate remarca faptul c a, dac a append copiaza
primul s au argument, partajeaz a nalul listei rezultat cu al doilea argument.
156 CAPITOLUL 11. LISTE
a b
a1 a2 a3 b1 b2 b3 b4
append(a,b}
a1 a2 a3
Figura 11.5: Concatenarea a dou a liste prin append
static Lista append (Lista a, Lista b) {
if (a == null)
return b;
else
return adauga(a.info, append (a.next, b)) ;
}
a b
a1 a2 a3 b1 b2 b3 b4
concat(a,b}
Figura 11.6: Concatenarea a dou a liste prin concat
static Lista concat(Lista a, Lista b)
{
if (a == null)
return b;
else
{
Lista c = a;
while (c.next != null)
c = c.next;
c.next = b;
return a;
}
}
11.5. OPERATII ASUPRA LISTELOR 157
Aceasta ultim a procedur a se poate scrie recursiv:
static Lista concat (Lista a, Lista b) {
if (a == null)
return b;
else {
a.next = concat (a.next, c);
return a;
}
}
Procedura de calcul de oglindire a unei liste a consta n construirea unei
liste n care elementele listei a sunt n ordine invers a. Realizarea acestei proceduri
este un exercit iu clasic de programare asupra listelor. D am aici dou a solut i, una
iterativ a, cealalt a recursiv a, complexitatea este O(n
2
) deci patratica, dar clasic a.
Noua metoda nReverse modica argumentul s au, n timp ce Reverse nu-l modica
ci construieste o alta lista pentru rezultat.
static Lista nReverse (Lista a) {
Lista b = null;
while (a != null) {
Lista c = a.next;
a.next = b; b = a; a = c;
}
return b;
}
a
b
e1 e2 e3 e4 e5
c
nill
Figura 11.7: Transformarea listei prin inversarea leg aturilor
static Lista Reverse (Lista a) {
if (a == null)
return a;
else
return append(Reverse (a.next), adauga(a.info, null));
}
158 CAPITOLUL 11. LISTE
Se poate scrie o versiune iterativa a versiunii recursive, grat ie unei funct ii auxiliare
care acumuleaza rezultatul ntr-unul din argumentele sale:
static Lista nReverse (Lista a) {
return nReverse1(null, a);
}
static Lista nReverse1 (Lista b, Lista a) {
if (a == null)
return b;
else
return nReverse1(adauga(a.info, b), a.next);
}
Un alt exercit iu formator consta n a administra liste n care elementele sunt
aranjate n ordine crescatoare. Procedura de ad augare devine ceva mai complexa
pentru c a trebuie g asit a pozit ia celulei care trebuie ad augat a dup a parcurgerea
unei p art i a listei.
Nu trat am acest exercit iu decat n cazul listelor circulare cu gard a. Pentru o
astfel de list a, valoarea campului info din prima celul a nu are nici o semnicat ie
(este celula de gard a). C ampul next din ultima celul a cont ine adresa primei celule.
a
a1 a2 a3 a4 a5 a6
garda
Figura 11.8: List a circular a cu gard a
static Lista insereaza (int v, Lista a) {
Lista b = a;
while (b.next != a && v > head(b.next))
b = b.next;
b.next = adauga(v, b.next);
a.info = head(a) + 1;
return a;
}
Capitolul 12
Algoritmi divide et impera
12.1 Tehnica divide et impera
Divide et impera
1
este o tehnica de elaborare a algoritmilor care const a n:
Descompunerea repetat a a problemei (subproblemei) ce trebuie rezolvat a n
subprobleme mai mici.
Rezolvarea n acelasi mod (recursiv) a tuturor subproblemelor.
Compunerea subsolut iilor pentru a obt ine solut ia problemei (subproblemei)
init iale.
Descompunerea problemei (subproblemelor) se face pan a n momentul n care
se obt in subprobleme de dimensiuni atat de mici nc at au solut ie cunoscuta sau
pot rezolvate prin tehnici elementare.
Metoda poate descrisa astfel:
procedure divideEtImpera(P, n, S)
if (n n
0
)
then rezolv a subproblema P prin tehnici elementare
else
mparte P n P
1
, ..., P
a
de dimensiuni n
1
, ..., n
a
divideEtImpera(P
1
, n
1
, S
1
)
...
divideEtImpera(P
a
, n
a
, S
a
)
combin a S
1
, ..., S
a
pentru a obt ine S
Exemplele tipice de aplicare a acestei metode sunt algoritmii de parcurgere
a arborilor binari si algoritmul de cautare binar a.
1
divide-and-conquer, n engleza
159
160 CAPITOLUL 12. ALGORITMI DIVIDE ET IMPERA
12.2 Ordinul de complexitate
Vom presupune c a dimensiunea n
i
a subproblemei i satisface relat ia n
i
n
b
,
unde b > 1. Astfel, pasul de divizare reduce o subproblem a la altele de dimensiuni
mai mici, ceea ce asigura terminarea subprogramului recursiv.
Presupunem c a divizarea problemei n subprobleme si compunerea solut iilor
subproblemelor necesita un timp de ordinul O(n
k
). Atunci, complexitatea timp
T(n) a algoritmului divideEtImpera este dat a de relat ia de recurent a:
T(n) =
_
O(1) , daca n n
0
a T(
n
b
) +O(n
k
) , daca n > n
0
(12.2.1)
Teorema 3 Daca n > n
0
atunci:
T(n) =
_
_
O(n
log
b
a
) , daca a > b
k
O(n
k
log
b
n) , daca a = b
k
O(n
k
) , daca a < b
k
(12.2.2)
Demonstrat ie: Putem presupune, f ar a a restrange generalitatea, c a n = b
m
n
0
. De
asemenea, presupunem ca T(n) = c n
k
0
daca n n
0
si T(n) = a T(
n
b
) + c n
k
daca n > n
0
. Pentru n > n
0
avem:
T(n) = aT
_
n
b
_
+cn
k
= aT
_
b
m1
n
0
_
+cn
k
= a
_
aT
_
b
m2
n
0
_
+c
_
n
b
_
k
_
+cn
k
= a
2
T
_
b
m2
n
0
_
+c
_
a
_
n
b
_
k
+n
k
_
= ...
= a
m
T (n
0
) +c
_
a
m1
_
n
b
m1
_
k
+... +a
_
n
b
_
k
+n
k
_
= a
m
cn
k
0
+c
_
a
m1
b
k
n
k
0
+... +a
_
b
m1
_
k
n
k
0
+ (b
m
)
k
n
k
0
_
= cn
k
0
a
m
_
1 +
b
k
a
+... +
_
b
k
a
_
m
_
= ca
m
m
i=0
_
b
k
a
_
i
unde am notat cn
k
0
prin c. Distingem cazurile:
1. a > b
k
. Seria
m
i=0
_
b
k
a
_
i
este convergenta si deci sirul sumelor part iale este
convergent. De aici rezult a ca T(n) = O(a
m
) = O(a
log
b
n
) = O(n
log
b
a
)
12.3. EXEMPLE 161
2. a = b
k
. Rezult a ca a
m
= b
km
= cn
k
si de aici T(n) = O(n
k
m) = O(n
k
log
b
n).
3. a < b
k
. Avem T(n) = O(a
m
(
b
k
a
)
m
) = O(b
km
) = O(n
k
).
12.3 Exemple
Dintre problemele clasice care se pot rezolva prin metoda divide et impera
ment ion am:
cautare binar a
sortare rapid a (quickSort)
problema turnurilor din Hanoi
sortare prin interclasare
dreptunghi de arie maxim a n placa cu g auri
12.3.1 Sortare prin interclasare - MergeSort
Ideea este de a utiliza interclasarea n etapa de asamblare a solut iilor.
i=1
x
i
c
i
.
Pentru rezolvare vom folosi metoda greedy. O modalitate de a ajunge la
solut ia optim a este de a considera obiectele n ordinea descrescatoare a valorilor
utilit at ilor lor date de raportul
vi
gi
(i = 1, ..., n) si de a le nc arca ntregi n rucsac
p an a cand acesta se umple. Din aceasta cauza presupunem n continuare c a
v
1
g
1
v
2
g
2
...
v
n
g
n
.
Vectorul x = (x
1
, x
2
, ..., x
n
) se numeste solut ie posibil a daca
_
x
i
[0, 1], i = 1, 2, ..., n
n
i=1
x
i
g
i
G
iar o solut ie posibil a este solut ie optim a daca maximizeaza funct ia f.
Vom nota G
p
greutatea permisa de a se nc arca n rucsac la un moment dat.
Conform strategiei greedy, procedura de rezolvare a problemei este urmatoarea:
procedure rucsac()
G
p
G
for i = 1, n
if G
p
> g
i
then G
p
G
p
g
i
else
x
i
Gp
gi
for j = i + 1, n
x
j
0
Vectorul x are forma x = (1, ..., 1, x
i
, 0, ..., 0) cu x
i
(0, 1] si 1 i n.
Propozit ia 1 Procedura rucsac() furnizeaz a o solut ie optim a.
Demonstrat ia se gaseste, de exemplu, n [25] la pagina 226.
Capitolul 14
Metoda backtracking
Metoda backtracking se utilizeaz a pentru determinarea unei submult imi a
unui produs cartezian de forma S
1
S
2
... S
n
(cu mult imile S
k
nite) care
are anumite propriet at i. Fiecare element (s
1
, s
2
, ..., s
n
) al submult imii produsului
cartezian poate interpretat ca solut ie a unei probleme concrete.
In gur a este ilustrat modul de parcurgere a spat iului solut iilor n cazul
gener arii produsului cartezian0, 1
4
.
In aplicat iile concrete solut ia nu este obt inut a numai n cazul n care k = n
ci este posibil sa e satisfacut a o condit ie de gasire a solut iei pentru k < n (pentru
anumite probleme nu toate solut iile cont in acelasi num ar de elemente).
14.2.1 Bactracking iterativ
Structura general a a algoritmului este:
for(k=1;k<=n;k++) x[k]=gol; init iarizarea vectorului solut ie
k=1; pozit ionare pe prima component a
while (k>0) cat timp exist a componente de analizat
if (k==n+1) daca x este solut ie
as(x); k; atunci: asare solut ie si pas stanga
else altfel:
A
Astfel, decizia d
i
depinde de deciziile anterioare d
i+1
, ..., d
n
. Spunem c a se aplica
metoda nainte.
Daca d
1
, d
2
, ..., d
n
este un sir optim de decizii care duc la trecerea sistemului
din starea S
0
n starea S
n
, atunci pentru orice i (1 i n) d
1
, d
2
, ..., d
i
este un sir
optim de decizii care duc la trecerea sistemului din starea S
0
n starea S
i
. Astfel,
decizia d
i+1
depinde de deciziile anterioare d
1
, ..., d
i
. Spunem c a se aplica metoda
napoi.
Daca d
1
, d
2
, ..., d
n
este un sir optim de decizii care duc la trecerea sistemului
din starea S
0
n starea S
n
, atunci pentru orice i (1 i n) d
i+1
, d
i+2
, ..., d
n
este
un sir optim de decizii care duc la trecerea sistemului din starea S
i
n starea S
n
si
d
1
, d
2
, ..., d
i
este un sir optim de decizii care duc la trecerea sistemului din starea
S
0
n starea S
i
. Spunem c a se aplica metoda mixt a.
Indiferent de varianta de prezentare, rezolvarea prin programare dinamica
presupune gasirea si rezolvarea unui sistem de recurent e.
In prima variant a avem
recurent e ntre subprobleme, n a doua variant a avem recurent e n sirul de decizii.
In afara cazurilor n care recurent ele sunt evidente, este necesara si o justicare
sau o demonstrat ie a faptului c a aceste recurent e sunt cele corecte.
Deoarece rezolvarea prin recursivitate duce de cele mai multe ori la ordin de
complexitate exponent ial, se folosesc tabele auxiliare pentru ret inerea optimelor
part iale iar spat iul de solut ii se parcurge n ordinea crescatoare a dimensiunilor
subproblemelor. Folosirea acestor tabele da si numele tehnicii respective.
Aseman ator cu metoda divide et impera, programarea dinamica rezolva
problemele combin and solut iile unor subprobleme. Deosebirea const a n faptul c a
divide et impera partit ioneaza probleman subprobleme independente, le rezolv a
(de obicei recursiv) si apoi combin a solut iile lor pentru a rezolva problema initial a,
n timp ce programarea dinamica se aplica problemelor ale c aror subprobleme nu
sunt independente, ele av and sub-subprobleme comune.
In aceasta situat ie, se
rezolva ecare sub-subproblema o singur a dat a si se foloseste un tablou pentru
a memora solut ia ei, evitandu-se recalcularea ei de c ate ori subproblema reapare.
Programarea dinamica se aplica problemelor care au mai multe solut ii, ecare
din ele cu c ate o valoare, si la care se doreste obt inerea unei solut ii optime (minime
sau maxime).
Algoritmul pentru rezolvarea unei probleme folosind programarea dinamica
se dezvolta n 4 etape:
1. caracterizarea unei solut ii optime (identicarea unei modalit ati optime de
rezolvare, care satisface una dintre formele principiului optimalit at ii)
2. denirea recursiv a a valorii unei solut ii optime (dar nu se implementeaz a
recursiv)
3. calculul efectiv al valorii unei solut ii optime folosind o structur a de date
(de obicei tablou)
4. reconstituirea unei solut ii pe baza informat iei calculate.
15.2. PROBLEME REZOLVATE 213
15.2 Probleme rezolvate
15.2.1 Inmult irea optimala a matricelor
Consider am n matrice A
1
, A
2
, ..., A
n
, de dimensiuni d
0
d
1
, d
1
d
2
, ...,
d
n1
d
n
. Produsul A
1
A
2
... A
n
se poate calcula n diverse moduri, aplic and
asociativitatea operat iei de nmultire a matricelor.
Numim nmult ire elementar a nmult irea a dou a elemente.
A
3. Pentru ca parantezarea s a e optimal a, parantezarea produselor A
1
A
2
... A
k
si A
k+1
... A
n
trebuie sa e de asemenea optimala. Prin urmare
elementele matricei M trebuie sa satisfaca urmatoarea relat ie de recurent a:
M[i][i] = 0, i = 1, 2, ..., n.
M[i][j] = min
ik<j
M[i][k] +M[k + 1][j] +d[i 1] d[k] d[j]
Cum interpret am aceasta relat ie de recurent a? Pentru a determina num arul
minim de nmult iri elementare pentru calculul produsului A
i
A
i+1
...A
j
,
x am pozit ia de parantezare k n toate modurile posibile (ntre i si j 1), si
alegem varianta care ne conduce la minim. Pentru o pozit ie k xata, costul
parantezarii este egal cu numarul de nmult iri elementare necesare pentru
calculul produsului A
i
A
i+1
...A
k
, la care se adauga num arul denmult iri
elementare necesare pentru calculul produsului A
k+1
... A
j
si costul
nmult irii celor doua matrice rezultate (d
i1
d
k
d
j
).
Observ am ca numai jum atatea de deasupra diagonalei principale din M este
utilizat a. Pentru a construi solut ia optim a este utila si ret inerea indicelui k,
pentru care se obtine minimul. Nu vom considera un alt tablou, ci-l vom
ret ine, pe pozit ia simetric a fat a de diagonala principala (M[j][i]).
4. Rezolvarea recursiva a relat iei de recurent a este inecienta, datorit a faptu-
lui c a subproblemele se suprapun, deci o abordare recursiv a ar conduce la
rezolvarea aceleiasi subprobleme de mai multe ori. Prin urmare vom rezolva
relat ia de recurent a n mod bottom-up: (determin am parantezarea optimal a
a produselor de doua matrice, apoi de 3 matrice, 4 matrice, etc).
long M[NMax][NMax];
void dinamic()
{
int nr, i, j, k, kmin;
long min, Infinit=1000000000;
for(nr=2; nr<=n; nr++) //nr=cate matrice se inmultesc
for (i=1; i<=n-nr+1; i++)
{
j=i+nr-1;
//se inmultesc nr matrice, de la Ai la Aj
for (k=i, min=Infinit; k<j; k++)
//determin minimul si pozitia sa
if (min>M[i][k]+M[k+1][j]+d[i-1]*d[k]*d[j])
{
min=M[i][k]+M[k+1][j]+d[i-1]*d[k]*d[j];
kmin=k;
}
15.2. PROBLEME REZOLVATE 215
M[i][j]=min;
M[j][i]=kmin;
}
}
Reconstituirea solut iei optime se face foarte usor n mod recursiv, utilizand
informat iile ret inute sub diagonala principal a n matricea M:
void afisare(int i, int j)
{
//afiseaza parantezarea optimala a produsului Aix...xAj
if (i==M[j][i]) cout<<"A"<<i;
else
{
cout<<"(";
afisare(i,M[j][i]);
cout<<")";
}
cout<<"x";
if (j==M[j][i]+1) cout<<"A"<<j;
else {cout<<"("; afisare(M[j][i]+1,j); cout<<")";}
}
15.2.2 Subsir crescator maximal
Fie un sir A = (a
1
, a
2
, ..., a
n
). Numim subsir al sirului A o succesiune de
elemente din A, n ordinea n care acestea apar n A:
a
i1
, a
i2
, ..., a
i
k
, unde 1 i
1
< i
2
< ... < i
k
n.
Se cere determinarea unui subsir crescator al sirului A, de lungime maxim a.
De exemplu, pentru
A = (8, 3, 6, 50, 10, 8, 100, 30, 60, 40, 80)
o solut ie poate
(3, 6, 10, 30, 60, 80).
Rezolvare
1. Fie A
i1
= (a
i1
a
i2
... a
i
k
) cel mai lung subsir crescator al
sirului A. Observ am ca el coincide cu cel mai lung subsir crescator al sirului
(a
i1
, a
i1+1
, ..., a
n
). Evident A
i2
= (a
i2
a
i3
... a
i
k
) este cel mai lung subsir
crescator al lui (a
i2
, a
i2+1
, ..., a
n
), etc. Prin urmare, o subproblema a problemei
init iale consta n determinarea celui mai lung subsir crescator care ncepe cu a
i
,
i = 1, .., n.
216 CAPITOLUL 15. PROGRAMARE DINAMIC
A
Subproblemele nu sunt independente: pentru a determina cel mai lung subsir
crescator care ncepe cu a
i
, este necesar s a determinam cele mai lungi subsiruri
crescatoare care ncep cu a
j
, a
i
a
j
, j = i + 1, .., n.
2. Pentru a ret ine solut iile subproblemelor vom considera doi vectori l si poz,
ecare cu n componente, avand semnicat ia:
l[i] =lungimea celui mai lung subsir crescator care ncepe cu a[i];
poz[i] =pozit ia elementului care urmeaza dup a a[i] n cel mai lung subsir
crescator care ncepe cu a[i], dac a un astfel de element exista, sau 1 daca un
astfel de element nu exista.
3. Relat ia de recurent a care caracterizeaza substructura optimal a a problemei
este:
l[n] = 1; poz[n] = 1;
l[i] = max
j=i+1,n
1 +l[j][a[i] a[j]
unde poz[i] = indicele j pentru care se obt ine maximul l[i].
Rezolvam relat ia de recurent a n mod bottom-up:
int i, j;
l[n]=1;
poz[n]=-1;
for (i=n-1; i>0; i--)
for (l[i]=1, poz[i]=-1, j=i+1; j<=n; j++)
if (a[i] <= a[j] && l[i]<1+l[j])
{
l[i]=1+l[j];
poz[i]=j;
}
Pentru a determina solut ia optim a a problemei, determinam valoarea maxima
din vectorul l, apoi asam solut ia, ncepand cu pozit ia maximului si utiliz and
informat iile memorate n vectorul poz:
//determin maximul din vectorul l
int max=l[1], pozmax=1;
for (int i=2; i<=n; i++)
if (max<l[i])
{
max=l[i]; pozmax=i;
}
cout<<"Lungimea celui mai lung subsir crescator: " <<max;
cout<<"\nCel mai lung subsir:\n";
for (i=pozmax; i!=-1; i=poz[i]) cout<<a[i]<< ;
Secvent ele de program de mai sus sunt scrise n c/C++.
15.2. PROBLEME REZOLVATE 217
Programele urmatoare sunt scrise n Java:
import java.io.*;
class SubsirCrescatorNumere
{
static int n;
static int[] a;
static int[] lg;
static int[] poz;
public static void main(String []args) throws IOException
{
int i,j;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("SubsirCrescatorNumere.in")));
PrintWriter out = new PrintWriter (
new BufferedWriter( new FileWriter("SubsirCrescatorNumere.out")));
st.nextToken();n=(int)st.nval;
a=new int[n+1];
lg=new int[n+1];
poz=new int[n+1];
for(i=1;i<=n;i++) { st.nextToken(); a[i]=(int)st.nval; }
int max,jmax;
lg[n]=1;
for(i=n-1;i>=1;i--)
{
max=0;
jmax=0;
for(j=i+1;j<=n;j++)
if((a[i]<=a[j])&&(max<lg[j])) { max=lg[j]; jmax=j; }
if(max!=0) { lg[i]=1+max; poz[i]=jmax; }
else lg[i]=1;
}
max=0; jmax=0;
for(j=1;j<=n;j++)
if(max<lg[j]){ max=lg[j]; jmax=j; }
out.println(max);
int k;
j=jmax;
for(k=1;k<=max;k++) { out.print(a[j]+" "); j=poz[j]; }
out.close();
}//main
}//class
218 CAPITOLUL 15. PROGRAMARE DINAMIC
A
import java.io.*;
class SubsirCrescatorLitere
{
public static void main(String[] args) throws IOException
{
int n,i,j,jmax,k,lmax=-1,pozmax=-1;
int [] l,poz;
String a;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("SubsirCrescatorLitere.in")));
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("SubsirCrescatorLitere.out")));
st.nextToken();
a=st.sval;
n=a.length();
out.println(a+" "+n);
l=new int[n];//l[i]=lg.celui mai lung subsir care incepe cu a[i]
poz=new int[n];//poz[i]=pozitia elementului care urmeaza dupa a[i]
for(i=0;i<n;i++) poz[i]=-1;
l[n-1]=1;
poz[n-1]=-1;
for(i=n-2;i>=0;i--)
{
jmax=i;
for(j=i+1;j<n;j++)
if((a.charAt(i)<=a.charAt(j))&&(1+l[j]>1+l[jmax])) jmax=j;
l[i]=1+l[jmax];
poz[i]=jmax;
if(l[i]>lmax) { lmax=l[i]; pozmax=i; }
}
out.print("Solutia ");
k=pozmax;
out.print("("+l[pozmax]+") : ");
for(j=1;j<=lmax;j++)
{
out.print(a.charAt(k));
k=poz[k];
}
out.close();
} // main
}// class
15.2. PROBLEME REZOLVATE 219
15.2.3 Suma n triunghi
S a consideram un triunghi format din n linii (1 < n 100), ecare linie
cont in and numere ntregi din domeniul [1, 99], ca n exemplul urmator:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Tabelul 15.1: Triunghi de numere
Problema const a n scrierea unui program care sa determine cea mai mare
suma de numere aate pe un drum ntre num arul de pe prima linie si un num ar
de pe ultima linie. Fiecare num ar din acest drum este situat sub precedentul, la
stanga sau la dreapta acestuia. (IOI, Suedia 1994)
Rezolvare
1. Vom ret ine triunghiul ntr-o matrice patratica T, de ordin n, sub diagonala
principal a. Subproblemele problemei date constau n determinarea sumei maxime
care se poate obt ine din numere aate pe un drumntre numarul T[i][j], p an a la un
num ar de pe ultima linie, ecare num ar din acest drum ind situat sub precedentul,
la stanga sau la dreapta sa. Evident, subproblemele nu sunt independente: pentru
a calcula suma maxima a numerelor de pe un drum de la T[i][j] la ultima linie,
trebuie sa calcul am suma maxima a numerelor de pe un drum de la T[i + 1][j] la
ultima linie si suma maxima a numerelor de pe un drum de la T[i + 1][j + 1] la
ultima linie.
2. Pentru a ret ine solut iile subproblemelor, vom utiliza o matrice S, p atratica
de ordin n, cu semnicat ia
S[i][j] = suma maxima ce se poate obt ine pe un drum de la T[i][j] la un
element de pe ultima linie, respect and condit iile problemei.
Evident, solut ia problemei va S[1][1].
3. Relat ia de recurent a care caracterizeaza substructura optimal a a problemei
este:
S[n][i] = T[n][i], i = 1, 2, ..., n
S[i][j] = T[i][j] + maxS[i + 1][j], S[i + 1][j + 1]
4. Rezolv am relat ia de recurent a n mod bottom-up:
int i, j;
for (i=1; i<=n; i++) S[n][i]=T[n][i];
for (i=n-1; i>0; i--)
for (j=1; j<=i; j++)
220 CAPITOLUL 15. PROGRAMARE DINAMIC
A
{
S[i][j]=T[i][j]+S[i+1][j];
if (S[i+1][j]<S[i+1][j+1])
S[i][j]=T[i][j]+S[i+1][j+1]);
}
Exercit iu: Asat i si drumul n triunghi pentru care se obt ine solut ia optim a.
15.2.4 Subsir comun maximal
Fie X = (x
1
, x
2
, ..., x
n
) si Y = (y
1
, y
2
, ..., y
m
) dou a siruri de n, respectiv m
numere ntregi. Determinat i un subsir comun de lungime maxima.
Exemplu
Pentru X = (2, 5, 5, 6, 2, 8, 4, 0, 1, 3, 5, 8) si Y = (6, 2, 5, 6, 5, 5, 4, 3, 5, 8) o
solut ie posibil a este: Z = (2, 5, 5, 4, 3, 5, 8).
Solutie
1. Not am cu X
k
= (x
1
, x
2
, ..., x
k
) (prexul lui X de lungime k) si cu Y
h
=
(y
1
, y
2
, ..., y
h
) prexul lui Y de lungime h. O subproblema a problemei date consta
n determinarea celui mai lung subsir comun al lui X
k
, Y
h
. Notam cu LCS(X
k
, Y
h
)
lungimea celui mai lung subsir comun al lui X
k
, Y
h
.
Utiliz and aceste notat ii, problema cere determinarea LCS(X
n
, Y
m
), precum
si un astfel de subsir.
Observat ie
1. Dac a X
k
= Y
h
atunci
LCS(X
k
, Y
h
) = 1 +LCS(X
k1
, Y
h1
).
2. Dac a Xk ,= Y h atunci
LCS(Xk, Y h) = max(LCS(X
k1
, Y
h
), LCS(X
k
, Y
h1
)).
Din observat ia precedenta deducem c a subproblemele problemei date nu sunt
independente si ca problema are substructura optimal a.
2. Pentru a ret ine solut iile subproblemelor vom utiliza o matrice cu n+1 linii
si m+1 coloane, denumita lcs. Linia si coloana 0 sunt utilizate pentru init ializare
cu 0, iar elementul lcs[k][h] va lungimea celui mai lung subsir comun al sirurilor
X
k
si Y
h
.
3. Vom caracteriza substructura optimal a a problemei prin urm atoarea relat ie
de recurent a:
lcs[k][0] = lcs[0][h] = 0, k = 1, 2, .., n, h = 1, 2, .., m
lcs[k][h] = 1 +lcs[k 1][h 1], daca x[k] = y[h]
max lcs[k][h 1], lcs[k 1][h], daca x[k] <> y[h]
Rezolvam relat ia de recurent a n mod bottom-up:
15.2. PROBLEME REZOLVATE 221
for (int k=1; k<=n; k++)
for (int h=1; h<=m; h++)
if (x[k]==y[h])
lcs[k][h]=1+lcs[k-1][h-1];
else
if (lcs[k-1][h]>lcs[k][h-1])
lcs[k][h]=lcs[k-1][h];
else
lcs[k][h]=lcs[k][h-1];
Deoarece nu am utilizat o structur a de date suplimentar a cu ajutorul c areia
sa memoram solut ia optim a, vom reconstitui solut ia optim a pe baza rezultatelor
memorate n matricea lcs. Prin reconstituire vom obt ine solut ia n ordine invers a,
din acest motiv vom memora solutia ntr-un vector, pe care l vom asa de la
sfarsit catre nceput:
cout<<"Lungimea subsirului comun maximal: " <<lcs[n][m];
int d[100];
cout<<"\nCel mai lung subsir comun este: \n";
for (int i=0, k=n, h=m; lcs[k][h]; )
if (x[k]==y[h])
{
d[i++]=x[k];
k--;
h--;
}
else
if (lcs[k][h]==lcs[k-1][h])
k--;
else
h--;
for (k=i-1;k>=0; k--)
cout<< d[k] << ;
Secvent ele de cod de mai sus sunt scrise n C/C++. Programul urm ator este
scris n Java si determin a toate solut iile. Sunt determinate cea mai mica si cea mai
mare solut ie, n ordine lexicograca. De asemenea sunt asate matricele auxiliare
de lucru pentru a se putea urm arii mai usor modalitatea de determinare recursiv a
a tuturor solut iilor.
import java.io.*; // SubsirComunMaximal
class scm
{
static PrintWriter out;
static int [][] a;
222 CAPITOLUL 15. PROGRAMARE DINAMIC
A
static char [][] d;
static String x,y;
static char[] z,zmin,zmax;
static int nsol=0,n,m;
static final char sus=|, stanga=-, diag=*;
public static void main(String[] args) throws IOException
{
citire();
n=x.length();
m=y.length();
out=new PrintWriter(new BufferedWriter( new FileWriter("scm.out")));
int i,j;
matrad();
out.println(a[n][m]);
afism(a);
afism(d);
z=new char[a[n][m]+1];
zmin=new char[z.length];
zmax=new char[z.length];
System.out.println("O solutie oarecare");
osol(n,m);// o solutie
System.out.println("\nToate solutiile");
toatesol(n,m,a[n][m]);// toate solutiile
out.println(nsol);
System.out.print("SOLmin = ");
afisv(zmin);
System.out.print("SOLmax = ");
afisv(zmax);
out.close();
System.out.println("\n... Gata !!! ...");
}
static void citire() throws IOException
{
BufferedReader br=new BufferedReader(new FileReader("scm.in"));
x=br.readLine();
y=br.readLine();
}
15.2. PROBLEME REZOLVATE 223
static void matrad()
{
int i,j;
a=new int[n+1][m+1];
d=new char[n+1][m+1];
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(x.charAt(i-1)==y.charAt(j-1)) // x.charAt(i)==y.charAt(j) !
{
a[i][j]=1+a[i-1][j-1];
d[i][j]=diag;
}
else
{
a[i][j]=max(a[i-1][j],a[i][j-1]);
if(a[i-1][j]>a[i][j-1]) d[i][j]=sus; else d[i][j]=stanga;
}
}
static void osol(int lin, int col)
{
if((lin==0)||(col==0)) return;
if(d[lin][col]==diag)
osol(lin-1,col-1);
else
if(d[lin][col]==sus)
osol(lin-1,col);
else osol(lin,col-1);
if(d[lin][col]==diag) System.out.print(x.charAt(lin-1));
}
static void toatesol(int lin, int col,int k)
{
int i,j;
if(k==0)
{
System.out.print(++nsol+" ");
afisv(z);
zminmax();
return;
}
i=lin+1;
224 CAPITOLUL 15. PROGRAMARE DINAMIC
A
while(a[i-1][col]==k)//merg in sus
{
i--;
j=col+1;
while(a[i][j-1]==k) j--;
if( (a[i][j-1]==k-1)&&(a[i-1][j-1]==k-1)&&(a[i-1][j]==k-1))
{
z[k]=x.charAt(i-1);
toatesol(i-1,j-1,k-1);
}
}//while
}
static void zminmax()
{
if(nsol==1)
{
copiez(z,zmin);
copiez(z,zmax);
}
else
if(compar(z,zmin)<0)
copiez(z,zmin);
else
if(compar(z,zmax)>0) copiez(z,zmax);
}
static int compar(char[] z1, char[] z2)//-1=<; 0=identice; 1=>
{
int i=1;
while(z1[i]==z2[i]) i++;// z1 si z2 au n componente 1..n
if(i>n)
return 0;
else
if(z1[i]<z2[i])
return -1;
else return 1;
}
static void copiez(char[] z1, char[] z2)
{
int i;
for(i=1;i<z1.length;i++) z2[i]=z1[i];
}
15.2. PROBLEME REZOLVATE 225
static int max(int a, int b)
{
if(a>b) return a; else return b;
}
static void afism(int[][]a)// difera tipul parametrului !!!
{
int n=a.length;
int i,j,m;
m=y.length();
System.out.print(" ");
for(j=0;j<m;j++) System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++) System.out.print(a[0][j]+" ");
System.out.println();
for(i=1;i<n;i++)
{
System.out.print(x.charAt(i-1)+" ");
m=a[i].length;
for(j=0;j<m;j++) System.out.print(a[i][j]+" ");
System.out.println();
}
System.out.println("\n");
}
static void afism(char[][]d)// difera tipul parametrului !!!
{
int n=d.length;
int i,j,m;
m=y.length();
System.out.print(" ");
for(j=0;j<m;j++) System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++) System.out.print(d[0][j]+" ");
System.out.println();
226 CAPITOLUL 15. PROGRAMARE DINAMIC
A
for(i=1;i<n;i++)
{
System.out.print(x.charAt(i-1)+" ");
m=d[i].length;
for(j=0;j<m;j++) System.out.print(d[i][j]+" ");
System.out.println();
}
System.out.println("\n");
}
static void afisv(char[]v)
{
int i;
for(i=1;i<=v.length-1;i++) System.out.print(v[i]);
for(i=1;i<=v.length-1;i++) out.print(v[i]);
System.out.println();
out.println();
}
}
Pe ecran apar urm atoarele rezultate:
1 3 2 4 6 5 a c b d f e
0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 1 1 1 1 1 1 1 1 1 1 1 1
2 0 1 1 2 2 2 2 2 2 2 2 2 2
3 0 1 2 2 2 2 2 2 2 2 2 2 2
4 0 1 2 2 3 3 3 3 3 3 3 3 3
5 0 1 2 2 3 3 4 4 4 4 4 4 4
6 0 1 2 2 3 4 4 4 4 4 4 4 4
a 0 1 2 2 3 4 4 5 5 5 5 5 5
b 0 1 2 2 3 4 4 5 5 6 6 6 6
c 0 1 2 2 3 4 4 5 6 6 6 6 6
d 0 1 2 2 3 4 4 5 6 6 7 7 7
e 0 1 2 2 3 4 4 5 6 6 7 7 8
f 0 1 2 2 3 4 4 5 6 6 7 8 8
1 3 2 4 6 5 a c b d f e
1 * - - - - - - - - - - -
2 | - * - - - - - - - - -
3 | * - - - - - - - - - -
15.2. PROBLEME REZOLVATE 227
4 | | - * - - - - - - - -
5 | | - | - * - - - - - -
6 | | - | * - - - - - - -
a | | - | | - * - - - - -
b | | - | | - | - * - - -
c | | - | | - | * - - - -
d | | - | | - | | - * - -
e | | - | | - | | - | - *
f | | - | | - | | - | * -
O solutie oarecare
1346acdf
Toate solutiile
1 1346acdf
2 1246acdf
3 1345acdf
4 1245acdf
5 1346abdf
6 1246abdf
7 1345abdf
8 1245abdf
9 1346acde
10 1246acde
11 1345acde
12 1245acde
13 1346abde
14 1246abde
15 1345abde
16 1245abde
SOLmin = 1245abde
SOLmax = 1346acdf
... Gata !!! ...
Press any key to continue...
228 CAPITOLUL 15. PROGRAMARE DINAMIC
A
Capitolul 16
Probleme
16.1 Probleme rezolvate
16.1.1 Poarta - OJI 2002
Se consider a harta universului ca ind o matrice cu 250 de linii si 250 de
coloane.
In ecare celul a se gaseste o asa numit a poart a stelara, iar n anumite
celule se gasesc echipaje ale port ii stelare.
La o deplasare, un echipaj se poate deplasa din locul n care se aa n oricare
alt loc n care se gaseste o a doua poart a, n cazul nostru n orice alt a pozit ie din
matrice.
Nu se permite situarea simultan a a mai mult de un echipaj ntr-o celul a. La
un moment dat un singur echipaj se poate deplasa de la o poart a stelara la alta.
Cerint a
D andu-se un num ar p (1 < p < 5000) de echipaje, pentru ecare echipaj ind
precizate pozit ia init ial a si pozit ia nal a, determinat i num arul minim de deplas ari
necesare pentru ca toate echipajele sa ajung a din pozit ia init ial a n cea nal a.
Datele de intrare
Se citesc din sierul text poarta.in n urm atorul format:
pe prima linie num arul natural p reprezentand num arul de echipaje,
pe urm atoarele p linii c ate 4 numere naturale, primele dou a reprezentand
coordonatele pozit iei init iale a unui echipaj (linie coloan a), urm atoarele doua
reprezentand coordonatele pozit iei nale a aceluiasi echipaj (linie coloan a).
Datele de iesire
Pe prima linie a sierului text poarta.out se scrie un singur numar reprezentand
num arul minim de deplas ari necesar.
229
230 CAPITOLUL 16. PROBLEME
Restrict ii si precizari
coordonatele pozit iilor init iale si nale ale echipajelor sunt numere naturale
din intervalul [1, 250];
pozit iile init iale ale celor p echipaje sunt distincte doua cate dou a;
pozit iile nale ale celor p echipaje sunt distincte doua cate dou a.
Exemplu
poarta.in poarta.out
3 4
1 2 3 4
6 5 3 9
3 4 1 2
Timp maxim de executare: 1 secunda/test
import java.io.*; // NrMinDeplasari=p+NrCicluri-NrStationare
class poarta
{
static int p,nmd,nc=0,ns=0;
static int[] xi,yi,xf,yf;
static boolean[] ea; // EsteAnalizat deja
public static void main(String[] args) throws IOException
{
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("poarta10.in")));
st.nextToken(); p=(int)st.nval;
xi=new int[p+1];
yi=new int[p+1];
xf=new int[p+1];
yf=new int[p+1];
ea=new boolean[p+1]; // EchipajAnalizat FALSE !!!
int i;
for(i=1;i<=p;i++)
{
st.nextToken(); xi[i]=(int)st.nval;
16.1. PROBLEME REZOLVATE 231
st.nextToken(); yi[i]=(int)st.nval;
st.nextToken(); xf[i]=(int)st.nval;
st.nextToken(); yf[i]=(int)st.nval;
}
for(i=1;i<=p;i++)
{
if(ea[i]) continue;
if((xf[i]==xi[i])&&(yf[i]==yi[i])) { ea[i]=true; ns++;}
else if(ciclu(i)) nc++;
}
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("poarta.out")));
nmd=p+nc-ns;
System.out.println(p+" "+nc+" "+ns+" "+nmd);
out.print(nmd);
out.close();
}
static boolean ciclu(int i)
{
int j=succesor(i);
while((j!=-1)&&(j!=i))
{
ea[j]=true;
j=succesor(j);
}
if(j==i) return true; else return false;
}
static int succesor(int j) // j --> k
{
int k;
for(k=1;k<=p;k++)
if((xf[j]==xi[k])&&(yf[j]==yi[k])) return k;
return -1;
}
}
16.1.2 Mouse - OJI 2002
Un experiment urmareste comportarea unui soricel pus ntr-o cutie dreptun-
ghiular a, mp art ita n m n camarut e egale de forma p atrat a. Fiecare c amarut a
cont ine o anumit a cantitate de hran a.
232 CAPITOLUL 16. PROBLEME
Soricelul trebuie sa porneasca din colt ul (1, 1) al cutiei si sa ajung a n colt ul
opus, m anc and c at mai mult a hran a. El poate trece dintr-o camer an una al aturat a
(dou a camere sunt alaturate dac a au un perete comun), m an anca toat a hrana din
camarut a atunci c and intr a si nu intr a niciodata ntr-o camer a f ar a hran a.
Cerint a
Stabilit i care este cantitatea maxima de hran a pe care o poate manca si
traseul pe care l poate urma pentru a culege aceast a cantitate maxima.
Datele de intrare
Fisierul de intrare mouse.in cont ine pe prima linie dou a numere m si n
reprezentand num arul de linii respectiv num arul de coloane ale cutiei, iar pe
urm atoarele m linii cele m n numere reprezentand cantitatea de hran a exis-
tent a n ecare camarut a, cate n numere pe ecare linie, separate prin spat ii.
Toate valorile din sier sunt numere naturale ntre 1 si 100.
Datele de iesire
x
1
...x
n
exte un numar natural.
In sierul de iesire sir.out se va asa, pe prima linie, num arul cerut urmat
de caracterul sfarsit de linie.
Restrict ii:
3 n 20000
3 k n
Exemple:
sir.in sir.out
3 1
4 3
5 7
Timp execut ie: 1 sec/test
16.1. PROBLEME REZOLVATE 255
import java.io.*;
class sir
{
public static void main(String []args) throws IOException
{
int ns=0,n=19999,r,k,i;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("sir.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("sir.out")));
st.nextToken(); n=(int)st.nval;
ns=0;
for(r=1;r<=(n-1)/2;r++)
for(k=3;k<=(n-1+r)/r;k++)
ns=ns+n-(k-1)*r;
System.out.println(ns);
out.println(ns);
out.close();
}
}
16.1.13 Triangulat ii - OJI 2002
O triangulat ie a unui poligon convex este o mult ime formata din diagonale
ale poligonului care nu se intersecteaz a n interiorul poligonului ci numai n v arfuri
si care mpart toat a suprafat a poligonului n triunghiuri.
Fiind dat un poligon cu n v arfuri notate 1, 2, ..., n sa se genereze toate
triangulat iile distincte ale poligonului. Dou a triangulat ii sunt distincte daca difer a
prin cel put in o diagonal a.
Date de intrare: n sierul text TRIANG.IN se a a pe prima linie un singur
num ar natural reprezent and valoarea lui n (n 11).
Date de iesire: n sierul text TRIANG.OUT se vor scrie:
- pe prima linie, num arul de triangulat ii distincte;
- pe ecare din urm atoarele linii cate o triangulat ie descrisa prin diagonalele
ce o compun. O diagonala va precizata prin dou a numere reprezentand cele dou a
v arfuri care o denesc; cele dou a numere ce denesc o diagonal a se despart prin
cel put in un spat iu, iar ntre perechile de numere ce reprezinta diagonalele dintr-o
triangulat ie se va lasa de asemenea minimum un spat iu.
Exemplu:
TRIANG.IN
5
TRIANG.OUT
5
256 CAPITOLUL 16. PROBLEME
1 3 1 4
2 4 2 5
5 2 5 3
3 5 3 1
4 2 1 4
Timp maxim de executare:
7 secunde/test pe un calculator la 133 MHz.
3 secunde/test pe un calculator la peste 500 MHz.
class Triangulatie
{
static int nv=5; // numar varfuri poligon
static int n=nv-3; // numar diagonale in triangulatie
static int m=nv*(nv-3)/2; // numarul tuturor diagonalelor
static int[] x=new int[n+1];
static int[] v1=new int[m+3],v2=new int[m+3];
static int nsol=0;
static void afisd()
{
int i;
System.out.print(++nsol+" ==> ");
for(i=1;i<=n;i++) System.out.print(v1[x[i]]+" "+v2[x[i]]+" ");
System.out.println();
}
static void afisx()
{
int i;
System.out.print(++nsol+" ==> ");
for(i=1;i<=n;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void diagonale()
{
int i,j,k=0;
i=1;
for(j=3;j<=nv-1;j++) {v1[++k]=i; v2[k]=j;}
for(i=2;i<=nv-2;i++)
for(j=i+2;j<=nv;j++){v1[++k]=i; v2[k]=j;}
}// diagonale()
static boolean seIntersecteaza(int k, int i)
16.1. PROBLEME REZOLVATE 257
{
int j; // i si x[j] sunt diagonalele !!!
for(j=1;j<=k-1;j++)
if(((v1[x[j]]<v1[i])&&(v1[i]<v2[x[j]])&&(v2[x[j]]<v2[i]))||
((v1[i]<v1[x[j]])&&(v1[x[j]]<v2[i])&&(v2[i]<v2[x[j]]))
) return true;
return false;
}
static void f(int k)
{
int i;
for (i=x[k-1]+1; i<=m-n+k; i++)
{
if(seIntersecteaza(k,i)) continue;
x[k]=i;
if(k<n) f(k+1);
// else afisx();
else afisd();
}
}
public static void main(String[] args)
{
diagonale();
f(1);
}
}// class
16.1.14 Spirala - OJI 2003
Se consider a un automat de criptare format dintr-un tablou cu n linii si n
coloane, tablou ce cont ine toate numerele de la 1 la n
2
asezate serpuit pe linii,
de la prima la ultima linie, pe liniile impare pornind de la st anga catre dreapta,
iar pe cele pare de la dreapta catre stanga (ca n gur a).
1 2 3 4
8 7 6 5
9 10 11 12
16 15 14 13
Numim amestecare operat ia de desfasurare n spiral a a valorilor din tablou
n ordinea indicat a de saget i si de reasezare a acestora n acelasi tablou, serpuit
pe linii ca si n cazul precedent.
258 CAPITOLUL 16. PROBLEME
1 2 3 4
14 13 12 5
15 16 9 8
10 11 6 7
De exemplu, desfasurarea tabloului conduce la sirul: 1 2 3 4 5 12 13 14 15
16 9 8 7 6 11 10, iar reasezarea acestuia n tablou conduce la obt inerea unui nou
tablou reprezentat n cea de-a doua gura.
Dup a orice operat ie de amestecare se poate relua procedeul, efectuand o nou a
amestecare. S-a observat un fapt interesant: c a dup a un num ar de amestecari, unele
valori ajung din nou n pozit ia init ial a (pe care o aveau n tabloul de pornire). De
exemplu, dup a dou a amestecari, tabloul de 4x4 cont ine 9 dintre elementele sale n
exact aceeasi pozit ie n care se aau init ial (vezi elemente marcate din gura).
1 2 3 4
6 7 8 5
11 10 15 14
16 9 12 13
Cerint a
Pentru n si k citite, scriet i un program care s a determine numarul minim de
amestecari ale unui tablou de n linii necesar pentru a ajunge la un tablou cu exact
k elemente aate din nou n pozit ia init ial a.
Date de intrare
Fisierul de intrare spirala.in cont ine pe prima linie cele doua numere n si k
despart ite printr-un spat iu.
Date de iesire
Fisierul de iesire spirala.out cont ine o singur a linie pe care se a a num arul
de amestecari determinat.
Restrict ii si precizari
3 N 50
Datele de intrare sunt alese astfel nc at num arul minim de amestecari necesare
sa nu dep aseasca 2 10
9
Exemple
spirala.in spirala.out spirala.in spirala.out
4 9 2 6 36 330
Timp maxim de executare/test: 1 secunda
import java.io.*;
class spirala
{
static int n,k;
static int[] pr;
static int[] p;
public static void main(String[] args) throws IOException
{
16.1. PROBLEME REZOLVATE 259
int r=0,i;
StreamTokenizer st=new StreamTokenizer(new BufferedReader(
new FileReader("spirala.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("spirala.out")));
st.nextToken();n=(int)st.nval;
st.nextToken();k=(int)st.nval;
p=new int[n*n+1];
pr=new int[n*n+1];
constrp();
r=1;
for(i=1;i<=n*n;i++) pr[i]=p[i]; // p^1
// afisv(p); afisv(pr); System.out.println("------------");
while(!arekfixe()&&(r<21))
{
r++;
pr=inmp(pr,p);
// System.out.print("\nr="+r); afisv(pr);
}
out.println(r);
out.close();
}
static void constrp()
{
int i,j,k,v,kp=0;
int[][] a=new int[n+1][n+1];
i=1; v=0;
for(k=1;k<=n/2;k++)
{
for(j=1;j<=n;j++) a[i][j]=++v;
for(j=n;j>=1;j--) a[i+1][j]=++v;
i=i+2;
}
if(n%2==1)
for(j=1;j<=n;j++) a[n][j]=++v;
// afism(a);
for(k=1;k<=n/2;k++) // dreptunghiul k
{
i=k;
for(j=k;j<=n+1-k;j++) p[++kp]=a[i][j];
j=n+1-k;
for(i=k+1;i<=n-k;i++) p[++kp]=a[i][j];
260 CAPITOLUL 16. PROBLEME
i=n+1-k;
for(j=n+1-k;j>=k;j--) p[++kp]=a[i][j];
j=k;
for(i=n-k;i>=k+1;i--) p[++kp]=a[i][j];
}
// afisv(p);
}
static int[] inmp(int[]a,int[]b)
{
int k;
int[] c=new int[n*n+1];
for(k=1;k<=n*n;k++) c[k]=a[b[k]];
return c;
}
static boolean arekfixe()
{
int i, s=0;
for(i=1;i<=n*n;i++) if(pr[i]==i) s++;
System.out.println("s="+s);
if(s==k) return true; else return false;
}
static void afism(int[][] x)
{
int i,j;
for(i=1;i<=n;i++)
{
System.out.println();
for(j=1;j<=n;j++) System.out.print(x[i][j]+"\t");
}
System.out.println();
}
static void afisv(int[] x)
{
int i;
System.out.println();
for(i=1;i<=n*n;i++) System.out.print(x[i]+" ");
System.out.println();
}
}
16.1. PROBLEME REZOLVATE 261
16.1.15 Partit ie - ONI 2003
Se deneste o partit ie a unui num ar natural n ca ind o scriere a lui n sub forma:
n = n
1
+n
2
+...nk, (k 1)
unde n
1
, n
2
, ..., n
k
sunt numere naturale care veric a urm atoarea relat ie:
n
1
n
2
... n
i
... n
k
1
Cerint a: Fiind dat un num ar natural n, sa se determine cate partit ii ale lui se
pot scrie, conform cerint elor de mai sus, stiind c a oricare num ar n
i
dintr-o partit ie
trebuie sa e un num ar impar.
Date de intrare
Fisierul partitie.in cont ine pe prima linie num arul n
Date de iesire
Fisierul partitie.out va cont ine pe prima linie num arul de partit ii ale lui n
conform cerint elor problemei.
Exemplu:
partitie.in partitie.out
7 5
Explicat ii:
Cele cinci partit ii sunt:
1+1+1+1+1+1+1
1+1+1+1+3
1+1+5
1+3+3
7
Restrict ii:
1 n 160
Timp maxim de executare: 3 secunde/test.
class PartitieNrImpare2 // n = suma de numere impare ;
{ // optimizat timp la jumatate !!!
static int dim=0,nsol=0; // nsol = 38328320 2Gata!!! timp = 4787
static int[] x=new int[161];
public static void main(String[] args)
{
long t1,t2;
int n=160;
int maxi=((n-1)/2)*2+1;
t1=System.currentTimeMillis();
f(n,maxi,1);
t2=System.currentTimeMillis();
262 CAPITOLUL 16. PROBLEME
System.out.println("nsol = "+nsol+" timp= "+(t2-t1));
}
static void f(int val, int maxip, int poz)
{
if(maxip==1)
{
nsol++;
// dim=poz-1;
// afis2(val);
return;
}
if(val==0)
{
nsol++;
// dim=poz-1;
// afis1();
return;
}
int maxi=((val-1)/2)*2+1;
int maxiok=min(maxip,maxi);
int i;
for(i=maxiok;i>=3;i=i-2)
{
x[poz]=i;
f(val-i,i,poz+1);
}
nsol++;
// dim=poz-1;
// afis2(val);
}
static void afis1()
{
int i;
System.out.print("\n"+nsol+" : ");
for(i=1;i<=dim;i++) System.out.print(x[i]+" ");
}
static void afis2(int val)
{
int i;
System.out.print("\n"+nsol+" : ");
16.1. PROBLEME REZOLVATE 263
for(i=1;i<=dim;i++) System.out.print(x[i]+" ");
for(i=1;i<=val;i++) System.out.print("1 ");
}
static int max(int a,int b) { return (a>b)?a:b; }
static int min(int a,int b) { return (a<b)?a:b; }
}// class
264 CAPITOLUL 16. PROBLEME
Bibliograe
[1] Aho, A.; Hopcroft, J.; Ullman, J.D.; Data strutures and algorithms, Addison
Wesley, 1983
[2] Aho, A.; Hopcroft, J.; Ullman, J.D.; The Random Access Machine, 1974
[3] Andonie R., G arbacea I.; Algoritmi fundamentali, o perspectiv a C++, Ed.
Libris, 1995
[4] Apostol C., Rosca I. Gh., Rosca V., Ghilic-Micu B., Introducere n progra-
mare. Teorie si aplicatii, Editura ... Bucuresti, 1993
[5] Atanasiu, A.; Concursuri de informatic a. Editura Petrion, 1995
[6] Atanasiu, A.; Ordinul de complexitate al unui algoritm. Gazeta de Informatic a
nr.1/1993
[7] - Bell D., Perr M.: Java for Students, Second Edition, Prentice Hall, 1999
[8] Calude C.; Teoria algoritmilor, Ed. Universit at ii Bucuresti, 1987
[9] Cerchez, E.; Informatica - Culegere de probleme pentru liceu, Ed. Polirom,
Iasi, 2002
[10] Cerchez, E., Serban, M.; Informatic a - manual pentru clasa a X-a., Ed.
Polirom, Iasi, 2000
[11] Cori, R.; Levy, J.J.; Algorithmes et Programmation, Polycopie, version 1.6;
http://w3.edu.polytechnique.fr/informatique/
[12] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Introducere n Algoritmi, Ed
Agora, 2000
[13] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Pseudo-Code Language, 1994
[14] Cristea, V.; Giumale, C.; Kalisz, E.; Paunoiu, Al.; Limbajul C standard, Ed.
Teora, Bucuresti, 1992
[15] Erickson J.; Combinatorial Algorithms; http://www.uiuc.edu/~jeffe/
265
266 BIBLIOGRAFIE
[16] Flanagan, D.; Java in a Nutshell, OReilly, 1997.
[17] Flanagan, D.; Java examples in a Nutshell, OReilly, 1997.
[18] Giumale, C.; Introducere n Analiza Algoritmilor, Ed.Polirom, 2004
[19] Giumale C., Negreanu L., C alinoiu S.; Proiectarea si analiza algoritmilor.
Algoritmi de sortare, Ed. All, 1997
[20] Gosling, J.; Joy, B.; Steele, G.; The Java Language Specication, Addison
Wesley, 1996.
[21] Knuth, D.E.; Arta program arii calculatoarelor, vol. 1: Algoritmi fundamentali,
Ed. Teora, 1999.
[22] Knuth, D.E.; Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici,
Ed. Teora, 2000.
[23] Knuth, D.E.; Arta programarii calculatoarelor, vol. 3: Sortare si cautare, Ed.
Teora, 2001.
[24] Lambert,K. A., Osborne,M.; Java. A Framework for Programming and Prob-
lem Solving, PWS Publishing, 1999
[25] Livovschi, L.; Georgescu H.; Analiza si sinteza algoritmilor. Ed. Enciclopedic a,
Bucuresti, 1986.
[26] Niemeyer, P.; Peck J.; Exploring Java, OReilly, 1997.
[27] Od agescu, I.; Smeureanu, I.; Stefanescu, I.; Programarea avansata a calcula-
toarelor personale, Ed. Militar a, Bucuresti 1993
[28] Od agescu, I.; Metode si tehnici de programare, Ed. Computer Lobris Agora,
Cluj, 1998
[29] Popescu Anastasiu, D.; Puncte de articulat ie si punt i n grafuri, Gazeta de
Informatica nr. 5/1993
[30] Rotariu E.; Limbajul Java, Computer Press Agora, Tg. Mures, 1996
[31] Tomescu, I.; Probleme de combinatorica si teoria grafurilor, Editura Didactic a
si Pedagogica, Bucuresti, 1981
[32] Tomescu, I.; Leu, A.; Matematic a aplicat a n tehnica de calcul, Editura Di-
dactica si Pedagogica, Bucuresti, 1982
[33] Vaduva, C.M.; Programarea in JAVA. Microinformatica, 1999
[34] ii nescu, R.;; Vii nescu, V.; Programare dinamic a - teorie si aplicat ii; GInfo nr.
15/4 2005
BIBLIOGRAFIE 267
[35] Vlada, M.; Conceptul de algoritm - abordare modern a, GInfo, 13/2,3 2003
[36] Vlada, M.; Grafuri neorientate si aplicat ii. Gazeta de Informatica, 1993
[37] Weis, M.A.; Data structures and Algorithm Analysis, Ed. The Ben-
jamin/Cummings Publishing Company. Inc., Redwoods City, California, 1995.
[38] Winston, P.H., Narasimhan, S.: On to JAVA, Addison-Wesley, 1996
[39] Wirth N., Algorithms + Data Structures = Programs, Prentice Hall, Inc 1976
[40] *** - Gazeta de Informatica, Editura Libris, 1991-2005