Sunteți pe pagina 1din 146

Despre algoritmi

Diferene ntre informatic i matematic


1) n lucrul pe calculator, majoritatea proprietilor algebrice nu sunt satisfcute. elementul neutru: egalitatea a+b=a poate fi satisfcut fr ca b=0: este situaia n care b0, dar ordinul su de mrime este mult mai mic dect al lui a. comutativitate: s considerm urmtoarea funcie scris n Pascal: Secvena de instruciuni:
a:=1; write (a+f(a))

function f(var a:integer):integer; begin a:=a+1; f:=a end;

produce la ieire valoarea 1+2=3, pe cnd secvena de instruciuni:


a:=1; write (f(a)+a))

produce la ieire valoarea 2+2=4. asociativitate: pentru simplificare vom presupune c numerele sunt memorate cu o singur cifr semnificativ i c la nmulire se face trunchiere. Atunci rezultatul nmulirilor (0.50.7)0.9 este 0.30.9=0.2, pe cnd rezultatul nmulirilor 0.5(0.70.9) este 0.50.6=0.3.

2) Nu intereseaz n general demonstrarea teoretic a existenei algoritmilor, ci accentul este pus pe elaborarea algoritmilor. Vom pune n vedere acest aspect prezentnd o elegant demonstraie a urmtoarei propoziii: Propoziie. Exist ,R\Q cu Q. Pentru demonstraie s considerm numrul real x=aa, unde a= 2 . Dac xQ, propoziia este demonstrat. Dac xQ, atunci xaQ i din nou propoziia este demonstrat.

Aspecte generale care apar la rezolvarea unei probleme


Aa cum am spus, informaticianului i se cere s elaboreze un algoritm pentru o problem dat, care s furnizeze o soluie (fie i aproximativ, cu condiia menionrii acestui lucru). Teoretic, paii sunt urmtorii: 1) demonstrarea faptului c este posibil elaborarea unui algoritm pentru determinarea unei soluii; 2) elaborarea unei algoritm (caz n care pasul anterior devine inutil); 3) demonstrarea corectitudinii algoritmului; 4) determinarea timpului de executare a algoritmului; 5) demonstrarea optimalitii algoritmului (a faptului c timpul de executare este mai mic dect timpul de executarea al oricrui alt algoritm pentru problema studiat).

Timpul de executare a algoritmilor


Un algoritm este elaborat nu numai pentru un set de date de intrare, ci pentru o mulime de astfel de seturi. Timpul de executare se msoar n funcie de lungimea n a datelor de intrare. Ideal este s determinm o formul matematic pentru T(n)=timpul de executare pentru orice set de date de intrare de lungime n. Din pcate, acest lucru nu este n general posibil. De aceea, n majoritatea cazurilor ne mrginim la a evalua ordinul de mrime al timpului de executare. Mai precis, spunem c timpul de executare este de ordinul f(n) i exprimm acest lucru prin T(n)=O(f(n)), dac raportul ntre T(n) i f(n) tinde la un numr real atunci cnd n tinde la . De exemplu, dac f(n)=nk pentru un anumit numr k, spunem c algoritmul este polinomial. Un algoritm se numete "acceptabil" dac este este polinomial.

Corectitudinea algoritmilor
n demonstrarea corectitudinii algoritmilor, exist dou aspecte importante: Corectitudinea parial: presupunnd c algoritmul se termin (ntr-un numr finit de pai), trebuie demonstrat c rezultatul este corect; Terminarea programului: trebuie demonstrat c algoritmul se ncheie n timp finit. Evident, condiiile de mai sus trebuie ndeplinite pentru orice set de date de intrare admis. Modul tipic de lucru const n introducerea n anumite locuri din program a unor invariani, adic relaii ce trebuie ndeplinite la orice trecere a programului prin acel loc.

Exemplul 1. Determinarea concomitent a cmmdc i cmmmc a dou numere naturale. Fie a,bN*. Se caut: (a,b)=cel mai mare divizor comun al lui a i b; [a,b]=cel mai mic multiplu comun al lui a i b. Algoritmul este urmtorul:
x a; y b; u a; v b; while xy { xv+yu = 2ab; (x,y)=(a,b) } if x>y then x x-y; u u+v else y y-x; v u+v write(x,(u+v)/2)

(*)

Demonstrarea corectitudinii se face n trei pai: 1) (*) este invariant: La prima intrare n ciclul while, condiia este evident ndeplinit. Mai trebuie demonstrat c dac relaiile (*) sunt ndeplinite i ciclul se reia, ele vor fi ndeplinite i dup reluare. Fie (x,y,u,v) valorile curente la o intrare n ciclu, iar (x',y',u',v') valorile curente la urmtoarea intrare n ciclul while. Deci: xv+yu=2ab i (x,y)=(a,b). Presupunem c x>y. Atunci x'=x-y, y'=y, u'=u+v, v'=v. x'v'+y'u'=(x-y)v+y(u+v)=xv+yu=2ab. Cazul x>y se studiaz similar. 2) Corectitudine parial: Este binecunoscut c n x=y se obine d=(a,b). Conform relaiilor (*), avem d(u+v)=2d2, unde a=d i b=d. Atunci (u+v)/2=d=ab/d=[a,b]. 3) Terminarea programului: Fie {xn}, {yn}, {un}, {vn} irul de valori succesive ale variabilelor. Toate aceste valori sunt numere naturale pozitive. Se observ c irurile {xn} i {yn} sunt descresctoare, iar irul {xn+yn} este strict descresctor. Aceasta ne asigur c dup un numr finit de pai vom obine x=y.

Exemplul 2. Metoda de nmulire a ranului rus. Fie a,bN. Se cere s se calculeze produsul ab. ranul rus tie doar: s verifice dac un numr este par sau impar; s adune dou numere; s afle ctul mpririi unui numr la 2. Cu aceste cunotine, ranul rus procedeaz astfel:

x a; y b; p 0 while x>0 { xy+p=ab } if x impar then p p+y x x div 2; y y+y write(p)

(*)

S urmrim cum decurg calculele pentru x=54, y=12:


x 54 27 13 6 3 1 0 y 12 24 48 96 192 384 ? p 0 24 72 264 648

Ca i la exemplul precedent, demonstrarea corectitudinii se face n trei pai: 1) (*) este invariant: La prima intrare n ciclul while, relaia este evident ndeplinit. Mai trebuie demonstrat c dac relaia (*) este ndeplinit i ciclul se reia, ea va fi ndeplinit i la reluare. Fie (x,y,p) valorile curente la o intrare n ciclu, iar (x',y',p') valorile curente la urmtoarea intrare n ciclul while. Deci: xy+p=ab. Presupunem c x este impar. Atunci (x',y',p')=((x-1)/2,2y,p+y). Rezult x'y'+p'=(x-1)/2+p+y=xy+p=ab. Presupunem c x este par. Atunci (x',y',p')=(x/2,2y,p). Rezult x'y'+p'=xy+p=ab. 2) Corectitudine parial: Dac programul se termin, atunci x=0, deci p=ab. 3) Terminarea programului: Fie {xn}, {yn} irul de valori succesive ale variabilelor corespunztoare. Se observ c irul {xn} este strict descresctor. Aceata ne asigur c dup un numr finit de pai vom obine x=0.

Optimalitatea algoritmilor
S presupunem c pentru o anumit problem am elaborat un algoritm i am putut calcula i timpul su de executare T(n). Este natural s ne ntrebm dac algoritmul nostru este "cel mai bun" sau exist un alt algoritm cu timp de executare mai mic. Problema demonstrrii optimalitii unui algoritm este foarte dificil, n mod deosebit datorit faptului c trebuie s considerm toi algoritmii posibili i s artm c ei au un timp de executare superior. Ne mrginim la a enuna dou probleme i a demonstra optimalitatea algoritmilor propui, pentru a pune n eviden dificultile care apar. Exemplul 1. Se cere s determinm m=min(a1,a2,...,an). Algoritmul binecunoscut este urmtorul:
m a1 for i=2,n if ai<m then m ai care necesit n-1 comparri ntre elementele vectorului a=(a1,a2,...,an).

Propoziia 1. Algoritmul de mai sus este optimal. Trebuie demonstrat c orice algoritm bazat pe comparri necesit cel puin n1 comparri. Demonstrarea optimalitii acestui algoritm se face uor prin inducie. Pentru n=1 este evident c nu trebuie efectuat nici o comparare. Presupunem c orice algoritm care rezolv problema pentru n numere efectueaz cel puin n-1 comparri s considerm un algoritm oarecare care determin cel mai mic dintre n+1 numere. Considerm prima comparare efectuat de acest algoritm; fr reducerea generalitii, putem presupune c s-au comparat a1 cu a2 i c a1<a2. Atunci m=min(a1,a3,...,an=1). Dar pentru determinarea acestui minim sunt necesare cel puin n-1 comparri, deci numrul total de comparri efectuat de algoritmul considerat este cel puin egal cu n. Exemplul 2. Se cere determinarea minimului i maximului elementelor unui vector. Mai precis, se cere determinarea cantitilor m=min(a1,a2,...,an) i M=min(a1,a2,...,an). Determinarea succesiv a valorilor m i M necesit timpul T(n)=2(n-1). O soluie mai bun const n a considera cte dou elemente ale vectorului, a determina pe cel mai mic i pe cel mai mare dintre ele, iar apoi n a compara pe cel mai mic cu minimul curent i pe cel mai mare cu maximul curent:

if n impar then m a1; M a1; k 1 else if a1<a2 then m a1; M a2 else m a2; M a1; k 2 { k = numrul de elemente analizate } while kn-2 if ak+1<ak+2 then if if else if if k k+2

ak+1<m ak+2>M ak+2<m ak+1>M

then then then then

m M m M

ak+1 ak+2 ak+2 ak+1

S calculm numrul de comparri efectuate: pentru n=2k, n faza de iniializare se face o comparare, iar n continuare se fac 3(k-1) comparri; obinem T(n)=1+3(k-1)=3k-3=3n/2-2=3n/2-2. pentru n=2k+1, la iniializare nu se face nici o comparare, iar n continuare se fac 3k comparri; obinem T(n)=(3n-3)/2=(3n+1)/2-2=3n/2-2. n concluzie, timpul de calcul este T(n)=3n/2-2.

Propoziia 2. Algoritmul de mai sus este optimal. Considerm urmtoarele mulimi i cardinalul lor: - A= mulimea elementelor care nu au participat nc la comparri; a=|A|; - B= mulimea elementelor care au participat la comparri i au fost totdeauna mai mari dect elementele cu care au fost comparate; b=|B|; - C= mulimea elementelor care au participat la comparri i au fost totdeauna mai mici dect elementele cu care au fost comparate; c=|C|; - D= mulimea elementelor care au participat la comparri i au fost cel puin o dat mai mari i cel puin o dat mai mici dect elementele cu care au fost comparate; d=|D|; Numim configuraie un quadruplu (a,b,c,d). Problema const n determinarea numrului de comparri necesare pentru a trece de la quadruplul (n,0,0,0) la quadruplul (0,1,1,n-2). Considerm un algoritm arbitrar care rezolv problem i artm c el efectueaz cel puin 3n/2-2 comparri. S analizm trecerea de la o configuraie oarecare (a,b,c,d) la urmtoarea. Este evident c nu are sens s efectum comparri n care intervine vreun element din D. Apar urmtoarele situaii posibile: 1) Compar dou elemente din A: se va trece n configuraia (a-2,b+1,c+1,d) . 2) Compar dou elemente din B: se va trece n configuraia (a,b-1,c+1,d+1) . 3) Compar dou elemente din C: se va trece n configuraia (a-2,b,c-1,d+1) .

4) Se compar un element din A cu unul din B. Sunt posibile dou situaii: - elementul din A este mai mic: se trece n configuraia (a-1,b,c+1,d); - elementul din A este mai mare: se trece n configuraia (a-1,b,c,d+1). Cazul cel mai defavorabil este primul, deoarece implic o deplasare "mai lent" spre dreapta a componentelor quadruplului. De aceea vom lua n considerare acest caz. 5) Se compar un element din A cu unul din C. Sunt posibile dou situaii: - elementul din A este mai mic: se trece n configuraia (a-1,b,c,d+1); - elementul din A este mai mare: se trece n configuraia (a-1,b+1,c,d). Cazul cel mai defavorabil este al doilea, deoarece implic o deplasare "mai lent" spre dreapta a componentelor quadruplului. De aceea vom lua n considerare acest caz. 6) Se compar un element din B cu unul din C. Sunt posibile dou situaii: - elementul din B este mai mic: se trece n configuraia (a,b-1,c-1,d+2); - elementul din B este mai mare: se rmne n configuraia (a,b,c,d). Cazul cel mai defavorabil este al doilea, deoarece implic o deplasare "mai lent" spre dreapta a componentelor quadruplului. De aceea vom lua n considerare acest caz. Observaie. Cazurile cele mai favorabile sunt cele n care d crete, deci ies din calcul elemente candidate la a fi cel mai mic i cel mai mare. Odat stabilit trecerea de la o configuraie la urmtoarea, ne punem problema cum putem trece mai rapid de la configuraia iniial la cea final. Analizm cazul n care n=2k (cazul n care n este impar este propus ca exerciiu). Paii sunt urmtorii: - plecm de la (n,0,0,0)=(2k,0,0,0); - prin k comparri ntre perechi de elemente din A ajungem la (0,k,k,0); - prin k-1 comparri ntre perechi de elemente din B ajungem la (0,1,k,k-1); - prin k-1 comparri ntre perechi de elemente din C ajungem la (0,1,1,n-2). n total au fost necesare k+(k-1)+(k-1)=3k-2=3n/2-2 comparri.

Existena algoritmilor
Acest aspect este i mai delicat dect precedentele, pentru c necesit o definiie matematic riguroas a noiunii de algoritm. Nu vom face dect s prezentm (fr vreo demonstraie) cteva definiii i rezultate. Un studiu mai amnunit necesit un curs aparte! Noiunea de algoritm nu poate fi definit dect pe baza unui limbaj sau a unei maini matematice abstracte. Numim problem nedecidabil o problem pentru care nu poate fi elaborat un algoritm. Definirea matematic a noiunii de algoritm a permis detectarea de probleme nedecidabile. Cteva dintre ele sunt urmtoarele: 1) Problema opririi programelor: pentru orice program i orice valori de intrare s se decid dac programul se termin. 2) Problema opririi programelor (variant): pentru un program dat s se decid dac el se termin pentru orice valori de intrare. 3) Problema echivalenei programelor: s se decid pentru orice dou programe dac sunt echivalente (produc aceeai ieire pentru aceleai date de intrare).

Noiunea de clas
class C { int x; boolean y; C() { x=2; } C(int a, boolean b) { x=a; y=b; } int met() { if (y) return x; else return x+1; } void met(int x) { if (this.x==x) y=true; } } // cmpuri // constructor // constructor // metoda

// metoda

Apare prima caracteristic a OOP: ncapsularea. Declararea i crearea unui obiect de tipul C
C Ob; Ob = new C();

sau
C Ob = new C(1,true);

Invocarea metodelor:
int i = Ob.met(); Ob.met(); Ob.met(7);

Observaie. La invocarea metodelor se folosete apelul prin valoare. Observaie. Dac metoda este declarat cu modificatorul static, ea poate fi invocat i prin numele clasei. Astfel, dac metoda met cu signatura vid era declarat prin:
static int met() { . . . }

atunci ea putea fi invocat i prin: C.met(); Observaie. Dac un cmp w este declarat cu modificatorul static, el este comun tuturor obiectelor de tipul C, deci este memorat o singur dat. n plus el poate fi referit i prin C.w .

O clas pentru intrri/ieiri: Clasa IO cu metode statice:


read(); readch(); readString(); write(s); writeln(s); // ntoarce un numr real citit de la intrare // ntoarce un caracter citit de la intrare // ntoarce un ir de caractere citit de la intrare ( "..." ) // tiprete irul de caractere s // tiprete irul de caractere s si trece la linia urmatoare

Invocrile se fac de exemplu prin: IO.writeln(i + ""); Un prim program: Fie fiierul Unu.java:
class Unu { public static void main(String[] sir) { for (int i=0; i<sir.length; i++) IO.writeln(sir[i]); } }

Compilarea:
javac Unu.java

Executarea:
java unu doi trei

produce la ieire:
unu doi trei

10

Liste
class elem { char c; elem leg; static elem p,u; elem() { } elem(char ch) { c = ch; } elem adaug(char ch) { elem x = new elem(ch); leg = x; return x; } void creare() { char ch = IO.readch(); p = new elem(ch); u = p; ch = IO.readch(); while (ch!='$') { u = u.adaug(ch); ch = IO.readch(); } } String direct(elem x) { if (x==null) return ""; else return x.c + direct(x.leg); } String invers(elem x) { if (x==null) return ""; else return invers(x.leg)+x.c; } } class Lista { public static void main(String[] s) { elem Ob = new elem(); Ob.creare(); IO.writeln( Ob.direct(elem.p) ); IO.writeln( Ob.invers(Ob.p) ); } }

unde operatorul != are sensul "diferit de". S presupunem c la intrare apare: abc$c1... La ieire vom obine:
abc cba

11

Arbori
Numim arbore un graf neorientat conex i fr cicluri. Aceasta nu este singurul mod n care putem defini arborii. Cteva definiii echivalente apar n urmtoarea teorem, expus fr demonstraie. 1) 2) 3) 4) 5) 6) Teorem. Fie G un graf cu n1 vrfuri. Urmtoarele afirmaii sunt echivalente: G este un arbore; G are n-1 muchii i nu conine cicluri; G are n-1 muchii i este conex; oricare dou vrfuri din G sunt unite printr-un unic drum; G nu conine cicluri i adugarea unei noi muchii produce un unic ciclu elementar; G este conex, dar devine neconex prin tergerea oricrei muchii.

n foarte multe probleme referitoare la arbori este pus n eviden un vrf al su, numit rdcin. Alegerea unui vrf drept rdcin are dou consecine: Arborele poate fi aezat pe niveluri astfel: rdcina este aezat pe nivelul 0; pe fiecare nivel i sunt plasate vrfurile pentru care lungimea drumurilor care le leag de rdcin este i; - se traseaz muchiile arborelui. Aceast aezare pe niveluri face mai intuitiv noiunea de arbore, cu precizarea c n informatic "arborii cresc n jos". Exemplul 3. Considerm urmtorul arbore i modul n care el este aezat pe niveluri prin alegerea vrfului 5 drept rdcin. 5 0 1 2 4 6 1 3 6 4 8 7 2 9 3 2 9 5 7 10 3 10 1 8 Arborele poate fi considerat un graf orientat, stabilind pe fiecare muchie sensul de la nivelul superior ctre nivelul inferior.

Reprezentarea pe niveluri a arborilor face ca noiunile de fii (descendeni) ai unui vrf, precum i de tat al unui vrf s aib semnificaii evidente. Un vrf fr descendeni se numete frunz.

12

Arbori binari
Un arbore binar este un arbore n care orice vrf are cel mult doi descendeni, cu precizarea c se face distincie ntre descendentul stng i cel drept. Din acest definiie rezult c un arbore binar nu este propriu-zis un caz particular de arbore. Primele probleme care se pun pentru arborii binari (ca i pentru arborii oarecare i pentru grafuri, aa cum vom vedea mai trziu) sunt: - modul de reprezentare; - parcurgerea lor. Forma standard de reprezentare a unui arbore binar const n: - a preciza rdcina rad a arborelui; - a preciza pentru fiecare vrf i tripletul st(i), dr(i) i info(i), unde acestea sunt respectiv descendentul stng, descendentul drept i informaia ataat vrfului. Trebuie stabilit o convenie pentru lipsa unuia sau a ambilor descendeni, ca de exemplu specificarea lor prin simbolul . Exemplul 4. Considerm de exemplu urmtorul arbore binar:
1

2 3 5

Presupunnd c informaia ataat fiecrui vrf este chiar numrul su de ordine, avem: - rad = 1; - st = (2,3,4,,6,,,,); - dr = (8,5,,,7,,,9,); - info= (1,2,3,4,5,6,7,8,9). Dintre diferitele alte reprezentri posibile, mai menionm doar pe cea care se reduce la vectorul su tata i la vectorul info. Pentru exemplul de mai sus: tata=(,1,2,3,2,5,5,1,8). Problema parcurgerii unui arbore binar const n identificarea unei modaliti prin care, plecnd din rdcin i mergnd pe muchii, s ajungem n toate vrfurile; n plus, atingerea fiecrui vrf este pus n eviden o singur dat: spunem c vizitm vrful respectiv. Aciunea ntreprins la vizitarea unui vrf depinde de problema concret i poate fi de exemplu tiprirea informaiei ataate vrfului.

13

Distingem trei modaliti standard de parcurgere a unui arbore binar: Parcurgerea n preordine Se parcurg recursiv n ordine: rdcina, subarborele stng, subarborele drept. Concret, se execut apelul preord(rad) pentru procedura:
procedure preord(x) if x= then else vizit(x); preord(st(x)); preord(dr(x)) end

Ilustrm acest mod de parcurgere pentru exemplul de mai sus, figurnd ngroat rdcinile subarborilor ce trebuie dezvoltai: 1 1, 2, 8 1, 2, 3, 5, 8, 9 1, 2, 3, 4, 5, 6, 7, 8, 9 Parcurgerea n inordine Se parcurg recursiv n ordine: subarborele stng, rdcina, subarborele drept. Ilustrm acest mod de parcurgere pentru Exemplul 4: 1 2, 1, 8 3, 2, 5, 1, 8, 9 4, 3, 2, 6, 5, 7, 1, 8, 9 Concret, se execut apelul inord(rad) pentru procedura:
procedure inord(x) if x= then else inord(st(x)); vizit(x); inord(dr(x)) end

Parcurgerea n postordine Se parcurg recursiv n ordine; subarborele stng, subarborele drept, rdcina. Ilustrm parcurgerea n postordine pentru Exemplul 4: 1 2, 8, 1 3, 5, 2, 9, 8, 1 4, 3, 6, 7, 5, 2, 9, 8, 1 Concret, se execut apelul postord(rad) pentru procedura:
procedure postord(x) if x= then else postord(st(x)); postord(dr(x)); vizit(x) end

14

Sortarea cu ansamble
Fie a=(a1,,an) vectorul care trebuie sortat (ordonat cresctor). Metoda sortrii de ansamble va folosi o reprezentare implicit a unui vector ca arbore binar. Acest arbore este construit succesiv astfel: - rdcina este 1; - pentru orice vrf descendenii si stng i drept sunt 2i i 2i+1 (cu condiia ca fiecare dintre aceste valori s nu depeasc pe n). Rezult c tatl oricrui vrf i este tata(i)=i/2. Evident, fiecrui vrf i i vom ataa eticheta ai. Pentru 2k-1n<2k arborele va avea k niveluri, dintre care numai ultimul poate fi incomplet (pe fiecare nivel i<k-1 se afl exact 2i vrfuri).
0 1

k-2 k-1

Vectorul a se numete ansamblu dac pentru orice i avem aia2i i aia2i+1 (dac fiii exist). S presupunem c subarborii de rdcini 2i i 2i+1 sunt ansamble. Ne propunem s transformm arborele de rdcin i ntr-un ansamblu. Ideea este de a retrograda valoarea ai pn ajunge ntr-un vrf ai crui descendeni au valorile mai mici dect ai. Acest lucru este realizat de procedura combin.
i procedure combin(i,n) i 2i; b ai while jn if j<n & aj<aj+1 then j j+1 if b>aj then aj/2 b; exit else aj/2 aj; j 2j aj/2 b end

2i

2i+1

ans

ans

Timpul de executare pentru procedura combin este O(k)=O(log n). Sortarea vectorului a se va face prin apelul succesiv al procedurilor creare i sortare prezentate mai jos. Procedura creare transform vectorul ntr-un ansamblu; n particular n a1 se obine cel mai mare element al vectorului.

15

Procedura sortare lucreaz astfel: pune pe a1 pe poziia n i reface ansamblul format din primele n-1 elemente; pune pe a1 pe poziia n-1 i reface ansamblul format din primele n-2 elemente; etc.
procedure sortare for i=n,2,-1 a1ai; combin(1,i-1) end
.

procedure creare for i=n/2,1,-1 combin(i,n) end

Timpul total de lucru este de ordinul O(n log n). Aa cum am menionat chiar de la nceput, structura de arbore este implicit i este menit doar s clarifice modul de lucru al algoritmului: calculele se refer doar la componentele vectorului.
3

12

11

10

16

Arbori de sortare
Un arbore de sortare este un arbore binar n care pentru orice vrf informaia ataat vrfului este mai mare dect informaiile vrfurilor din subarborele stng i mai mic dect informaiile vrfurilor din subarborele drept. 11 5 7 17 20

15 18 Observaie. Parcurgerea n inordine a unui arbore de cutare produce informaiile ataate vrfurilor n ordine cresctoare. Fie a=(a1,...an) un vector ce trebuie ordonat cresctor. Conform observaiei de mai sus, este suficient s crem un arbore de sortare n care informaiile vrfului s fie tocmai elementele vectorului. Pentru aceasta este suficient s precizm modul n care prin adugarea unei noi valori, s obinem tot un arbore de sortare. Pentru exemplul considerat: - adugarea valorii 6 trebuie s conduc la crearea unui nou vrf, cu informaia 6 i care este descendent stng al vrfului cu informaia 7; - adugarea valorii 16 trebuie s conduc la crearea unui nou vrf, cu informaia 16 i care este descendent drept al vrfului cu informaia 15. Presupunem c un vrf din arborele de sortare este o nregistrare sau obiect de tipul varf, ce conine cmpurile: - informaia info ataat vrfului; - descendentul stng st i descendentul drept dr (lipsa acestora este marcat, ca de obicei, prin ). Crearea unui nou vrf se face prin funcia varf_nou care ntoarce un nou vrf:
function varf_nou(info)

creez un nou obiect/ o nou nregistrare x n care informaia este info, iar descendentul stng i cel drept sunt ;
return x end

Inserarea unei noi valori val (n arborele de rdcin rad) se face prin apelul adaug(rad,val), unde funcia adaug ntoarce o nregistrare i are forma: function adaug(x,val) { se insereaz val n subarborele de rdcin x} if x= then return varf_nou(val) else if val<info(x) then st(x) adaug(st(x),val) else dr(x) adaug(dr(x),val) end

17

Programul principal ntreprinde urmtoarele aciuni: citete valorile ce trebuie ordonate i le insereaz n arbore; parcurge n inordine arborele de sortare; vizitarea unui vrf const n tiprirea informaiei ataate. Prezentm programul n Java:

class elem { int c; elem st,dr; elem() { } elem(int ch) { c=ch; st=null; dr=null; } elem adaug(elem x, int ch) { if (x==null) x=new elem(ch); else if (ch<x.c) x.st=adaug(x.st,ch); else x.dr=adaug(x.dr,ch); return x; } String parcurg(elem x) { if (x==null) return(""); else return( parcurg(x.st) + x.c + " " + parcurg(x.dr)); } } class Arbsort { public static void main(String arg[]) { elem rad=null; double val; elem Ob = new elem(); val = IO.read(); while ( ! Double.isNaN(val) ) { rad = Ob.adaug(rad,(int) val); val = IO.read(); } IO.writeln(Ob.parcurg(rad)); } }

Arbori oarecare
Primele probleme care se pun sunt aceleai ca pentru arborii binari: modalitile de reprezentare i de parcurgere. Exemplul 5. Considerm urmtorul arbore: 1 2 5 6 3 7 8 4 9 0 1 2 3

10 11

12

13
18

Se consider c arborele este aezat pe niveluri i c pentru fiecare vrf exist o ordine ntre descendenii si. Modul standard de reprezentare al unui arbore oarecare const n a memora rdcina, iar pentru fiecare vrf i informaiile: - info(i) = informaia ataat vrfului; - fiu(i) = primul vrf dintre descendenii lui i; - frate(i) = acel descendent al tatlui lui i, care urmez imediat dup i. Ca i pentru arborii binari, lipsa unei legturi ctre un vrf este indicat prin . Pentru arborele din Exemplul 5:
fiu =(2,5,7,8,10,11,,,,,,8,); frate =(,3,4,,6,,,9,,,12,13,).

O alt modalitate de reprezentare const n a memora pentru fiecare vrf tatl su. Aceast modalitate este incomod pentru parcurgerea arborilor, dar se dovedete util n alte situaii, care vor fi prezentate n continuare. n unele cazuri este util s memorm pentru fiecare vrf att fiul i fratele su, ct i tatl su. Parcurgerea n preordine Se parcurg recursiv n ordine rdcina i apoi subarborii care au drept rdcin descendenii si. Pentru Exemplul 5: 1 1,2,3,4 1,2,5,6,3,7,4,8,9 1,2,5,10,6,11,12,13,3,7,4,8,9. Concret, executm apelul Apreord(rad) pentru procedura: procedure Apreord(x) if x= then else vizit(x); Apreord(fiu(x)); Apreord(frate(x)) end Ca aplicaie, s presupunem c informaiile ataate vrfurilor sunt funcii de diferite ariti (aritatea unei funcii este numrul variabilelor de care depinde; o funcie de aritate 0 este o constant). Pentru Exemplul 5, vectorul de aritate este: aritate=(3,2,1,2,1,3,0,0,0,0,0,0,0). Rezultatul 1 2 5 10 6 11 12 13 3 7 4 8 9 al parcurgerii n preordine este o form fr paranteze (dar la fel de consistent) a scrierii expresiei funcionale:
1(2(5(10),6(11,12,13)),3(7),4(8,9))

Aceast form se numete forma polonez direct.

19

Parcurgerea n postordine Se parcurg recursiv n ordine subarborii rdcinii i apoi rdcina. Pentru Exemplul 5: 1 2,3,4,1 5,6,2,7,3,8,9,4,1 10,5,11,12,13,6,2,7,3,8,9,4,1. Concret, executm apelul Apostord(rad) pentru procedura:
procedure Apostord(x) if x= then else yfiu(x); while y<> Apostord(y); yfrate(x) vizit(x) end

Pentru aplicaia anterioar, dorim s calculm valoarea expresiei funcionale, cunoscnd valorile frunzelor. Evident, trebuie s parcurgem arborele n postordine.
class varf { int v; varf fiu,frate; static varf rad; varf() { } varf(int val) { v = val; } void creare() { IO.write("rad : "); int i = (int) IO.read(); rad = new varf(i); creare(rad); } void creare(varf x) { // ataseaza fiu si frate lui x int i; double d; IO.write("fiul lui " + x.v + " : "); d = IO.read(); if( !Double.isNaN(d) ) { i = (int) d; x.fiu = new varf(i); creare(x.fiu); } IO.write("fratele lui " + x.v + " : "); d = IO.read(); if( !Double.isNaN(d) ) { i = (int) d; x.frate = new varf(i); creare(x.frate); } } String pre(varf x) { if (x==null) return ""; else return x.v + " " + pre(x.fiu) + pre(x.frate); } void post(varf x) { varf y = x.fiu; while (y != null) { post(y); y = y.frate; } IO.write(x.v + " "); } }

20

class Arbori { public static void main(String[] s) { varf Ob = new varf(); Ob.creare(); IO.writeln( "Preordine:\t" + Ob.pre(varf.rad) ); IO.writeln("Postordine:\t"); Ob.post(varf.rad); } }

Parcurgerea pe niveluri Se parcurg vrfurile n ordinea distanei lor fa de rdcin, innd cont de ordinea n care apar descendenii fiecrui vrf. Pentru Exemplul 5: 1,2,3,4,5,6,7,8,9,10,11,12,13. Pentru implementare vom folosi o coad C, n care iniial apare numai rdcina. Atta timp ct coada este nevid, vom extrage primul element, l vom vizita i vom introduce n coad descendenii si:
C ; C rad while C x C; vizit(x); y fiu(x); while y y C; y frate(x) end

Parcurgerea pe niveluri este n general util atunci cnd se caut vrful care este cel mai apropiat de rdcin i care are o anumit proprietate/informaie. Pentru a include n programul anterior i parcurgerea pe niveluri, putem proceda astfel: - n metoda main adugm instruciunea:
IO.writeln("\rNiveluri : " +"\t" + Ob.niveluri() ); - n clasa varf adugm clasa intern elem i metoda niveluri: class elem { varf inf; elem leg; elem (varf v) { inf = v; } } String niveluri() { elem p,u,x; varf y; String s=""; p = new elem(null); u = new elem(root); p.leg = u; while ( p != u ) { x = p.leg; s += x.inf.v + " "; p.leg = x.leg; if(p.leg == null) u = p; y = x.inf.fiu; while ( y != null) { x = new elem(y); u.leg = x; u = x; y = y.frate; } } return s; } }

21

Grafuri
Parcurgerea DF a grafurilor neorientate
Fie G=(V,M) un graf neorientat. Ca de obicei, notm n=|V| i m=|M|. Parcurgere n adncime a grafurilor (DF = Depth First) generalizeaz parcurgerea n preordine a arborilor oarecare. Eventuala existen a ciclurilor conduce la necesitatea de a marca vrfurile vizitate. Ideea de baz a algoritmului este urmtoarea: se pleac dinr-un vrf i0 oarecare, apelnd procedura DF pentru acel vrf. Orice apel de tipul DF(i) prevede urmtoarele operaii: - marcarea vrfului i ca fiind vizitat; - pentru toate vrfurile j din lista Li a vecinilor lui i se execut apelul DF(j) dac i numai dac vrful j nu a fost vizitat. Simpla marcare a unui vrf ca fiind sau nu vizitat poate fi nlocuit cu atribuirea unui numr de ordine fiecrui vrf; mai precis, n nrdf(i), iniial egal cu 0, va fi memorat al ctelea este vizitat vrful i; nrdf(i) poart numele de numrul (de ordine) DF al lui i. Este evident c plecnd din vrful i0 se pot vizita numai vrfurile din componenta conex a lui i0. De aceea, n cazul n care graful nu este conex, dup parcurgerea componentei conexe a lui i0 vom repeta apelul DF pentru unul dintre eventualele vrfuri nc neatinse.
procedure DF(i) ndf ndf+1; nrdf(i) ndf; vizit(i); for toi j Li if nrdf(j)=0 then DF(j)

Programul principal este: citete graful; ndf 0;

for i=1,n nrdf(i) 0 for i=1,n if nrdf(i)=0 then DF(i) scrie nrdf;

Observaie. Dac dorim doar s determinm dac un graf este conex, vom nlocui al doilea ciclu for din programul principal prin: DF(1);
if ndf=n then write(CONEX) else write(NECONEX);

Observaie. Parcurgerea DF mparte muchiile grafului n: muchii de avansare: sunt acele muchii (i,j) pentru care n cadrul apelului DF(i) are loc apelul DF(j). Aceste muchii formeaz un graf parial care este o pdure: fiecare vrf este vizitat exact o dat, deci nu exist un ciclu format din muchii de avansare. muchii de ntoarcere: sunt acele muchii ale grafului care nu sunt muchii de avansare.

22

Determinarea mulimilor A i I a muchiilor de avansare i ntoarcere, precum i memorarea arborilor pariali din pdurea DF se poate face astfel: - n programul principal se fac iniializrile:
A ; I; for i=1,n tata(i) 0; - instruciunea if din procedura DF devine: if nrdf(j)=0 then A A {(i,j)}; tata(j) i; DF(j); else if tata(j) i then I I {(i,j)};

Exemplu. Pentru graful urmtor:


2 3 1 5 4 8 6 7 9

cu urmtoarele liste ale vecinilor vrfurilor:


L1={4,2,3}; L4={1,2,5}; L7={9}; L2={1,4}; L5={3,4}; L8={6} L3={1,5}; L6={8,9}; L9={6,7}

pdurea DF este format din urmtorii arbori pariali corespunztori componentelor conexe:
1 6 4 2 5 3 8 7 9

Timpul cerut de algoritmul de mai sus este O(max{n,m})=O(n+m), adic liniar, deoarece: - pentru fiecare vrf i, apelul DF(i) are loc exact o dat; - executarea unui apel DF(i) necesit un timp proporional cu grad(i)=|Li|; n consecin timpul total va fi proporional cu m=|M|. Propoziie. Dac (i,j) este muchie de ntoarcere, atunci i este descendent al lui j. Muchia (i,j) este detectat ca fiind muchie de ntoarcere n cadrul executrii apelului DF(i), deci nrdf(j)<nrdf(i). Deoarece exist muchie ntre vrfurile i i j, rezult c n timpul executrii lui DF(j) va fi vizitat i vrful i, deci i este descendent al lui j.

23

Propoziia de mai sus spune c muchiile de ntoarcere leag totdeauna dou vrfuri situate pe aceeai ramur a pdurii pariale DF. n particular, nu exist muchii de traversare (care s lege doi descendeni ai aceluiai vrf dintr-un arbore DF). Observaie. Un graf este ciclic (conine cel puin un ciclu) dac i numai dac n timpul parcurgerii sale n adncime este detectat o muchie de ntoarcere. Aplicaie. S se determine dac un graf este ciclic i n caz afirmativ s se identifice un ciclu. Vom memora pdurea format din muchiile de avansare cu ajutorul vectorului tata i n momentul n care este detectat o muchie de ntoarcere (i,j) vom lista drumul de la i la j format din muchii de avansare i apoi muchia (j,i). Procedura DF va fi modificat astfel:
procedure DF(i) ndf ndf+1; nrdf(i) ndf; vizit(i); for toi j Li if nrdf(j)=0 then tata(j) i; DF(j) else if tata(j)i then k i; while k j write(k,tata(k)); k tata(k); write(j,i); stop end.

Observaii: dac notm prin nrdesc(i) numrul descendenilor lui i n subarborele de rdcin i, aceast valoare poate fi calculat plasnd dup ciclul for din procedura DF instruciunea nrdesc(i)ndf-nrdf(i)+1; un vrf j este descendent al vrfului i n subarborele DF de rdcin i nrdf(i)nrdf(j)<nrdf(i)+nrdesc(i).

O aplicaie: Problema brfei

Se consider n persoane. Fiecare dintre ele emite o brf care trebuie cunoscut de toate celelalte persoane. Prin mesaj nelegem o pereche de numere (i,j) cu i,j{1,...,n} i cu semnificaia c persoana i transmite persoanei j brfa sa, dar i toate brfele care i-au parvenit pn la momentul acestui mesaj. Se cere una dintre cele mai scurte succesiuni de mesaje prin care toate persoanele afl toate brfele. Cu enunul de mai sus, o soluie este imediat i const n succesiunea de mesaje:
(1,2),(2,3),...,(n-1,n),(n,n-1),(n-1,n-2),...,(2,1). Sunt transmit deci n-2 mesaje. Dup cum vom vedea mai jos, acesta este numrul

minim de mesaje prin care toate persoanele afl toate brfele. Problema se complic dac exist persoane care nu comunic ntre ele (sunt certate) i deci nu-i vor putea transmite una alteia mesaje.

24

Aceast situaie poate fi modelat printr-un graf n care vrfurile corespund persoanelor, iar muchiile leag persoane care nu sunt certate ntre ele. Vom folosi matricea de adiacen a de ordin n n care aij este 0 dac persoanele i i j sunt certate ntre ele (nu exist muchie ntre i i j) i 1 n caz contrar. Primul pas va consta n detectarea unui arbore parial; pentru aceasta vom folosi parcurgerea DF. Fiecrei persoane i i vom ataa variabila boolean vi, care este true dac i numai dac vrful corespunztor a fost atins n timpul parcurgerii; iniial toate aceste valori sunt false. Vom pune aij=2 pentru toate muchiile de naintare.
vi false, i=1,...n nr 0; DF(1) if nr<n then write('Problema nu are soluie (graf neconex)') else rezolv problema pe arborele parial construit

unde procedura DF are forma cunoscut:


procedure DF(i) vi true for j=1,n if aij=1 & not vj then aij 2; DF(j) end

S observm c n acest mod am redus problema de la un graf la un arbore! Descriem n continuare modul n care rezolvm problema pe acest arbore parial, bineneles n ipoteza c problema are soluie (graful este conex). Printr-o parcurgere n postordine, n care vizitarea unui vrf const n transmiterea de mesaje de la fiii si la el, rdcina (presupus a fi persoana 1) va ajunge s cunoasc toate brfele. Aceasta se realizeaz prin apelul postord(1), unde procedura postord are forma:
procedure postord(i) for j=1,n if aij=2 then postord(j); write(j,i) end

n continuare, printr-o parcurgere n preordine a arborelui DF, mesajele vor circula de la rdcin ctre frunze. Vizitarea unui vrf const n transmiterea de mesaje fiilor si. Pentru aceasta executm apelul preord(1), unde procedura preord are forma:
procedure preord(i) for j=1,n if aij=2 then write(i,j); preord(j); end

Observm c att la parcurgerea n postordine, ct i la cea n preordine au fost listate n-1 perechi (mesaje), deoarece un arbore cu n vrfuri are n-1 muchii. Rezult c soluia de

25

mai sus const ntr-o succesiune de 2n-2 mesaje. Mai rmne de demonstrat c acesta este numrul minim posibil de mesaje care rezolv problema. Propoziie. Orice soluie pentru problema brfei conine cel puin 2n-2 mesaje. S considerm o soluie oarecare pentru problema brfei. Punem n eviden primul mesaj prin care o persoan a ajuns s cunoasc toate brfele; fie k aceast persoan. Deoarece celelate persoane trebuie s le fi emis, nseamn c pn acum au fost transmise cel puin n-1 mesaje. Dar k este prima persoan care a aflat toate brfele, deci celelalte trebuie s mai afle cel puin o brf. Rezult c n continuare trebuie s apar nc cel puin n-1 mesaje. n concluzie, soluia considerat este format din cel puin 2n-2 mesaje.

Circuitele fundamentale ale unui graf


Fie G=(V,M) un graf neorientat conex. Fie A =(V,M) un arbore parial al lui G. Muchiile din M\M vor fi numite corzi. Pentru fiecare coard exist un unic drum, format numai din muchii din M, ce unete extremitile corzii. mpreun cu coarda, acest drum formeaz un ciclu numit ciclu fundamental. Fie G1=(X1,M1) i G2=(X2,M2) dou grafuri. Suma lor circular este graful:
G1 G2 = (X1X2, M1M2\M1M2)

Observaii: 1) Operaia este comutativ i asociativ; 2) Dac M1 i M2 reprezint cicluri, atunci M1 M2 este tot un ciclu sau o reuniune disjunct (n privina muchiilor) de cicluri:

Teorem. Pentru un graf i un arbore parial A al su date, ciclurile fundamentale formeaz o baz, adic sunt ndeplinite condiiile: 1) orice ciclu se poate exprima ca sum circular de cicluri fundamentale; 2) nici un ciclu fundamental nu poate fi exprimat ca sum circular de cicluri fundamentale.

26

Exemplu. Considerm urmtorul graf i un arbore parial al su:


2 1 3 2 3 5

Arborele parial A:

Ciclul (1,2,5,4,3,1) se poate scrie ca o sum circular de cicluri fundamentale astfel:


2 2 1 3 5 1 1 3 5 4 4 3 1 3 5

ciclu n G, ale crui muchii sunt partiionate n C={e1,,ek}{ek+1,,ej} unde e1,...,ek sunt corzi, iar ek+1,...,ej sunt muchii din A. Fie C(e1),...,C(ek) ciclurile fundamentale din care fac parte e1,,ek. Fie C=C(e1)... C(ek). Vom demonstra c C=C (sunt formate din aceleai muchii). Presupunem c CC. Atunci CC. S observm c att C ct i C conin corzile e1,,ek. Conform unei observaii de mai sus, C i apoi CC sunt cicluri sau reuniuni disjuncte de cicluri. Cum att C ct i C conin corzile e1,...,ek i n rest muchii din A, rezult c CC conine numai muchii din A, deci nu poate conine un circuit. Contradicie. Fie un ciclu fundamental care conine coarda e. Fiind singurul ciclu fundamental ce conine e, el nu se va putea scrie ca sum circular de alte cicluri fundamentale. Consecin. Baza format din circuitele fundamentale are ordinul mn+1. Observaie. O muchie din C(e1)...C(ek) este tears apare de numr par de ori. Determinarea mulimii ciclurilor fundamentale Printr-o parcurgere a arborelui A, putem stabili pentru el legtura tata. Atunci pentru orice coard (i,j) procedm dup cum urmeaz: 1) Determinm vectorii:
u = (u1=i, u2=tata(u1), ... , unu=tata(unu-1)=rad) v = (v1=j, v2=tata(v1), ... , vnv=tata(vnv-1)=rad). 2) Parcurgem simultan vectorii u i v de la stnga la dreapta i determinm cel mai mic indice k cu uk=vk.

Demonstraie. Considerm un

27

Atunci ciclul cutat este:


u1=i, u2, ... , uk=vk, ... , v1=j, i

Observm c pentru fiecare coard, timpul este O(n).

Parcurgerea DF a grafurilor orientate


Algoritmul este acelai ca la grafuri neorientate. Arcele de avansare formeaz o pdure constituit din arbori n care toate arcele au orientarea "de la rdcin ctre frunze", numit "pdure DF". Exemplu. Pentru graful:
4 5 2 6 7 8 1 3 11 9 10

obinem pdurea:
1 7 8 9

10

11

i vectorul nrdf = (1,2,3,4,5,6,7,8,9,10,11). Parcurgerea DF mparte arcele (i,j) n 3 categorii: 1) arce de avansare, caracterizate prin nrdf(i)<nrdf(j): 1.1) arce componente ale pdurii DF; 1.2) arce ce leag un vrf de un descendent al su care nu este fiu al su; 2) arce de ntoarcere, ce leag un vrf de un predecesor al su n pdurea DF; evident nrdf(i)>nrdf(j); 3) arce de traversare: leag dou vrfuri care nu sunt unul descendentul celuilalt.

28

Pentru exemplul considerat avem: 1.2) : (1,6) 2) : (3,1), (6,4), (11,9) 3) : (7,2), (8,2), (8,7), (9,1), (11,2), (11,8) Propoziie. Pentru orice arc de traversare (i,j) avem nrdf(i)>nrdf(j).

S presupunem prin absurd c nrdf(i)<nrdf(j). Atunci n momentul n care s-a ajuns prima dat la i, vrful j nu a fost nc atins. Din modul n care lucreaz algoritmul, (i,j) va fi arc de avansare:
i i

sau
j j

Observaie. Spre deosebire de arcele de traversare, arcele de ntoarcere determin un circuit elementar (prin adugarea unui astfel de arc la pdurea DF ia natere un circuit elementar). Putem stabili dac un arc (i,j) cu nrdf(i)>nrdf(j) este de ntoarcere sau de traversare astfel:
ki; while k0 & kj ktata(k) if k=0 then write(traversare) else write(ntoarcere)

Parcurgerea BF a grafurilor neorientate


Fie un graf G=(V,M) i fie i0 un vrf al su. n unele situaii se pune probleme determinrii vrfului j cel mai apropiat de i0 cu o anumit proprietate. Parcurgerea DF nu mai este adecvat. Parcurgerea pe lime BF (Breadth First) urmrete vizitarea vrfurilor n ordinea cresctoare a distanelor lor fa de i0. Este generalizat parcurgerea pe niveluri a arborilor, inndu-se cont c graful poate conine cicluri. Va fi deci folosit o coad C. La fel ca i la parcurgerea DF, vrfurile vizitate vor fi marcate. Pentru exemplul din primul paragraf al acestui capitol, parcurgerea BF produce vrfurile n urmtoarea ordine: 1,4,2,3,5,6,8,9,7. Algoritmul care urmeaz parcurge pe lime componenta conex a lui i0:
for i=1,n vizitat(i) false

29

9 C (i0} while C i C; viziteaz i; vizitat(i)true for toi j vecini ai lui i if not vizitat(j) then j C

Programul Java corespunztor folosete: Clasa Vector:

Un vector (obiect de tipul Vector) are lungime variabil i conine obiecte oarecare. Vector() : construiete vectorul vid (de lungime 0); void addElement(Object) : adaug obiect la sfritul vectorului; boolean remove Element(Object) : elimin, cu deplasare la stnga, prima apariie; boolean isEmpty() : verific dac vectorul este vid; Object firstElement() : ntoarce primil element al vectorului. Clasa nfurtoare Integer:

Integer(int): constructor ce construiete un obiect ce conine ntregul respectiv; int intValue(): ntoarce ntregul asociat obiectului.

============
class BF { public static void main(String[] s) { nivel Ob = new nivel(); Ob.parc(); } } import java.util.*; class nivel { int[][] mat; int[] temp; int n; nivel() { int[] temp; int ntemp; double d; IO.write("n= "); n = (int) IO.read(); mat = new int[n][]; temp = new int[n]; for (int i=0; i<n; i++) { IO.write("Vecinii lui "+i+" : "); ntemp = 0; d = IO.read(); while( ! Double.isNaN(d) ) { temp[ntemp++]= (int) d; d = IO.read(); } mat[i] = new int[ntemp]; for (int j=0; j<ntemp; j++) mat[i][j] = temp[j]; } IO.writeln("**************"); }

30

10 void parc() { boolean[] v = new boolean[n]; Integer Int; int i,j,k; Vector coada = new Vector(); coada.addElement(new Integer(0)); v[0] = true; while ( ! coada.isEmpty() ) { Int = (Integer) coada.firstElement(); coada.removeElementAt(0); i = Int.intValue(); IO.write(i+"\t"); for (k=0; k<mat[i].length; k++) { j = mat[i][k]; if( !v[j] ) { coada.addElement(new Integer(j)); v[j]=true; } } } } }

La fel ca pentru parcurgerea DF, algoritmul poate fi completat pentru parcurgerea ntregului graf, pentru determinarea unei pduri n care fiecare arbore este un arbore parial al unei componente conexe etc.

31

Metoda Greedy
Metoda Greedy (greedy=lacom) este aplicabil problemelor de optim. Considerm mulimea finit A={a1,...,an} i o proprietate p definit pe mulimea submulimilor lui A:
p()= 1 p:P(A){0,1} cu p(X) p(Y), Y X O submulime SA se numete soluie dac p(S)=1. Dintre soluii aleg una care optimizeaz o funcie p:P(A)R dat.

Metoda urmrete evitarea parcurgerii tuturor submulimilor (ceea ce ar necesita un timp de calcul exponenial), mergndu-se "direct" spre soluia optim. Nu este ns garantat obinerea unei soluii optime; de aceea aplicarea metodei Greedy trebuie nsoit neaprat de o demonstraie. Distingem dou variante generale de aplicare a metodei Greedy:
S for i=1,n xalege(A); AA\{x} if p(S{x})=1 then SS{x} prel(A) S for i=1,n if p(S{ai}) then SS{ai}

Observaii: n algoritm nu apare funcia f !! timpul de calcul este liniar (exceptnd prelucrrile efectuate de procedurile prel i alege); dificultatea const n a concepe procedurile alege, respectiv prel, n care este "ascuns" funcia f.

Exemplul 1. Se consider mulimea de valori reale A={a1,...,an}. Se caut submulimea a crei sum a elementelor este maxim. Vom parcurge mulimea i vom selecta numai elementele pozitive.
k0 for i=1,n if ai>0 then kk+1; skai write(s)

Exemplul 2. Caut cel mai scurt ir cresctor cu elemente din mulimea {a1,...,an}. ncepem prin a ordona cresctor elementele mulimii (corespunztor procedurii prel). Apoi:
k1; s1a1; lung1; baza1 for i=2,n if ai>sk then kk+1; sk ai else if k>lung then lungk; bazai-k k1; s1ai

32

Astfel, dac n urma ordonrii a rezultat a=(1,1,2,3,4,4,5,6,7,8,8}, vom obine succesiv:


baza=1; lung=1; s=(1) baza=2; lung=4; s=(1,2,3,4) baza=6; lung=5; s=(4,5,6,7,8).

(Contra)exemplul 3. Fie mulimea A={a1,...,an} cu elemente pozitive. Caut submulimea de sum maxim, dar cel mult egal cu M dat. Dac procedez ca n exemplul 1, pentru A=(6,3,4,2) i M=7 obin {6}. Dar soluia optim este {3,4} cu suma egal cu 7. Continum cu prezentarea unor exemple clasice.

Metoda textelor pe band


Textele cu lungimile L(1),...,L(n) urmeaz a fi aezate pe o band. Pentru a citi textul de pe poziia k, trebuie citite textele de pe poziiile 1,2,...,k (conform specificului accesului secvenial pe band). O soluie nseamn o permutare pSn. Pentru o astfel de permutare (ordine de aezare a textelor pe band), timpul pentru a citi textul de pe poziie k este: Tp(k)=L(p1)+...+L(pk). Presupunnd
1 n Tp(k). n k=1 1 n S observm c funcia T se mai poate scrie: T(p)= (n k + 1)L(pk ) n k=1 (textul de pe poziia k este citit dac vrem s citim unul dintre textele de pe poziiile k,...,n).

textele egal probabile, trebuie minimizat valoarea T(p)=

Conform strategiei Greedy, ncepem prin a ordona cresctor vectorul L. Rezult c n continuare L(i)<L(j), i<j. Demonstrm c n acest mod am obinut modalitatea optim, adic permutarea identic minimizeaz funcia de cost T. Fie pSn optim, adic p minimizeaz funcia T. Dac p este diferit de permutarea identic i<j cu L(pi)>L(pj):
p=( pi pj )

Considerm permutarea p' n care am interschimbat elementele de pe poziiile i i j:


p'=( pj pi )

Atunci n[T(p)-T(p)] = (n-i+1)L(pi) + (n-j+1)L(pj) =


= (n-i+1)L(pj) + (n-j+1)L(pi) = = (j-i)L(pi)+(i-j)L(pj) = = (j-i)[L(pi)-L(pj)]>0, ambii factori fiind pozitivi.

Rezult c T(p)<T(p). Contradicie.

33

Problema contiunu a rucsacului


Se consider un rucsac de capacitate (greutate) maxim G i n obiecte caracterizate prin: - greutile lor g1,...,gn; - ctigurile c1,...,cn obinute la ncrcarea lor n totalitate n rucsac. Din fiecare obiect poate fi ncrcat orice fraciune a sa. Se cere o modalitate de ncrcare de (fraciuni de) obiecte n rucsac, astfel nct ctigul total s fie maxim.
xi [0,1],i Prin soluie nelegem un vector x=(x1,,xn) cu n gi x i G i=1

O soluie optim este soluie care maximizeaz funcia f(x)= ci xi .


i=1

Dac suma greutilor obiectelor este mai mic dect G, atunci ncarc toate obiectele: x=(1,...,1). De aceea presupunem n continuare c g1+...+gn>G. Conform strategiei Greedy, ordonez obiectele descresctor dup ctigul la unitatea de greutate, deci lucrm n situaia:
c1 c2 c ... n g1 g 2 gn

(*)

Algoritmul const n ncrcarea n aceast ordine a obiectelor, atta timp ct nu se depete greutatea G (ultimul obiect pote fi eventual ncrcat parial):
G1 G { G1 reprezint greutatea disponibil } for i=1,n if giG1 then xi1; G1G1-gi else xiG1/gi; for j=i+1,n xj 0 stop write(x)

Am obinut deci x=(1,...,1,xj,0,...,0) cu xj[0,1). Artm c soluia astfel obinut este optim.
n giy i = G Fie y soluia optim: y=(...,yk,...) cu i=1 n c y maxim i=1 i i Dac yx, fie k prima poziie pe care ykxk.

Observaii: kj: pentru k>j se depete G. yk<xk: pentru k<j: evident, deoarece xk=1; pentru k=j: dac yk>xk se depete G.

34

Considerm soluia: y=(y1,,yk-1,xk,yk+1,, yn) cu <1 (primele k1 componente coincid cu cele din x). Pstrez greutatea total G, deci: gkxk+(gk+1yk+1+...+gnyn)=gkyk+gk+1yk+1+...+gnyn. Rezult:
gk(xk-yk)=(1-)(gk+1yk+1++gnyn) (**) Compar performana lui y' cu cea a lui y: f(y)-f(y) = ckxk +ck+1yk+1 +...+ cnyn - (ckyk+ck+1yk+1 +...+cnyn) = = ck(xk-yk) + (-1)(ck+1yk+1+...+cnyn) = = ck/gk[gk(xk-yk)+(-1)(gk/ckck+1yk+1+...+gk/ckcnyn)] Dar -1>0 i gk/ck gs/cs, s>k. Atunci f(y)-f(y)>ck/gk [gk(xk-yk)+(-1)(gk+1yk+1+...+gnyn)]=0, deci f(y')>f(y). Contradicie.

Problema discret a rucsacului difer de cea continu prin faptul c fiecare obiect poate fi ncrcat numai n ntregime n rucsac. S observm c aplicarea metodei Greedy eueaz n acest caz. ntr-adevr, aplicarea ei pentru: G=5, n=3 i g=(4,3,2), c=(6,4,2.5) are ca rezultat ncrcarea primul obiect; ctigul obinut este 6. Dar ncrcarea ultimelor dou obiecte conduce la ctigul superior 6.5.

Problema arborelui parial de cost minim.


Fie G=(V,M) un graf neorientat cu muchiile etichetate cu costuri strict pozitive. Se cere determinarea unui graf parial de cost minim. Ca exemplificare, s considerm n orae iniial nelegate ntre ele. Pentru fiecare dou orae se cunoate costul conectrii lor directe (considerm acest cost egal cu + dac nu este posibil conectarea lor). Constructorul trebuie s conecteze oraele astfel nct din oricare ora s se poat ajunge n oricare altul. Ce legturi directe trebuie s aleag constructorul astfel nct costul total al lucrrii s fie minim? Este evident c graful parial cutat este un arbore (dac ar exista un ciclu, am putea ndeprta orice muchie din el, cu pstrarea conexitii i micorarea costului total). Vom aplica metoda Greedy: adaug mereu o muchie de cost minim dintre cele nealese i care nu formeaz un ciclu cu precedentele muchii alese. Acest algoritm poart numele de algoritmul lui Kruskal. Ca de obicei, fie |V|=n, |M|=m. Vor fi alese deci n-1 muchii. Construim o matrice mat cu m linii i n coloane. Pe fiecare linie apar extremitile i i j ale unei muchii, precum i costul acestei muchii. ncepem prin a ordona liniile matricii cresctor dup ultima coloan (a costurilor muchiilor).

35

Exemplu. Considerm graful de mai jos i matricea mat ataat. 1 3 5 2 2 4 4 2 5 2 5 3 1 1 4 1 3 2 3 2 4 5 5 4 5 3 2 2 2 3 4 5 5

Conform algoritmului lui Kruskal, vor fi alese n ordine muchiile: (1,2), (1,4), (4,5), (3,4) cu costul total egal cu 10. Muchia (1,5) nu a fost aleas deoarece formeaz cu precedentele un ciclu. Dificultatea principal const n verificarea faptului c o muchie formeaz sau nu cu precedentele un ciclu. Plecnd de la observaia c orice soluie parial este o pdure, vom asocia fiecrui vrf i un reprezentant ri care identific componenta conex (arborele) din care face parte vrful n soluia parial. Atunci: - o muchie (i,j) va forma un ciclu cu precedentele ri=rj; - la alegerea (adugarea) unei muchii (i,j) vom pune rkrj pentru orice vrf k cu rk=ri (unim doi arbori, deci vrfurile noului arbore trebuie s aib acelai reprezentant). n algoritmul care urmeaz metoda descris, l este numrul liniei curente din matricea mat, nm este numrul de muchii alese, iar cost este costul muchiilor alese
ri i, i=1,n l 1; nm 0; cost 0 while lm & nm<n-1 i1 mat(l,1); i2 mat(l,2) r1 ri1; r2 ri2 if r1r2 then nm nm+1; cost cost+mat(l,3); write(i1,i2) for k=1,n if rk=r2 then rk r1 l l+1 if nm<n-1 then write('Graf neconex') else write('Graf conex. Costul=,cost)

Demonstrm n continuare corectitudinea algoritmului lui Kruskal. Fie G=(V,M) un graf conex. PM se numete mulime promitoare de muchii dac nu conine cicluri i poate fi extins la un arbore parial P de cost minim.

36

Propoziia 1. Fie P promitoare i YV astfel nct nici un vrf din Y nu este o extremitate a unei muchii din P. Fie m una dintre muchiile de cost minim cu cel puin o extremitate n Y. Atunci P{m} este promitoare. Fie P arborele parial de cost minim la care poate fi extins P.
P Y

Dac mP , O.K. Dac mP, atunci P{m} are un ciclu. n el exist, n afar de m, o alt muchie m' adiacent lui Y. Fie P'=P {m}\{m}. Observm c P{m}P' pentru c PP. P' este de cost minim deoarece costul lui m este mai mic sau egal cu costul lui m'. Rezult c mulimea de muchii P{m} este promitoare. Propoziia 2. Arborele furnizat de metoda Kruskal este arbore parial de cost minim. Artm prin inducie c dup k=0,1,...,n-1 pai, cele k muchii alese formeaz o mulime promitoare. Pentru k=0 rezultatul este evident. Fie P mulimea promitoare a muchiilor selectate la primii k pai. Fie Y=V\{x | x extremitate a unei muchii din P} = mulimea punctelor izolate. Conform Propoziiei 1, mulimea P{muchia selectat la pasul k+1} este promitoare. n final, o mulime promitoare cu n-1 muchii este chiar un arbore parial de cost minim. Observaie. Tot o ilustrare a metodei Greedy pentru problema enunat o constituie algoritmul lui Prim, care const n urmtoarele: - se ncepe prin selectarea unui vrf; - la fiecare pas aleg o muchie (i,j) de lungime minim cu i selectat, dar j neselectat. De aceast dat, la fiecare pas se obine un arbore. Propunem ca exerciiu demonstrarea faptului c dup n-1 pai se obine un arbore parial de cost minim.

37

Vizibilitate i acces
Pachetele, clasele, metodele, cmpurile i variabilele sunt identificate prin numele lor. Problema care se pune este de a stabili din ce loc putem face referire la aceste nume (cu semnificaia dorit), adic de a preciza domeniul lor de vizibilitate. Variabile locale i etichete

Prin bloc nelegem o secven (eventual vid) de instruciuni, cuprins ntre acolade. Un bloc este o instruciune i de aceea poate fi folosit n orice loc unde poate s apar o instruciune. De exemplu orice metod i orice constructor sunt formate dintr-un antet urmat de un bloc. n schimb ceea ce urmeaz dup modificatorii i numele unei clase nu constituie un bloc, dei apar acoladele deschis i nchis: coninutul unei clase nu este o succesiune de instruciuni. Orice bloc poate conine la rndul su un alt bloc, putnd astfel lua natere o structur imbricat de blocuri. Prin variabil local nelegem o variabil declarat n interiorul unui bloc, inclusiv ntr-o instruciune for. Domeniul de vizibilitate al unei variabile locale este constituit din partea din blocul n care a fost declarat, ce urmeaz declarrii. Prin urmare o variabil local nu poate fi referit nainte de a fi declarat. Variabilele locale exist numai pe perioada n care blocul n care sunt declarate este n curs de executare. Nu putem folosi facilitatea de imbricare a blocurilor pentru a redeclara o variabil local. O variabil local nu poate fi redefinit nici n blocul n care a fost declarat, nici ntr-un bloc inclus n acesta. Regulile care guverneaz domeniul de vizibilitate al etichetelor sunt aceleai ca pentru variabilele locale. Parametrii metodelor i constructorilor

Numele parametrilor dintr-o metod sau dintr-un constructor sunt vizibile doar n interiorul acestora, adic n blocul care urmeaz antetului metodei sau constructorului. Este ns posibil ca ntr-un bloc dintr-o metod sau dintr-un constructor s redeclarm numele unui parametru; n acest caz, n partea din bloc ce urmeaz redeclarrii, numele respectiv este considerat ca avnd semnificaia dat de redeclarare. Cu alte cuvinte, domeniul de vizibilitate al unui parametru este constituit din corpul metodei sau constructorului n care apare, mai puin blocurile (evident disjuncte) n care este redeclarat. Vizibilitatea n interiorul unei clase

tim c o clas este constituit din cmpuri, constructori (folosii la crearea de obiecte) i metode (folosite la invocarea lor). Din orice constructor i din orice metod putem s ne referim la orice cmp sau metod a clasei. De asemenea dintr-un constructor putem apela alt constructor prin this urmat, ntre paranteze, de o list de argumente ce respect signatura noului constructor.

38

Ordinea n care apar cmpurile nestatice i metodele n cadrul clasei nu este relevant (de exemplu nu conteaz dac un cmp nestatic este declarat la nceputul sau la sfritul clasei). Totui, dac numele unui cmp este redeclarat ntr-o metod (este folosit ca nume al unui parametru sau declarat ntr-un bloc), atunci declararea cea mai interioar este cea care primeaz cnd se face o referire la acel nume (identificator). Putem spune c un parametru poate ascunde un cmp, iar o variabil local poate ascunde un parametru i/sau un cmp. Dup cum vom vedea n continuare, numele unei metode nu poate fi ascuns. Modificatorul final

Modificatorul final poate fi ataat att variabilelor (locale sau cmpuri), ct i metodelor. n cazul variabilelor locale, variabilei i se atribuie valoare la declarare sau nainte de a fi folosit; variabila nu poate primi de dou ori valoare. Menionm c final este singurul modificator ce poate fi ataat unei variabile locale. Unui cmp final trebuie s i se atribuie valoare fie prin iniializare, fie prin constructori. Menionm c n general modificatorul final este folosit mpreun cu static, pentru a trata un cmp ca o "constant cu nume". Efectul modificatorului final asupra metodelor i claselor va fi prezentat atunci cnd vom vorbi despre extinderea claselor. Modificatorul static

Modificatorul static poate fi folosit la declararea cmpurilor i metodelor unei clase. O variabil local nu poate fi declarat cu static . Fie c un cmp declarat cu static n clasa Clasa. Aceast declarare are dou consecine: - cmpul c este comun tuturor obiectelor de tipul Clasa; - referirea la cmp din exteriorul clasei se face fie conform regulii generale (prin crearea i utilizarea unei instane a clasei Clasa), fie direct, prin precalificarea cmpului cu numele clasei (Clasa.c). Fie acum met o metod declarat cu modificatorul static n clasa Clasa; ea se numete metod static sau metod de clas. Invocarea ei se poate face fie conform regulii generale (prin crearea i utilizarea unei instane a clasei Clasa), fie direct, prin precalificarea metodei cu numele clasei: Clasa.met(...) ; De aceea ntr-o metod static nu poate fi folosit referina this. O metod static poate accesa numai cmpurile i metodele statice ale clasei din care face parte sau a altei clase. Metodele statice (de clas) sunt folosite de obicei pentru a efectua prelucrri asupra cmpurilor statice ale clasei. Putem "ocoli" ns aceast regul de exemplu astfel: - la invocarea metodei i transmitem ca parametru o referin la un obiect; - n metod construim explicit un obiect de tipul clasei la ale crei metode i cmpuri dorim s ne referim.

39

Modificatorii de acces

Toate cmpurile i metodele unei clase sunt accesibile (pot fi referite) din interiorul clasei. 3 Rolul modificatorilor public, protected, private i cel implicit (numit uneori package) referitor la accesul din exteriorul unei clase la membrii si este sintetizat n urmtorul tabel: Membrii clasei cu acest modificator sunt accesibili ... public de oriunde clasa este accesibil protected din cod din acelai pachet implicit (package din cod din acelai pachet ) private numai din interiorul clasei Reamintim c modificatorii de mai sus joac un rol nu numai n privina accesului, dar i pentru facilitatea de extindere a claselor, ceea ce va face diferena ntre modificatorul protected i cel implicit. Determinarea semnificaiei unui nume Modificator

Pentru un identificator folosit ca nume de variabil (local sau cmp), metod sau clas, semnificaia sa se obine la prima cutare ncununat de succes din urmtorul ir de cutri succesive: 1) variabilele locale declarate ntr-un bloc sau un ciclu for; 2) numai dac suntem n interiorul unei metode sau a unui constructor: parametrii metodei sau constructorului; 3) membrii clasei, inclusiv cei rezultai prin extinderea claselor (vezi capitolul urmtor) 4) tipuri importate explicit; 5) tipuri declarate n acelai pachet; 6) tipuri importate implicit; 7) pachete disponibile pe sistemul gazd. Mai mult, se face distincie clar ntre tipurile de identificatori: nume de variabile (locale), de pachete, de clase, de metode i de cmpuri; iat de ce numele unei metode nestatice nu poate fi ascuns. Aceasta permite s folosim un acelai identificator pentru a numi una sau mai multe astfel de entiti.

40

Metoda Backtracking
Aa cum s-a subliniat n capitolele anterioare, complexitatea n timp a algoritmilor joac un rol esenial. n primul rnd un algoritm este considerat "acceptabil" numai dac timpul su de executare este polinomial, adic de ordinul O(nk) pentru un anumit k; n reprezint numrul datelor de intrare. Pentru a ne convinge de acet lucru, vom considera un calculator capabil s efectueze un milion de operaii pe secund.
n=20 n 2n 3n
3

n=40

n=60

1 sec 58 min

12,7 zile 3855 secole

0,2 sec 366 secole 1013 secole

Chiar dac n prezent calculatoarele performante sunt capabile de a efectua zeci de miliarde de operaii pe secund, tabelul de mai sus arat c algoritmii exponeniali nu sunt acceptabili.

Descrierea metodei
Fie X=X1 ... Xn. Caut xX cu (x), unde :X {0,1} este o proprietate definit pe X. Din cele de mai sus rezult c generarea tuturor elementelor produsului cartezian X nu este acceptabil. Metoda backtracking ncearc, dar nu reuete totdeauna, micorarea timpului de calcul. X este numit spaiul soluiilor posibile, iar sintetizeaz condiiile interne. Vectorul x este construit progresiv, ncepnd cu prima component. Se avanseaz cu o valoare xk dac este satisfcut condiia de continuare k(x1,...,xk). Condiiile de continuare rezult de obicei din ; ele sunt strict necesare, ideal fiind s fie i suficiente. Distingem urmtoarele cazuri posibile la alegerea lui xk: 1) Atribuie i avanseaz: mai sunt valori neconsumate din Xk i valoarea xk aleas satisface k se mrete k. 2) ncercare euat: mai sunt valori neconsumate din Xk i valoarea xk aleas dintre acestea nu satisface k se va relua, ncercndu-se alegerea unei noi valori pentru xk. 3) "Revenire": nu mai exist valori neconsumate din Xk (Xk epuizat) ntreaga Xk devine disponibil i kk-1. 4) "Revenire dup determinarea unei soluii": este reinut soluia. Reinerea unei soluii const n apelarea unei proceduri retsol care prelucreaz soluia (o tiprete, o compar cu alte soluii etc.) i fie oprete procesul (dac se dorete o singur soluie), fie prevede kk-1 (dac dorim s determinm toate soluiile.

41

Notm prin CkXk mulimea valorilor consumate din Xk. Algoritmul este urmtorul:
Ci, i; k1; while k>0 if k=n+1 then retsol(x); kk-1; { revenire dup obinerea unei soluii } else if CkXk then alege vXk\Ck; CkCk{v}; if k(x1,,xk-1,v) then xkv; kk+1; { atribuie i avanseaz } else { ncercare euat } else Ck; kk-1; { revenire }

Pentru cazul particular X1=...=Xn={1,,s}, algoritmul se simplific astfel:


k1; xi0, i=1,...,n while k>0 if k=n+1 then retsol(x); kk-1; else if xk<s then xkxk+1; if k(x1,,xk) then kk+1; else else xk0; kk-1;

{ revenire dup obinerea unei soluii }

{ atribuie i avanseaz } { ncercare euat } { revenire }

42

Exemple
n exemplele care urmeaz, k va fi notat n continuare prin cont(k). Se aplic algoritmul de mai sus pentru diferite forme ale funciei de continuare. 1) Colorarea hrilor. Se consider o hart. Se cere colorarea ei folosind cel mult n culori, astfel nct oricare dou ri vecine (cu frontier comun de lungime strict pozitiv) s fie colorate diferit. Fie xk culoarea curent cu care este colorat ara k.
function cont(k: integer): boolean; b true; i 1; while b and (i<k) if vecin(i,k) & xi=xk then b false else i i+1 cont b end;

2) Problema celor n dame Se consider un caroiaj nn. Prin analogie cu o tabl de ah (n=8), se dorete plasarea a n dame pe ptrelele caroiajului, astfel nct s nu existe dou dame una n btaia celeilalte (adic s nu existe dou dame pe aceeai linie, coloan sau diagonal). Evident, pe fiecare linie vom plasa exact o dam. Fie xk coloana pe care este plasat dama de pe linia k. Damele de pe liniile i i k sunt: - pe aceeai coloan: dac xi=xk ; - pe aceeai diagonal: dac |xi-xk|=k-i.
function cont(k:integer): boolean; b true; i 1; while b and i<k if |xi-xk|=k-i or xi=xk then b false else i i+1; cont b end;

43

3) Problema ciclului hamiltonian Se consider un graf neorientat. Un ciclu hamiltonian este un ciclu care trece exact o dat prin fiecare vrf al grafului. Pentru orice ciclu hamiltonian putem presupune c el pleac din vrful 1. Vom nota prin xi al i-lea vrf din ciclu. x=(x1,...,xn) soluie dac: x1=1 & {x2,...,xn}={2,...,n} & xn,x1 vecine. Vom considera c graful este dat prin matricea sa de adiacen.
function cont(k:integer): boolean; if a(xk-1,xk)=0 then cont false else i 1; b true; while b & (i<k) if xk=xi then b false else i i+1; if k=n then b b a(xn,x1)=1; cont b end;

44

O descriere succint a metodei backtracking


Metoda backtracking poate fi descris astfel: Backtracking = parcurgerea limitat *) n adncime a unui arbore
*)

conform condiiilor de continuare

Rolul condiiilor de continuare este ilustrat n figura ce urmeaz. Dac pentru xk este aleas o valoare ce nu satisface condiiile de continuare, atunci la parcurgerea n adncime este evitat parcurgerea unui ntreg subarbore.
x1

x2

xk

45

Variante
Variantele cele mai uzuale ntlnite n aplicarea metodei backtracking sunt urmtoarele: - soluia poate avea un numr variabil de componente i/sau - dintre ele alegem una care optimizeaz o funcie dat. Exemplu. a=(a1,,an)Zn. Caut un subir cresctor de lungime maxim. Deci caut 1x1<...<xkn cu
k maxim ax1 < ax2 <...< axk

Pentru n=8 i a=(1,4,2,3,7,5,8,6) va rezulta k=5. Aici, soluia posibil: care nu poate fi continuat; exemplu: (4,7,8). Notm prin xf i kf soluia optim i lungimea sa. Completez cu - i + : a0 -; nn+1; an +; Funcia cont are urmtoarea form: function cont(k) cont axk-1<ak end; Procedura retsol are forma: procedure retsol(k) if k>kf then xfx; kfk; end; Algoritmul backtracking se modific astfel:
k1; x00; x10; kf0; while k>0 if xk<n then xkxk+1; if cont(k) then if xk=n { an=+ } then retsol(k); kk-1 else kk+1; xkxk-1 else else kk-1;

Observaie. Se face tot o parcurgere limitat n adncime a unui arbore.

46

Varianta recursiv
O descriem pentru nceput pentru X1=...=Xn={1,...,s}. Apelul iniial este: back(1).
procedure back(k) if k=n+1 then retsol else for i=1,s xki; if cont(k) then back(k+1); revenire din recursivitate else end.

Exemplu. Dorim s producem toate irurile de n paranteze ce se nchid corect. Exist soluii n par. Fie dif=nr( - nr). Condiiile sunt : dif0 pentru k<n; dif=0 pentru k=n. Pornirea algoritmului backtracking se face prin:
a1( ; dif1; back(2);

Procedura back are urmtoarea form:


procedure back(k) if k=n+1 then retsol {scrie soluia} else ak( ; dif++; if dif n-k then back(k+1) dif--; ak); dif--; if dif0 then back(k+1) dif++; end.

Observaie. n exemplul tratat backtracking-ul este optimal! : se avanseaz dac i numai dac exist anse de obinere a unei soluii. Cu alte cuvinte, condiiile de continuare nu sunt numai necesare, dar i suficiente.

47

Metoda backtracking n plan


Se consider un caroiaj (matrice) A cu m linii i n coloane. Poziile pot fi: - libere: aij=0; - ocupate: aij=1. Se mai d o poziie (i0,j0). Se caut toate drumurile care ies n afara matricii, trecnd numai prin poziii libere. Variante: cum pot ajunge ntr-o poziie (i1,j1) dat? se cere determinarea componentelor conexe. Micrile posibile sunt date printr-o matrice depl cu dou linii i ndepl coloane. De exemplu, dac deplasrile permise sunt cele ctre poziiile vecine situate la Est, Nord, Vest i Sud, matricea are forma:
1 0 1 0 0 1 0 1

Bordez matricea cu 2 pentru a nu studia separat ieirea din matrice. Pentru refacerea drumurilor, pentru fiecare poziie atins memorm minte legtura la precedenta. Dac poziia e liber i pot continua, pun aij=-1 (a fost atins), continum i apoi repunem aij0 (ntoarcere din recursivitate). Programul n Java are urmtoarea form:
class elem { int i,j; elem prec; static int m,n,i0,j0,ndepl; static int[][] mat; static int[][] depl = { {1,0,-1,0}, {0,-1,0,1} }; static { ndepl = depl[0].length; } elem() { int i,j; IO.write("m,n = "); m = (int) IO.read(); n = (int) IO.read(); // de fapt m+2,n+2 mat = new int[m][n]; for(i=1; i<m-1; i++) for(j=1; j<n-1; j++) mat[i][j] = (int) IO.read(); for (i=0; i<n; i++) {mat[0][i] = 2; mat[m-1][i] = 2; } for (j=0; j<m; j++) {mat[j][0] = 2; mat[j][n-1] = 2; } IO.write("i0,j0 = "); i0 = (int) IO.read(); j0 = (int) IO.read(); } elem(int ii, int jj, elem x) { i = ii; j = jj; prec = x; } String print(elem x) { if (x == null) return "(" + i + "," + j + ")"; else return x.print(x.prec)+" "+"("+i+","+j+")"; }

48

void p() { elem x; int ii,jj; for (int k=0; k<ndepl; k++) { ii = i+depl[0][k]; jj = j+depl[1][k]; if (mat[ii][jj] == 1); else if (mat[ii][jj] == 2) IO.writeln(print(prec)); else if (mat[ii][jj] == 0) { mat[i][j] = -1; x = new elem(ii,jj,this); x.p(); mat[i][j] = 0; } } } } class DrumPlan { public static void main(String[] args) { new elem(); elem start = new elem(elem.i0,elem.j0,null); start.p(); } }

Contraexemplu. n plan, se cere s se determine toate punctele n care se poate ajunge din (i0,j0), precum i numrul minim de deplasri pn la ele.
0 0 0 0 4 1 3 2

(i0,j0)

Soluia corect const n a folosi o coad!

49

EXTINDEREA CLASELOR
Cum se definesc clasele extinse
Limbajul Java pune la dispoziie i o alt facilitate important legat de OOP : posibilitatea de extindere a claselor. Pe scurt, deci incomplet, aceasta const n: - o clas poate fi extins, adugndu-se noi cmpuri i noi metode, care permit considerarea unor atribute suplimentare (dac ne gndim la cmpuri) i unor operaii noi (asupra cmpurilor "iniiale", dar i asupra cmpurilor nou adugate); - unele metode ale clasei pe care o extindem pot fi redefinite, iar anumite cmpuri ale acestei clase pot fi "ascunse"; - un obiect avnd ca tip clasa extins poate fi folosit oriunde este ateptat un obiect al clasei care a fost extins. La prima vedere, rezolvarea problemelor de mai sus se poate face simplu: modificm clasa, introducnd sau/i modificnd cmpurile i metodele clasei. n practica programrii, aceast soluie este de neconceput. Pe de o parte se pot introduce erori, iar pe de alt parte utilizatorii clasei vechi nu vor mai putea folosi clasa aa cum o fceau nainte i cum vor n continuare s o fac; clasa (firma care a elaborat-o) i va pierde astfel vechii clieni. S reinem deci ca o regul general de programare faptul c nu trebuie modificat o clas testat i deja folosit de muli utilizatori; cu alte cuvinte, nu trebuie modificat "contractul" ce a dus la scrierea clasei. De aceea vechea clas trebuie s rmn nemodificat, iar actualizarea ei trebuie fcut prin mecanismul de extindere a claselor, prezentat n continuare. Exemplu. S presupunem c dorim s urmrim micarea unui punct n plan. Vom ncepe prin a considera clasa:
class Punct { int x,y; Punct urm; Punct(int x, int y) { this.x=x; this.y=y; } void Origine() { x=0; y=0; } Punct Miscare(int x, int y) { Punct p = new Punct(x,y); urm=p; return p; } } Clasa Punct conine: - cmpurile x, y, urm ; - constructorul Punct cu signatura (int, int); - metoda Origine cu signatura () i metoda Miscare cu signatura (int,int).

Vom extinde clasa Punct astfel nct punctul s aib i o culoare:


class Pixel extends Punct { String culoare; Pixel(int x, int y, String culoare) { super(x,y); this.culoare=culoare; } void Origine() { super.Origine(); culoare="alb"; } }

50

Clasa Pixel conine: - cmpurile x,y,urm (motenite de la clasa Punct) i culoare (care a fost adugat prin extindere); - constructorul Pixel cu signatura (int,int,String); - metoda Miscare cu signatura (int,int), motenit de la clasa Punct, precum i metoda Origine cu signatura (), care redefinete metoda Origine a clasei Punct. Este adoptat urmtoarea terminologie: clasa Punct este superclas a lui Pixel, iar Pixel este subclas (clas extins) a lui Punct. n Java, clasele formeaz o structur de arbore n care rdcina este clasa Object, a crei definiie apare n pachetul java.lang. Orice clas extinde direct (implicit sau explicit) sau indirect clasa Object. tim c n informatic arborii "cresc n jos", deci rdcina (clasa Object) se afl pe cel mai de sus nivel. Termenii de superclas i subclas se refer exclusiv la relaia tat fiu, conform relaiei de extindere, n arborele de clase. Este incorect de a interpreta aceti termeni n sensul de incluziune! n exemplul de mai sus, apelurile super.Origine() i super(x,y) se refer respectiv la metoda Origine i la constructorul din superclasa Punct a lui Pixel. Vom reveni ns cu toate detaliile legate de super. Fie Sub o clas ce extinde clasa Super. Sunt importante urmtoarele precizri: - obiectele de tip Sub pot fi folosite oriunde este ateptat un obiect de tipul Super. De exemplu dac un parametru al unei metode este de tipul Super, putem invoca acea metod cu un argument de tipul Sub; - spunem c Sub extinde comportarea lui Super; - o clas poate fi subclas a unei singure clase (motenire simpl) deoarece, reamintim, clasele formeaz o structur de arbore. Drept consecin o clas nu poate extinde dou clase, deci nu exist motenire multipl ca n alte limbaje (exist ns mecanisme pentru a simula aceast facilitate i anume interfeele); - o metod din superclas poate fi rescris n subclas. Spunem c metoda este redefinit (dac nu este static) sau ascuns (dac este static). Evident, ne referim aici la o rescriere a metodei folosind aceeai signatur, pentru c dac scriem n subclas o metod cu o signatur diferit de signaturile metodelor cu acelai nume din superclas, atunci va fi vorba de o metod nou. La rescriere nu putem modifica tipul valorii ntoarse de metod. Accesul la metoda cu aceeai signatur din superclas se face, dup cum vom vedea, prin precalificare cu super; - un cmp redeclarat n Sub ascunde cmpul cu acelai nume din Super; - cmpurile neascunse i metodele nerescrise sunt automat motenite de subclas (n exemplul de mai sus este vorba de cmpurile x i y i de metoda Miscare); - apelurile super din constructorii subclasei trebuie s apar ca prim aciune.

Despre iniializare i constructori


Suntem acum n msur s prezentm mai complet i aciunile ntreprinse la crearea unui obiect. Prima aciune const n a atribui cmpurilor valorile iniiale implicite:

51

- pentru tipuri numerice - pentru tipul char - pentru tipul boolean - pentru referine (la obiecte, inclusiv tablouri i String). n continuare este invocat constructorul corespunztor (determinat de signatur, n acelai mod ca pentru metode); aciunea sa cuprinde trei faze: 1) Invocarea unui constructor al superclasei i anume: - explicit, cu super(...) ; - implicit: este vorba de constructorul fr argumente. 2) Executarea, n ordinea n care apar, a iniializrilor directe ale cmpurilor; 3) Executarea corpului constructorului.
0 \u0000 false null

Observaii: - un constructor poate invoca un alt constructor i anume prin this(...); aceast invocare poate avea loc numai ca prim aciune a constructorului (deci nu pot fi invocai doi constructori). n schimb acest mecanism nu poate fi folosit pentru a invoca, din corpul unei metode, un constructor; - un constructor poate invoca o metod, caz n care se consider implementarea metodei corespunztoare tipului real al obiectului (vezi subcapitolul urmtor). Exemplu. S considerm clasele:
class a { int va=1, v; a() { v=va; } } class b extends a { int vb=2; b() { v=va+vb; } }

La crearea unui obiect de tipul b, prin new b(), au loc n ordine urmtoarele aciuni: - cmpurile v, va, vb primesc valoarea implicit 0; - este invocat constructorul b; - este invocat constructorul a (al superclasei); - are loc iniializarea direct a lui va, care primete valoarea 1; - este executat corpul constructorului a i ca urmare v primete valoarea 1; - are loc iniializarea direct a lui vb, care primete valoarea 2; - este executat corpul constructorului b i ca urmare v primete valoarea 3. Dac o clas este declarat cu modificatorul public, constructorul implicit are automat acest modificator. Constructorii unei clase nu sunt considerai membri ai clasei. De aceea ei nu pot fi motenii sau redeclarai. Constructorilor li se pot ataa modificatori de acces la fel ca metodelor (vezi subcapitolele urmtoare). Dar, spre deosebire de metode, un constructor nu poate fi declarat cu ali modificatori dect cei de acces. ntr-un constructor poate fi folosit instruciunea return, dar nensoit de o expresie.
52

Dac un constructor se invoc (direct sau indirect) pe el nsui, la compilare este semnalat o eroare. ntr-o invocare explicit a unui constructor, argumentele nu pot fi instanieri de cmpuri declarate n aceeai clas sau ntr-o superclas; de asemenea nu este permis utilizarea lui this i super n expresii. Exemplificm n continuare acest aspect pentru cmpuri. Exemplu. S considerm urmtoarea clas:
class C { int x; int y; C() { this(x); } C(int x) { y = x; } }

n clasa C, constructorul fr parametri invoc pe cel cu un parametru. Va fi semnalat o eroare la compilare. n schimb clasa va fi corect dac de exemplu cmpul x ar fi fost declarat cu modificatorul static.

Rescrierea metodelor i ascunderea cmpurilor


Cmpurile pot fi ascunse prin redeclararea lor ntr-o subclas. Cmpul cu acelai nume din superclas nu mai poate fi accesat direct prin numele su, dar ne putem referi la el folosind super (vezi subcapitolul urmtor) sau o referin la tipul superclasei. O metod declarat ntr-o clas poate fi rescris ntr-o subclas prin declararea ei n subclas cu acelai nume, aceeai signatur i acelai tip al valorii ntoarse. n metoda rescris putem schimba modificatorul de acces, cu condiia ca dreptul de acces s creasc; reamintim c modificatorii de acces, n ordine de la cel mai restrictiv la cel mai permisiv, sunt: private, protected, cel implicit (package) i public. Spunem c metodele rescrise sunt ascunse (dac e vorba de metode statice) sau redefinite (n cazul metodelor nestatice). Nu este vorba numai de o diferen de terminologie, deoarece metodele statice pot fi rescrise (ascunse) numai de metode statice, iar metodele nestatice pot fi rescrise (redefinite) numai de metode nestatice. Alte precizri vor fi fcute n continuare. Fie A o clas i fie B o subclas a sa. S considerm urmtoarele patru aciuni echivalente din punctul de vedere al obiectului a ce ia natere: 1) A a; a = new B(...); 2) A a = new B(...); 3) A a; B b; b = new B(...); a = b; 4) A a; B b; b = new B(...); a = (A) b; S observm c este vorba de o conversie de la o subclas la o superclas, conversie numit upcasting ; ea poate fi implicit (ca n primele trei aciuni) sau explicit (ca n cea de a patra aciune). Vom spune c obiectul a are tipul declarat A i tipul real B, ceea ce pune n eviden noiunea de polimorfism.

53

Tipul real al unui obiect coincide cu tipul su declarat (este cazul obiectului b) sau este o subclas a tipului declarat (vezi obiectul a). Fie camp un cmp al clasei A, ce este redeclarat (ascuns) n subclasa B. Dac obiectul a face referire la cmpul camp, atunci este vorba de cmpul declarat n clasa A, adic este folosit tipul declarat al obiectului. Fie met o metod a clasei A, care este rescris n subclasa B. La invocarea metodei met de ctre obiectul a, este folosit fie implementarea corespunztoare metodei ascunse (dac este static), fie cea corespunztoare metodei redefinite (dac este nestatic). Cu alte cuvinte, pentru metode statice este folosit tipul declarat (la fel ca pentru cmpuri), iar pentru metode nestatice este folosit tipul real al obiectului. n ambele cazuri, se pleac de la tipul indicat mai sus i se merge n sus spre rdcin (clasa Object) n arborele de clase pn cnd se ajunge la primul tip n care apare metoda respectiv (evident cu signatura corespunztoare); inexistena unui astfel de tip este semnalat chiar n faza de compilare. Exemplu. Urmtorul program:
class Super { static void met1() { IO.writeln("static_Super"); } void met2() { IO.writeln("Super"); } } class Sub extends Super { static void met1() { IO.writeln("static_Sub"); } void met2() { IO.writeln("Sub"); } } class Test1 { public static void main(String[] s) { Super Ob = new Sub(); Ob.met1(); Ob.met2(); } }

produce la ieire:
static_Super Sub

Exemplu. S considerm urmtorul program:


class A { String s="Super"; void scrie() { IO.writeln("A : "+s); } } class B extends A { String s="Sub"; void scrie() { IO.writeln("B : "+s); } } class AB1 { public static void main(String[] s) { B b = new B(); A a = b; a.scrie(); b.scrie(); IO.writeln(a.s + "\t" + b.s); }

54

La executare sunt tiprite urmtoarele:


B : Sub B : Sub Super Sub

adic rezultatele ateptate, innd cont c: - pentru obiectul b: tipul declarat coincide cu cel real i este B; - pentru obiectul a: tipul declarat este A, iar tipul real este B. S considerm o clas C i dou subclase X i Y ale sale, precum i urmtoarea secven de instruciuni:
C . . . . Ob; . . Ob = new X(...); . . Ob = new C(...); . . Ob = new Y(...); . . n care Ob are mai nti tipul real X, apoi tipul real C, apoi tipul real Y. Spunem c Ob

este o variabil polimorfic, deoarece are pe rnd forma (comportamentul) a mai multor clase.

Cuvntul cheie super


Putem folosi cuvntul cheie super n orice metod nestatic a unei clase extinse, i anume sub una dintre formele: - super(...) : pentru a invoca un constructor al superclasei clasei curente; - super.met(...) : pentru a invoca o metod a superclasei, metod ce a fost redefinit n clasa curent; - super.c : pentru a accesa un cmp c al superclasei, cmp ce a fost ascuns n clasa curent prin redefinirea sa. Observm c la accesarea unui cmp sau a unei metode, super acioneaz ca referin la obiectul curent ca instaniere a superclasei sale. Exemplu. Programul urmtor:
class A { String s="Super"; void scrie() { IO.writeln("A : "+s); } void metoda() { IO.writeln("A:"); } } class B extends A { String s="Sub"; void scrie() { IO.writeln("B : "+s+" "+super.s); } void metoda() { scrie(); super.scrie(); super.metoda(); } } class AB2 { public static void main(String[] x) { B b = new B(); b.metoda(); IO.writeln(b.s); IO.writeln("****************"); A a; a = b; a.metoda(); IO.writeln(a.s); IO.writeln("****************"); A c = new A(); c.metoda(); IO.writeln(c.s); } }

55

produce la ieire urmtorul rezultat:


B : Sub Super A : Super A: Sub **************** B : Sub Super A : Super A: Super **************** A: Super

Motenire, polimorfism i legare dinamic


Reamintim urmtoarele: - orice obiect care instaniaz o clas poate fi folosit n orice cod menit s lucreze cu instanieri ale superclaselor sale; - orice obiect care instaniaz o clas poate folosi (n modurile descrise mai sus) codul supraclasei. La aceste dou caracteristici legate de motenire (extinderea claselor), Java adaug polimorfismul i legarea dinamic, ntre care exist o strns relaie. Polimorfismul const n posibilitatea ca o invocare Ob.met(...) s aib drept consecin invocarea unei anumite metode cu numele met, n funcie de tipul real al obiectului Ob. Unii autori consider c i cele dou faciliti enunate mai sus fac parte din conceptul de polimorfism. Legarea dinamic identific metoda met respectiv la executarea programului. Polimorfismul i legarea dinamic sunt dou caracteristici eseniale ale programrii orientate pe obiecte i vor fi analizate n acest subcapitol. Aa cum tim, fiecare obiect are un tip declarat i un tip real; tipul real coincide cu cel declarat sau este un subtip al acestuia. La invocarea metodelor nestatice se folosete tipul real, iar la invocarea metodelor statice i la referirea cmpurilor se folosete tipul declarat. Pentru metode nestatice se folosete tipul real al obiectelor care le invoc deoarece: - cronologic, superclasa a fost scris naintea clasei i este de presupus c a fost folosit de mai muli utilizatori, care vor n continuare s lucreze cu ea; - n metodele noii clase se pot face referiri la cmpuri nou definite, ceea ce nu este posibil pentru metodele superclasei. Utilizarea tipului declarat pentru metodele statice se datoreaz urmtorului fapt: compilatorul "face tot ce poate" pentru a detecta metoda ce trebuie invocat. Java folosete legarea dinamic, adic identificarea metodei nestatice concrete care este folosit la o invocare se face la executare i nu la compilare. De aceea legarea dinamic se mai numete legare trzie. Necesitatea legrii dinamice apare din urmtorul exemplu:

56

Exemplu. Fie A o clas n care este definit o metod met, iar B o subclas a sa n care metoda met este redefinit. Secvena de instruciuni:
double d = IO.read(); A Ob; if (d>0) Ob = new A(...); else Ob = new B(...); Ob.met(...);

arat c doar la momentul invocrii metodei devine clar care dintre cele dou metode met va fi invocat.

Despre modificatori
Relum discuia despre modificatori, pentru a include i aspectele legate de extinderea claselor. Lista complet a modificatorilor folosii n Java este urmtoarea: - modificatorii de acces (public, protected, private i cel implicit); - abstract, static, final, synchronized, native, transient, volatile. Ei pot fi folosii astfel: - pentru clase: public,cel implicit, abstract, final; - pentru interfee: modificatorii de acces; - pentru constructori: modificatorii de acces; - pentru cmpuri: modificatorii de acces, final, static, transient, volatile; - pentru metode: modificatorii de acces, final, static, abstract, synchronized, native. Modificatorul final poate fi asociat (n afara variabilelor locale i cmpurilor) i metodelor i claselor. El trebuie neles n sensul de "variant final": - o metod cu acest modificator nu poate fi rescris ntr-o subclas; - o clas cu acest modificator nu poate fi extins. Metodele declarate cu private sau/i static sunt echivalente cu final, din punctul de vedere al redefinirii: nu pot fi redefinite. Dac o metod este final, "ne putem baza" pe implementarea ei (bineneles dac nu invoc metode nefinale). n unele situaii, este bine s marcm toate metodele cu final, dar nu i clasa.

57

Vizibilitate i acces
Pachetele, clasele, metodele, cmpurile i variabilele sunt identificate prin numele lor. Problema care se pune este de a stabili din ce loc putem face referire la aceste nume (cu semnificaia dorit), adic de a preciza domeniul lor de vizibilitate. Variabile locale i etichete

Prin bloc nelegem o secven (eventual vid) de instruciuni, cuprins ntre acolade. Un bloc este o instruciune i de aceea poate fi folosit n orice loc unde poate s apar o instruciune. De exemplu orice metod i orice constructor sunt formate dintr-un antet urmat de un bloc. n schimb ceea ce urmeaz dup modificatorii i numele unei clase nu constituie un bloc, dei apar acoladele deschis i nchis: coninutul unei clase nu este o succesiune de instruciuni. Orice bloc poate conine la rndul su un alt bloc, putnd astfel lua natere o structur imbricat de blocuri. Prin variabil local nelegem o variabil declarat n interiorul unui bloc, inclusiv ntr-o instruciune for. Domeniul de vizibilitate al unei variabile locale este constituit din partea din blocul n care a fost declarat, ce urmeaz declarrii. Prin urmare o variabil local nu poate fi referit nainte de a fi declarat. Variabilele locale exist numai pe perioada n care blocul n care sunt declarate este n curs de executare. Nu putem folosi facilitatea de imbricare a blocurilor pentru a redeclara o variabil local. O variabil local nu poate fi redefinit nici n blocul n care a fost declarat, nici ntr-un bloc inclus n acesta. Regulile care guverneaz domeniul de vizibilitate al etichetelor sunt aceleai ca pentru variabilele locale. Parametrii metodelor i constructorilor

Numele parametrilor dintr-o metod sau dintr-un constructor sunt vizibile doar n interiorul acestora, adic n blocul care urmeaz antetului metodei sau constructorului. Este ns posibil ca ntr-un bloc dintr-o metod sau dintr-un constructor s redeclarm numele unui parametru; n acest caz, n partea din bloc ce urmeaz redeclarrii, numele respectiv este considerat ca avnd semnificaia dat de redeclarare. Cu alte cuvinte, domeniul de vizibilitate al unui parametru este constituit din corpul metodei sau constructorului n care apare, mai puin blocurile (evident disjuncte) n care este redeclarat. Vizibilitatea n interiorul unei clase

tim c o clas este constituit din cmpuri, constructori (folosii la crearea de obiecte) i metode (folosite la invocarea lor). Din orice constructor i din orice metod putem s ne referim la orice cmp sau metod a clasei. De asemenea dintr-un constructor putem apela alt constructor prin this urmat, ntre paranteze, de o list de argumente ce respect signatura noului constructor.

58

Ordinea n care apar cmpurile nestatice i metodele n cadrul clasei nu este relevant (de exemplu nu conteaz dac un cmp nestatic este declarat la nceputul sau la sfritul clasei). Totui, dac numele unui cmp este redeclarat ntr-o metod (este folosit ca nume al unui parametru sau declarat ntr-un bloc), atunci declararea cea mai interioar este cea care primeaz cnd se face o referire la acel nume (identificator). Putem spune c un parametru poate ascunde un cmp, iar o variabil local poate ascunde un parametru i/sau un cmp. Dup cum vom vedea n continuare, numele unei metode nu poate fi ascuns. Modificatorul final

Modificatorul final poate fi ataat att variabilelor (locale sau cmpuri), ct i metodelor. n cazul variabilelor locale, variabilei i se atribuie valoare la declarare sau nainte de a fi folosit; variabila nu poate primi de dou ori valoare. Menionm c final este singurul modificator ce poate fi ataat unei variabile locale. Unui cmp final trebuie s i se atribuie valoare fie prin iniializare, fie prin constructori. Menionm c n general modificatorul final este folosit mpreun cu static, pentru a trata un cmp ca o "constant cu nume". Efectul modificatorului final asupra metodelor i claselor va fi prezentat atunci cnd vom vorbi despre extinderea claselor. Modificatorul static

Modificatorul static poate fi folosit la declararea cmpurilor i metodelor unei clase. O variabil local nu poate fi declarat cu static . Fie c un cmp declarat cu static n clasa Clasa. Aceast declarare are dou consecine: - cmpul c este comun tuturor obiectelor de tipul Clasa; - referirea la cmp din exteriorul clasei se face fie conform regulii generale (prin crearea i utilizarea unei instane a clasei Clasa), fie direct, prin precalificarea cmpului cu numele clasei (Clasa.c). Fie acum met o metod declarat cu modificatorul static n clasa Clasa; ea se numete metod static sau metod de clas. Invocarea ei se poate face fie conform regulii generale (prin crearea i utilizarea unei instane a clasei Clasa), fie direct, prin precalificarea metodei cu numele clasei: Clasa.met(...) ; De aceea ntr-o metod static nu poate fi folosit referina this. O metod static poate accesa numai cmpurile i metodele statice ale clasei din care face parte sau a altei clase. Metodele statice (de clas) sunt folosite de obicei pentru a efectua prelucrri asupra cmpurilor statice ale clasei. Putem "ocoli" ns aceast regul de exemplu astfel: - la invocarea metodei i transmitem ca parametru o referin la un obiect; - n metod construim explicit un obiect de tipul clasei la ale crei metode i cmpuri dorim s ne referim.

59

Modificatorii de acces

Toate cmpurile i metodele unei clase sunt accesibile (pot fi referite) din interiorul clasei. Rolul modificatorilor public, protected, private i cel implicit (numit uneori package) referitor la accesul din exteriorul unei clase la membrii si este sintetizat n urmtorul tabel: Membrii clasei cu acest modificator sunt accesibili ... public de oriunde clasa este accesibil protected din cod din acelai pachet implicit (package din cod din acelai pachet ) private numai din interiorul clasei Reamintim c modificatorii de mai sus joac un rol nu numai n privina accesului, dar i pentru facilitatea de extindere a claselor, ceea ce va face diferena ntre modificatorul protected i cel implicit. Determinarea semnificaiei unui nume Modificator

Pentru un identificator folosit ca nume de variabil (local sau cmp), metod sau clas, semnificaia sa se obine la prima cutare ncununat de succes din urmtorul ir de cutri succesive: 1) variabilele locale declarate ntr-un bloc sau un ciclu for; 2) numai dac suntem n interiorul unei metode sau a unui constructor: parametrii metodei sau constructorului; 3) membrii clasei, inclusiv cei rezultai prin extinderea claselor (vezi capitolul urmtor) 4) tipuri importate explicit; 5) tipuri declarate n acelai pachet; 6) tipuri importate implicit; 7) pachete disponibile pe sistemul gazd. Mai mult, se face distincie clar ntre tipurile de identificatori: nume de variabile (locale), de pachete, de clase, de metode i de cmpuri; iat de ce numele unei metode nestatice nu poate fi ascuns. Aceasta permite s folosim un acelai identificator pentru a numi una sau mai multe astfel de entiti. Exemplul 3. Urmtoarea clas este corect din punct de vedere sintactic, compilatorul nesemnalnd vreo eroare:
class obsesie { obsesie obsesie; obsesie() { obsesie = new obsesie(); } obsesie obsesie (obsesie obsesie) { obsesie: while (obsesie != null) if ( obsesie.obsesie(obsesie)==obsesie ) break obsesie; return obsesie; } }

60

Nu recomandm ns s abuzm n acest mod de distincia menionat. Clasa de mai sus poate fi scris sub urmtoarea form mai clar i "neobsesiv":
class obsesie { obsesie C; obsesie() { C = new obsesie(); } obsesie met (obsesie param) { et: while (param != null) { if ( param.met(param)==param ) break et; } return param; } }

61

Principiul lui Dirichlet


Principiul lui Dirichlet are un enun simplu i care nu necesit demonstraie: Dac m>kn obiecte sunt plasate n n csue, atunci va exista o csu ce va conine mai mult de k obiecte. Prezentm n continuare cteva aplicaii ale acestui principiu. Exemplul 1. Exist un numr de n perechi de pantofi de mrimi diferite, dar nearanjai pe perechi. Care este numrul minim de pantofi care trebuie cercetai pentru a forma o pereche ? Rspunsul este imediat. Folosim cte o cutie (iniial goal) pentru fiecare mrime. Este evident c dup aezarea n cutii a n+1 pantofi, o cutie va conine doi pantofi (m=n+1, k=1). Exemplul 2. Se consider un vector a=(a1,...,an) de numere naturale. Se caut indicii i<j cu ai+...+aj multiplu de n. n continuare, pentru orice numr natural x, vom nota prin x clasa sa de echivalen modulo n. Considerm sumele sk=a1+...+ak pentru k=1,...,n. Fie sk clasele de echivalen corespunztoare. Deosebim dou situaii: 1) dac exist k cu sk=0, atunci o soluie este (i,j)=(1,k); 2) n caz contrar, este clar c s1,...,sn{1,2,...,n-1}. Conform principiului lui Dirichlet, vor exista indicii k<l cu sk=sl. Atunci o soluie este (i,j)=(k+1,l). Exemplul 3. Pentru un numr natural n dat, caut un multiplu N al su n a crei scriere obinuit (n baza 10) apar numai cifrele 0 i 1. Problema este asemntoare celei precedente. Pentru k=1,...,n consider numrul sk a crui scriere n baza 10 este format din k de 1, adic s1=1, s2=11 etc.; fie sk clasa de echivalen modulo n corespunztoare. La fel ca mai sus, deosebim dou situaii: 1) dac exist k cu sk=0, atunci o soluie este chiar sk; 2) n caz contrar, este clar c s1,...,sn{1,2,...,n-1}. Conform principiului lui Dirichlet, vor exista indicii k<l cu sk=sl. Atunci o soluie este sl-sk, adic numrul n a crei scriere apar l-k de 1 urmai de k de 0. Exemplul 4. Teorema lui Erds. Se dau (m-1)(n-1)+1 numere naturale oarecare. S se arate c printre ele exist m care se divid unul pe altul, sau exist n care nu se divid ntre ele. Vom aplica tot principiul lui Dirichlet, dar n plan. Se ordoneaz mai nti cresctor numerele date. Consider un caroiaj cu n-1 linii i m-1 coloane. Mai consider c exist i linia imaginar cu numrul de ordine 0, pe care este plasat numrul 1.

62

Considerm pe rnd numerele (n ordine cresctoare). Fiecare numr va fi plasat pe linia i cu i minim i cu proprietatea c numrul curent se divide cu un numr aflat pe o linie anterioar. Pentru exemplificare, s considerm c m=5, n=4, iar numerele sunt: 3,5,9,12,14,15,33,x,... Dup plasarea primelor 8 numere, suntem n situaia: 3 9 24 5 12 14 15 33

Dac de exemplu x=72, atunci el ar trebui plasat pe a patra linie (inexistent) i am obine n=4 numere care se divid unul pe altul (de exemplu 3, 12, 24, 72). Dac de exemplu x=35, atunci el ar trebui plasat pe a doua linie i am obine m=5 numere care nu se divid ntre ele: 9, 12, 15, 33, 35. Este important de notat c pe fiecare linie numerele plasate apar n ordine cresctoare. Principiul lui Dirichlet ne asigur c cel mai trziu dup plasarea ultimului numr vom iei din "cutie": aici caroiajul (n-1)(m-1). O variant a principiului lui Dirichlet este urmtoarea: Fie k1,...kn , K=(k1+...+kn)/n i x un numr oarecare. Atunci: dac x<K i cu x<ki dac x>K i cu x>ki

Exemplul 5. Dac vrfurile unui decagon sunt etichetate distinct cu numerele 0,1,...,9 ntro ordine oarecare, atunci exist 3 vrfuri consecutive pentru care suma etichetelor este mai mare dect 13. Fie xi{0,1,...,9} eticheta vrfului i, pentru i=1,...,10. Considerm numerele: k1=x1+x2+x3 k2=x2+x3+x4 . . . . .
k9=x9+x10+x1 k10=x10+x1+x2

Atunci K=(k1+...+kn)/n = 3(0+1+...+9)/10 = 13,5. Conform principiului lui Dirichlet, va exista i cu ki>13,5>13. Exemplul 6. Se consider m calculatoare i n imprimante (m>n). Fie =numrul minim de legturi calculatorimprimant ce trebuie stabilite, astfel nct dac orice n calculatoare doresc s scrie simultan, acest lucru s fie este posibil. Se cere s se arate c n(m-n+1). Fie ki numrul de calculatoare legate la imprimanta i. Numrul de legturi este deci =(k1+...+kn).

63

Dac <n(m-n+1), atunci (k1+...+kn)/n<m-n+1. Conform variantei principiului lui Dirichlet, exist o imprimant i legat la cel mult m-n calculatoare, deci care nu este legat la cel puin n calculatoare; dac acestea vor s scrie simultan, nu vor reui. Contradicie. Exerciiu. S se descrie o modalitate prin care problema poate fi rezolvat cu exact n(mn+1) legturi.

64

Metoda Divide et Impera


Metoda Divide et Impera ("desparte i stpnete") const n mprirea repetat a unei probleme de dimensiuni mari n mai multe subprobleme de acelai tip, urmat de rezolvarea acestora i combinarea rezultatelor obinute pentre a determina rezultatul corespunztor problemei iniiale. Pentru fiecare subproblem procedm n acelai mod, cu excepia cazului n care dimensiunea ei este suficient de mic pentru a fi rezolvat direct. Este evident caracterul recursiv al acestei metode.

Schema general
Descriem schema general pentru cazul n care aplicm metoda pentru o prelucrare oarecare asupra elementelor unui vector. Funcia DivImp, care ntoarce rezultatul prelucrrii asupra unei subsecvene ap,...au, va fi apelat prin DivImp(1,n).
function DivImp(p,u) if up< then r Prel(p,u) else m Interm(p,u); r1 DivImp(p,m); r2 DivImp(m+1,u); r Combin(r1,r2) return r end; p u

unde: - funcia Interm ntoarce un indice n intervalul p..u (de obicei m=(p+u)/2 ). - funcia Prel este capabil s ntoarc rezultatul subsecvenei p..u, dac aceasta este suficient de mic; - funcia Combin ntoarce rezultatul asamblrii rezultatelor pariale r1 i r2. Exemple: Calculul maximului elementelor unui vector; Parcurgerile n preordine, inordine i postordine ale unui arbore binar; Sortarea folosind arbori de sortare.

65

Cutarea binar
Se consider vectorul a=(a1, ...,an) ordonat cresctor i o valoare x. Se cere s se determine dac x apare printre componentele vectorului. Problema enunat constituie un exemplu pentru cazul n care problema se reduce la o singur subproblem. innd cont de faptul c a este ordonat cresctor, vom compara pe x cu elementul din "mijlocul" vectorului. Dac avem egalitate, algoritmul se ncheie; n caz contrar vom cuta fie n "jumtatea" din stnga, fie n cea din dreapta. Vom aduga a 0 =-, an+1=+ . Cutm perechea (b,i) dat de: (true,i) dac ai=x; (false,i) dac ai-1<x<ai. Deoarece problema se reduce la o singur subproblem, nu mai este necesar s folosim recursivitatea. Algoritmul este urmtorul:
procedure CautBin p u; u n while pu i (p+u)/2 case ai>x : u i-1 ai=x : write(true,i); stop ai<x : p i+1 write(false,p) end

Algoritmul necesit o mic analiz, legat de corectitudinea sa parial. Mai precis, ne ntrebm: cnd se ajunge la p>u? pentru cel puin 3 elemente : nu se poate ajunge la p>u; pentru 2 elemente, adic pentru u=p+1: se alege i=p. Dac x<ai, atunci up-1. Se observ c se iese din ciclul while i ai-1<x<ai=ap; pentru un element, adic p=u: se alege i=p=u. Dac x<ai atunci up-1, iar dac x>ai atunci pu+1; n ambele cazuri se prsete ciclul while i tiprete un rezultat corect.

Problema turnurilor din Hanoi


Se consider 3 tije. Iniial pe tija 1 se afl n discuri cu diametrele decresctoare privind de la baz ctre vrf, iar pe tijele 2 i 3 nu se afl nici un disc. Se cere s se mute aceste discuri pe tija 2, ajutndu-ne i de tija 3, respectnd condiia ca n permanen pe orice tij sub orice disc s se afle baza tijei sau un disc de diametru mai mare.

66

O mutare este notat prin (i,j) i semnific deplasarea discului din vrful tijei i deasupra discurilor aflate pe tija j. Se presupune c mutarea este corect (vezi condiia de mai sus). Fie H(m;i,j) irul de mutri prin care cele m discuri din vrful tijei i sunt mutate peste cele de pe tija j, folosind i a treia tij, al crei numr este evident 6-i-j. Problema const n a determina H(n;1,2). Se observ c este satisfcut relaia: H(m;i,j)= H(m-1;i,j) (i,j) H(m-1;i,j) (*) respectndu-se condiia din enun. Deci problema pentru m discuri a fost redus la dou probleme pentru m-1 discuri, al cror rezultat este asamblat conform (*). Corespunztor, vom executa apelul Hanoi(n,1,2), unde procedura Hanoi are forma:
procedure Hanoi(n,i,j) if n=1 then write(i,j) else k6-i-j; Hanoi(n-1,i,k); Hanoi(1,i,j); Hanoi(n-1,k,j) end

Observaie. Numrul de mutri este 2n-1.

Sortare prin interclasare


Fie a=(a1,...,an) vectorul care trebuie ordonat cresctor. Ideea este urmtoarea: mprim vectorul n doi subvectori, ordonm cresctor fiecare subvector i asamblm rezultatele prin interclasare. Se observ c este aplicat tocmai metoda Divide et Impera. ncepem cu procedura de interclasare. Fie secvena de indici p..u i fie m un indice intermediar. Presupunnd c (ap,...,am) i (am+1,...,au) sunt ordonai cresctor, procedura Inter va ordona cresctor ntreaga secven (ap,...,au). Mai precis, vom folosi notaiile: k1 = indicele curent din prima secven; k2 = indicele curent din a doua secven; k3 = poziia pe care va fi plasat cel mai mic dintre ak1 i ak2 ntr-un vector auxiliar b.
procedure Inter(p,m,u) k1p; k2m+1; k3p; while k1m & k2u if ak1<ak2 then k1k1+1; bk3ak1 else k2k2+1; bk3ak2 k3k3+1

67

if k1>m { au fost epuizate elementelei primei subsecvene } then for i=k2,u bk3ai; k3k3+1 else { au fost epuizate elementelei primei subsecvene } for i=k1,m bk3ai; k3k3+1 for i=p,u aibi

end

Timpul de calcul este de ordinul O(u-p), adic liniar n lungimea secvenei analizate. Programul principal urmeaz ntocmai strategia Divide et Impera, deci se face apelul SortInter(1,n), unde procedura recursiv SortInter are forma:
procedure SortInter(p,u) if p=u then else m(p+u)/2; SortInter(p,m); SortInter(m+1,u); Inter(p,m,u) end

Calculm n continuare timpul de executare T(n), unde T(n) se poate scrie: t0 (constant), pen tru n=1; 2T(n/2)+an, pentru n>1, unde a este o constant: problema de dimensiune n s-a descompus n dou subprobleme de dimensiune n/2, iar combinarea rezultatelor s-a fcut n timp liniar (prin interclasare). Presupunem c n=2k. Atunci:

T(n) = T(2k) =2 T(2k-1) + a 2k = =2[2T(2k-2) + a 2k-1] + a 2k = 22T(2k-2) + 2 a 2k = =22[T(2k-3) + a 2k-2] + 2 a 2k = 2 3 T(2k-3) + 3 a 2k = . . . = 2iT(2k-i) + i a 2k = . . . =2kT(0) + k a 2k = nt0 + a n log 2 n.

Rezult c T(n)=0(n.log n). Se observ c s-a obinut acelai timp ca i pentru sortarea cu ansamble. Meniune. Se poate demonstra c acest timp este optim.

68

Metoda Quicksort
Prezentm nc o metod de sortare a unui vector a=(a1,...,an). Va fi aplicat tot metoda Divide et Impera. i de aceast dat fiecare problem va fi descompus n dou subprobleme mai mici de aceeai natur, dar nu va mai fi necesar combinarea (asamblarea) rezultatelor rezolvrii subproblemelor. Fie (ap,...,au) secvena curent care trebuie sortat. Vom poziiona pe ap n secvena (ap,...,au), adic printr-o permutare a elementelor secvenei: x=ap va trece pe o poziie k; toate elementele aflate la stnga poziiei k vor fi mai mici dect x; toate elementele aflate la dreapta poziiei k vor fi mai mari dect x. n acest mod ap va aprea pe poziia sa final, rmnnd apoi s ordonm cresctor elementele aflate la stnga sa, precum i pe cele aflate la dreapta sa. Fie poz funcia cu parametrii p,u care ntoarce indicele k pe care va fi poziionat ap n cadrul secvenei (ap,...,au). Atunci sortarea se realizeaz prin apelul QuickSort(1,n), unde procedura QuickSort are forma:
procedure QuickSort(p,u) if p=u then else k poz(p,u); QuickSort(p,k-1); QuickSort(k+1,n) end

Funcia poz lucreaz astfel:


function poz(p,u) ip; ju; ii0; jj-1 while i<j if ai<aj then else ai<aj; (ii,jj) (-ii,-jj) ii+ii; jj+jj (*) poz i end

S urmrim cum decurg calculele pentru secvena:


(a4,...,a11)=(6,3,2,5,8,1,9,7) se compar 6 cu a11,a10,... pn cnd gsim un element mai mic. Acesta este a9=1. Se interschimb 6 cu 1. Acum secvena este (1,3,2,5,8,6,9,7) i vom lucra n continuare pe subsecvena (3,2,5,8,6), schimbnd direcia de comparare conform (*); 6 va fi comparat succesiv cu 3,2,... pn cnd gsim un element mai mare. Acesta este a8=8. Se interschimb 6 cu 8.

69

Se obine astfel (1,3,2,5,6,8,9,7), n care la stnga lui 6 apar valori mai mici, iar la dreapta lui 6 apar valori mai mari, deci l-am poziionat pe 6 pe poziia 8, valoare ntoars de funcia poz. Observaie. Cazul cel mai defavorabil pentru metoda Quicksort este cel n care vectorul este deja ordonat cresctor: se compar a1 cu a2,...,an rezultnd c el se afl pe poziia final, apoi se compar a2 cu a3,...,an rezultnd c el se afl pe poziia final etc. Trecem la calculul timpul mediu de executare al algoritmului Quicksort. Vom numra cte comparri se efectueaz (componentele vectorului nu sunt neaprat numere, ci elemente dintr-o mulime ordonat oarecare). Timpul mediu este dat de formulele:
1 n T(n)= n 1 + [T(k 1)+ T(n k)] n k =1 T(1)= T (0)=0

deoarece: n cazul cel mai defavorabil a1 se compar cu celelalte n-1 elemente; a1 poate fi poziionat pe oricare dintre poziiile k=1,2,...,n; considerm aceste cazuri echiprobabile; T(k-1) este timpul (numrul de comparri) necesar ordonrii elementelor aflate la stnga poziiei k, iar T(n-k) este timpul necesar ordonrii elementelor aflate la dreapta poziiei k.
nT(n) = n(n-1)+2[T(0) +T(1)+.+T(n-1)] (n-1)T(n-1) = (n-1)(n-2)+2[T(0)+.+T(n-2)]

Scznd cele dou relii obinem:


nT(n)(n-1)T(n-1) = 2(n-1)+ 2T(n-1), dci: nT(n) = (n+1)T(n-1)+2(n-1). mpart cu n(n+1):

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

f(x)=ln x

Prin adunarea relaiilor de mai sus obinem:


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

2 3

n+1

Cum suma ultimilor doi termeni este negativ, rezult:


70

T(n) n +1 1 n dx = 2lnx| +1 2 ln(n+1) 2 n +1 x 2

(am folosit o inegalitate bazat pe sumele Rieman pentru funcia f(x)=ln x). Deci T(n)=0(n.log n). ncheiem cu meniunea c metoda Divide et Impera are o larg aplicativitate n calculul paralel.

71

Metoda programrii dinamice


Vom ncepe prin a enuna o problem general i a trece n revist mai muli algoritmi de rezolvare. Abia dup aceea vom descrie metoda programrii dinamice.

1. O problem general
Fie A i B dou mulimi oarecare. Fiecrui element xA urmeaz s i se asocieze o valoare v(x)B. Iniial v este cunoscut doar pe XA, X. Fiecrui xA\X i asociem: AxA : mulimea elementelor din A de a cror valoare depinde v(x); fx funcie care specific dependena de mai sus. Dac Ax={a1,...,ak}, : atunci v(x)=fx(v(a1),...,v(ak)). Se mai d zA. Se cere s se calculeze, dac este posibil, valoarea v(z). Exemplu.
A={1,2,...,12}; X={1,2,6,7,8,9}; A3={1,2}; A4={1,2,3}; A5={1,4}; A11={8,9}; A12={10,11}. A10={7,8}; Elementele din X au asociat valoarea 1. Fiecare funcie fx calculeaz v(x) ca fiind suma valorilor elementelor din Ax. z=5. Este evident c vom obine v=(1,1,2,4,5,1,1,1,1,1,2,2,4). O ordine posibil de a considera elementele lui A\X astfel nct s putem calcula valoarea asociat lor este: 3,10,11,12,4,2.

Lucrurile devin mai clare dac reprezentm problema pe un graf de dependene. Vrfurile corespund elementelor din A, iar descendenii unui vrf x sunt vrfurile din Ax. Vrfurile din X apar ngroate. 5 12 4 3 2 7 8 9 10 6 10 11

72

Problema enunat nu are totdeauna soluie, aa cum se vede pe graful de dependene de mai jos, n care exist un circuit. z=3. 3 1 2 4

Observaii: - A poate fi chiar infinit; - B este de obicei N, Z, R, {0,1} sau un produs cartezian; - fx poate fi un minim, un maxim, o sum etc. Pentru orice xA, spunem c x este accesibil dac, plecnd de la X, poate fi calculat valoarea v(x). Evident, problema are soluie dac i numai dac z este accesibil. Pentru orice xA, notm prin Ox mulimea vrfurilor observabile din x, adic mulimea vrfurilor y pentru care exist un drum de la y la x. Problema enunat are soluie dac i numai dac: 1) Oz nu are circuite; 2) vrfurile din Oz n care nu sosesc arce fac parte din X. Prezentm n continuare mai multe metode de rezolvare a problemei enunate.

Metoda irului cresctor de mulimi


Fie A o mulime finit i X o submulime a sa. Definim urmtorul ir cresctor de mulimi:
X0 = X Xk+1 = Xk {x AAx Xk}, k>0 Evident, X0 X1 ... Xk Xk+1 ...

Propoziie. Dac Xk+1=Xk, atunci Xk+i=Xk ,iN. Facem demonstraia prin inducie dup i. Pentru i=1 rezultatul este evident. Presupunem Xk+i=Xk i demonstrm c Xk+i+1=Xk : Xk+i+1 = cf. definiiei irului de mulimi
= = = = Xk+i {x A AxXk+i} = cf. ipotezei de inducie Xk {x A AxXk} = cf. definiiei irului de mulimi Xk+1 = cf. ipotezei de inducie Xk.

73

Consecine. ne oprim cu construcia irului cresctor de mulimi la primul k cu Xk=Xk+1 (A este finit!); - dac aplicm cele de mai sus pentru problema general enunat, aceasta are soluie dac i numai dac zXk.
-

Prezentm n continuare algoritmul corespunztor acestei metode, adaptat la problema general. Vom lucra cu o partiie A=UV, unde U este mulimea curent de vrfuri a cror valoare asociat este cunoscut.
U X; V A\X repeat W V for toi xV if AxU then U U {x}; V V\{x} calculeaz v(x) conform funciei fx if x=z then write v(x); stop until V=W { nu s-a avansat! } write(z, 'nu este accesibil)

Metoda descris are dou deficiene majore: - la fiecare reluare se parcurg toate elementele lui V; - nu este precizat o ordine de considerare a elementelor. Aceste deficiene fac ca aceast metod s nu fie performant. Metoda irului cresctor de mulimi este larg folosit n teoria limbajelor formale, unde de cele mai multe ori ne intereseaz existena unui algoritm i nu performanele sale. Sortarea topologic

Fie A={1,..,n} o mulime finit. Pe A este dat o relaie tranzitiv, notat prin "<". Relaia este dat prin mulimea perechilor (i,j) cu i<j. Se cere s se listeze elementele 1,..,n ale mulimii ntr-o ordine ce satisface cerina: dac i<j, atunci i apare la ieire naintea lui j. Problema enunat apare de exemplu la nscrierea unor termeni ntr-un dicionar astfel nct explicaiile pentru orice termen s conin numai termeni ce apar anterior. Este evident c problema se transpune imediat la grafurile de dependen: se cere o parcurgere a vrfurilor grafului astfel nct dac exist un arc de la i la j, atunci i trebuie vizitat naintea lui j.

74

Observaii: - problema are soluie dac i numai dac graful este aciclic; - dac exist soluie, ea nu este neaprat unic. n esen, algoritmul care urmeaz repet urmtorii pai: - determin i care nu are predecesori; - l scrie; - elimin perechile pentru care sursa este i. Fie M mulimea curent de vrfuri. Iniial M=A. Fie C coada elementelor din M care nu au predecesori. Pentru fiecare iA, considerm: Si = lista succesorilor lui i; nrpredi = numrul predecesorilor lui i din mulimea M curent. Etapa de iniializare const n urmtoarele:
Si , nrpredi0, i C ; nr 0 { nr este numrul elementelor produse la ieire } for k=1,m { m este numrul perechilor din relaia "<" } read(i,j) Si j; nrpredj nrpredj+1 for i=1,n if nrpredi=0 then i C

S observm c timpul cerut de etapa de iniializare este de ordinul O(m+n). Algoritmul propriu-zis, adaptat la problema general, este urmtorul:
while C i C; write(i); calculeaz v(x) conform funciei fx if i=z then write v(i); stop for toi jSi nrpredj nrpredj-1 if nrpredj=0 then j C if nr<n then write('Nu)

Fiecare executare a lui while necesit un timp proporional cu Si. Dar |S1|+...+|Sn|=m, ceea ce face ca timpul de executare s fie de ordinul O(m). innd cont i de etapa de iniializare, rezult c timpul total este de ordinul O(m+n), deci liniar. Totui, sortarea topologic aplicat problemei generale prezint un dezavantaj: sunt calculate i valori ale unor vrfuri "neinteresante", adic neobservabile din z.

75

ncercare cu metoda Divide et Impera


Este folosit o procedur DivImp, apelat prin DivImp(z).
procedure DivImp(x) for toi yAx\X DivImp(y) calculeaz v(x) conform funciei fx end;

Apare un avantaj: sunt parcurse doar vrfurile din Oz. Dezavantajele sunt ns decisive pentru renunarea la aceast ncercare: - algoritmul nu se termin pentru grafuri ciclice; - valoarea unui vrf poate fi calculat de mai multe ori, ca de exemplu pentru situaia:

Soluie final
-

Etapele sunt urmtoarele: Identificm Gz = subgraful asociat lui Oz ; Aplicm sortarea topologic. Fie Gz=(Xz,Mz). Iniial Xz=, Mz=. Pentru a obine graful Gz executm apelul DF(z), unde procedura DF este:

procedure DF(x) x Xz for toi yAx if yXz then (y,x) Mz; DF(y) end;

Timpul este liniar. Observaie. Ar fi totui mai bine dac : - am cunoate de la inceput Gz; - forma grafului ar permite o parcurgere mai simpl.

76

2. Metoda programrii dinamice


Definim un PDarbore de rdcin z ca fiind un graf de dependene n care:
x, xOz (exist drum de la x la z); X={xgrad-(x)=0}.

Exemplu. Urmtorul graf este un PD-arbore de rdcin z=5. 5 5 4 4 3 1 2 3

1 2 Un PD-arbore nu este neaprat un arbore, dar: - poate fi pus pe niveluri: fiecare vrf x va fi pus pe nivelul egal cu lungimea celui mai lung drum de la x la z; - poate fi parcurs (cu mici modificri) n postordine; Prin parcurgerea n postordine, vrfurile apar sortate topologic. Algoritmul de parcurgere n postordine folosete un vector parcurs pentru a ine evidena vrfurilor vizitate. Este iniializat vectorul parcurs i se ncepe parcurgerea prin apelul postord(z):
for toate vrfurile x parcurs(x) xX postord(z)

unde procedura postord are forma:


procedure postord(x) for toi jAx cu parcurs(j)=false postord(j) calculeaz v(x) conform funciei fx; parcurs(x)true end

Timpul de executare a algoritmului este evident liniar. Metoda programrii dinamice se aplic problemelor care urmresc calcularea unei valori i const n urmtoarele: 1) Se asociaz problemei un graf de dependene; 2) n graf este pus n eviden un PD-arbore; problema se reduce la determinarea valorii asociate lui z (rdcina arborelui); 3) Se parcurge n postordine PD-arborele.

77

Mai pe scurt, putem spune: Metoda programrii dinamice const n identificarea unui PD-arbore i parcurgerea sa n postordine. n multe probleme este util s cutam n PD-arbore regulariti care s evite memorarea valorilor tuturor vrfurilor. Vom ncepe cu cteva exemple foarte simple, dar care pun n eviden anumite caracteristici ale metodei programrii dinamice. Exemplul 1. irul lui Fibonacci tim c acest ir este definit astfel:
F0=0; F1=1; Fn = Fn-1 + Fn-2 , n2 Dorim s calculm Fn pentru un n oarecare.

Aici A={0,...,n}, X={0,1}, B=N, iar Ak={k-1,k-2}, k2 v(k)=Fk ; fk(a,b)=a+b, k2 Un prim graf de dependene este urmtorul: 0 1

n-2

n-1

n=z

S observm c o mai bun alegere a mulimii B simplific structura BDarborelui.


A={1,2,.,n}; B=NN; v(k)=(Fk-1, Fk); fk(a,b)=(b,a+b) v(0)=(0,1). n-1 1 2 3

i obinem algoritmul binecunoscut:


a0; b1 for i=2,n (a,b)(b,a+b) write(b)

78

Exemplul 2. Calculul sumei a1+ ...+an Este evident c trebuie calculate anumite sume pariale. O prim posibilitate este s considerm un graf n care fiecare vrf s fie o submulime {i1,...,ik} a lui {1,2,...,n}, cu valoarea asociat ai1+...+aik. Aceast abordare este nerealizabil: numrul de vrfuri ar fi exponenial. O a doua posibilitate este ca vrfurile corespund mulimilor {i,i+1,...,j} cu ij i cu valoarea ataat si,j=ai+...+aj. Vom nota un astfel de vrf prin (i:j). Dorim s calculm valoarea vrfului z=(1:n). Putem considera mai muli PD-arbori: Arborele liniar constituit din vrfurile cu i=1. Obinem relaiile de recuren: s1,1=a1; s1,j=s1,j-1+aj , ji care corespund asociativitii la stnga: (...((a1+a2)+a3)+...). Arborele liniar constituit din vrfurile cu j=n. Acest arbore corespunde asociativitii la dreapta a sumei. Arborele binar strict n care fiecare vrf (afar de frunze) are descendenii (i:k) i (k+1:j) cu k=(i+j)/2. Prezentm acest arbore pentru n=7: (1:7)

(1:4)

(5:7)

(1:2)

(3:4)

(5:6)

(1:1)

(2:2)

(3:3)

(4:4)

(5:5)

(6:6)

(7:7)

Relaiile de recuren sunt:


sii=ai si,j=sik+sk+1,j pentru i<j.

iar algoritmul este urmtorul:


k1 while k<n k2k+k; i1 while i+kn aiai+ai+k; ii+k2 k k2

79

Rezultatul este obinut n a1. Evoluia calculelor apare n urmtorul tabel:


n 7 k2 2 k 1 i 1 3 5 7 1 5 9 1 9 a1a1+a2 a3a3+a4 a5a5+a6 a1a1+a3 a5a5+a7 a1a1+a5

Algoritmul de mai sus nu este att de stupid i inutil pe ct apare la prima vedere pentru o problem att de simpl. Calculele pentru fiecare reluare a ciclului while interior sunt executate asupra unor seturi de date disjuncte. De aceea, n ipoteza c pe calculatorul nostru dispunem de mai multe procesoare, calculele pe fiecare nivel al arborelui (mergnd de jos n sus) pot fi executate n paralel. Drept urmare, timpul de calcul va fi de ordinul O(log n), deci sensibil mai bun dect cel secvenial, al crui ordin este O(n). Exemplul 3. Deteminarea subirului cresctor de lungime maxim. Se consider vectorul a=(a1,...,an). Se cer lungimea celui mai lung ir cresctor, precum i toate subirurile cresctoare de lungime maxim. Introducem notaiile: nr = lungimea maxim cutat; lung(i)= lungimea maxim a subirului cresctor ce ncepe cu ai. A={1,2,...,n}; X={n}; Ai={i+1,...,n}, i<n;
Evident, suntem n prezena unui PD-arbore de rdcin 1.

fi=lung(i)

Determinarea lui nr se face astfel:


nr 1; lung(n)1 for i=n-1,1,-1 lung(i)1+max{lung(j)j>i & ai<aj} nr max{nr,lung(i)}

Determinarea tuturor subirurilor cresctoare de lungime maxim se face printrun backtracking recursiv optimal. Subirurile se obin n vectorul s, iar ind reprezint ultima poziie completat din s.
for i=1,n if lung(i)=nr then ind 1; s(1)ai; scrie(i)

unde procedura scrie are forma:


80

procedure scrie(i) if ind=nr then write(s) else for j=i+1,n if ai<aj & lung(i)=1+lung(j) then ind++; s(ind)a(j); scrie(j); indind-1 end;

Observaie. Dac dorim s obinem un singur subir cresctor de lungime nr, este suficient s eliminm instruciunea indind-1. Exemplul 4. nmulirea optim a unui ir de matrici. Avem de calculat produsul de matrici A1A2...An, unde dimensiunile matricilor sunt respectiv (d1,d2), (d2,d3), ... ,(dn,dn+1). tiind c nmulirea matricilor este asociativ, se pune problema ordinii n care trebuie nmulite matricile astfel nct numrul de nmuliri elementare s fie minim. Presupunem c nmulirea a dou matrici se face n modul uzual, adic produsul matricilor A(m,n) i B(n,p) necesit mnp nmuliri elementare.
Pentru a pune n eviden importana ordinii de nmulire, s considerm produsul de matrici A1A2A3A4 unde A1(100,1), A2(1,100), A3(100,1), A4(1,100). Pentru ordinea de nmulire (A1A2)(A3A4) sunt necesare 1.020.000 de nmuliri elementare. n schimb, pentru ordinea de nmulire (A1(A2A3))A4 sunt

necesare doar 10.200 de nmuliri elementare. Fie cost(i,j) numrul minim de nmuliri elementare pentru calculul produsului Ai...Aj. Punnd n eviden ultima nmulire de matrici, obinem relaiile:
cost(i,i) = 0 cost(i,j) = min {cost(i,k)+cost(k+1,j)+didk+1dj+1 | ik<j}. Valoarea cerut este cost(1,n).

Vrfurile grafului de dependen sunt perechile (i,j) cu ik. cost(i,j) depinde de vrfurile din stnga i de cele de deasupra. Se observ uor c suntem n prezena unui PD-arbore.
j 1 n j

(i,i)

(i,j) (j,j)

81

Forma particular a PD-arborelui nu face necesar aplicarea algoritmului general de parcurgere n postordine: este suficient s parcurgem coloanele 2,...,n, iar pe fiecare coloan s mergem de la diagonal pn la (i,j). for j=2,n
for i=j-1,1,-1 cost(i,j) calculat ca mai sus; fie k valoarea pentru care se realizeaz minimul cost(j,i)k write cost(1,n)

(se observ c am folosit partea inferior triunghiular a matricii pentru a memora indicii pentru care se realizeaz minimul). Dac dorim numai costul final, nu este nevoie s memorm ntreaga matrice, ci este suficient s folosim un vector. Dac dorim s producem i o ordine de nmulire optim, avem nevoie de ntreaga matrice. Vom apela sol(1,n), unde procedura sol are forma:
procedure sol(p,u) if p=u then write(p) else kcost(u,p) write('('); sol(p,k); write(','); sol(k+1,u); write(')') end;

Pentru evaluarea timpului de lucru, vom calcula numrul de comparri efectuate. Aceste este:
j=2i=1 (j1)(j2) = O(n3) (j i + 1)= j(j 1) 2 j=2 n j1 n

Exemplul 5. Descompunerea unui dreptunghi n ptrate Se consider un dreptunghi cu laturile de m, respectiv n uniti (m<n). Asupra sa se pot face tieturi complete pe orizontal sau vertical. Se cere numrul minim de ptrate n care poate fi descompus dreptunghiul. Fie aij = numrul minim de ptrate n care poate fi descompus un dreptunghi de laturi i i j. Evident aij=aji. Rezultatul cutat este amn. Vrfurile grafului de dependene sunt (i,j), iar valorile asociate sunt aij .
k k i i-k j j-k

82

Pentru calculul lui aij avem de ales ntre a face: - o tietur pe vertical; costurile sunt: aik+ai,j-k, k j/2 ; - o tietur pe orizontal; costurile sunt: ak,j+ai-k,j, k i/2 . Rezult c valoarea aij a unui vrf (i,j) depinde de valorile vrfurilor din stnga sa i de cele aflate deasupra sa. Se observ c graful de dependene este un PDarbore.
j

(i,j)

Dependenele pot fi exprimate astfel:


ai,1=i, i=1,...,m a1,j=j, j=1,,n aii=1, =1,...,m aij = min{,}, unde =min{aik+ai,j-k | k j/2} , iar =min{ ak,j+ai-k,j, | k i/2 }.

Forma particular a PD-arborelui permite o parcurgere mai uoar dect aplicarea algoritmului general de postordine. De exemplu putem cobor pe linii, iar pe fiecare linie mergem de la stnga la dreapta. Dup iniializrile date de primele 3 dependene de mai sus, efectum calculele:
for i=2,m for j=i+1,n

calculul lui aij conform celei de a patra dependene de mai sus


if jm then ajiaij

Observaie. Am lucrat numai pe partea superior triunghiular, cu actualizri dedesubt.

83

Drumuri n grafuri
Fie G=(V,M) graf orientat cu n=|V|, m=|M|. Fie A matricea sa de adiacen. Considerm irul de matrici:
A 1 = A k A = A k-1A, k 2

a crui semnificaie este urmtoarea: Propoziia 1. Ak-1(i,j) = numrul drumurilor de lungime k de la i la j. - pentru k=1: evident. k-1 k : A k(i,j)= A k1(i,s) A(s,j), unde s este penultimul vrf din
s=1 n

drumul de la i la j. n continuare dorim s determinm numai existena drumurilor de lungime k. Considerm irul de matrici:
A(1) = A (k) (k-1) A = A A, k 2

unde A(k) j)= A(k1) s) A(k1) j) (i, (i, (s,


s=1

a crui semnificaie este urmtoarea (elementele matricilor sunt 0 sau 1): Propoziia 2. Ak-1(i,j)=1 exist drum de lungime k de la i la j. Demonstraia se face prin inducie ca mai sus. Definim matricea drumurilor D prin: D(i,j)=1 drum de la i la j. D=A(1)...A(n-1), deoarece dac exist un drum de la i la j, exist i un drum de lungime cel mult egal cu n-1 de la i la j. Construciile matricilor de mai sus necesit un timp de ordinul O(n4). Vom cuta s obinem un timp de executare mai bun, inclusiv pentru cazul n care lungimea arcelor este oarecare (n cele de mai sus s-a presupus implicit c arcele au lungimea egal cu 1).

84

n continuare, fiecare arc <i,j> va avea o etichet et(<i,j>) strict pozitiv, ce reprezint lungimea arcului. et(< i,j >) dac < i,j > M Consider P(i,j)= 0 dac i = j + altfel i irul de matrici:
P0 = P Pk(i,j)= min{Pk1(i,j),Pk1(i,k)+ Pk1(k,j)}, k 1

Propoziia 3. Pn este matricea celor mai scurte drumuri. Vom demonstra prin inducie dup k urmtoarea afirmaie: Pk(i,j) = lungimea celui mai scurt drum de la i la j n care numerele de ordine ale nodurilor intermediare sunt cel mult egale cu k. - pentru k=0: evident. - k-1 k : Considerm un drum de lungime minim de la i la j. Dac drumul nu trece prin k, Pk(i,j)=Pk-1(i,j). Dac drumul trece prin k, el va trece o singur dat prin k (are lungime minim) i n drumurile de lungime minim de la i la k i de la k la j vrful k nu apare ca vrf intermediar, deci Pk(i,j)=Pk-1(i,k)+Pk-1(k,j). i k j Observaii: 1) s-a folosit metoda programrii dinamice; 2) Pk(i,i)=0; 3) Pk(i,k)=Pk-1(i,k) i Pk(k,j)=Pk-1(k,j), deci la trecerea de la Pk-1 la Pk linia k i coloana k rmn neschimbate. Rezult c putem folosi o singur matrice. Algoritmul se simplific astfel:
for k=1,n for i=i,n for j=1,n P(i,j) min {P(i,j),P(i,k)+P(k,j)} Timpul de executare este evident de ordinul O(n3).

85

Dac dorim s determinm doar existena drumurilor i nu lungimea lor minim, vom proceda similar. Considerm irul de matrici:
A 0 = 0 A k(i,j)= A k1(i,j)[A k1(i,k) A k1(k,j)] , k 0

Propoziia 4. An este matricea drumurilor. Demonstrm prin inducie dup k urmtoarea afirmaie: Ak(i,j)=1 drum de la i la j cu numerele de ordine ale vrfurilor intemediare egale cu cel mult k. - pentru k=0: evident; - k-1 k: Dac A(i,j)=1, atunci fie Ak-1(i,j)=1, fie Ak-1(i,k)=Ak-1(k,j)=1; n ambele situaii va exista, conform ipotezei de inducie, un drum de la i la j cu numerele de ordine ale vrfurilor intemediare egale cu cel mult k. Dac exist un drum de la i la j cu numerele de ordine ale vrfurilor intemediare egale cu cel mult k, prin eliminarea ciclurilor drumul va trece cel mult o dat prin k. Este suficient n continuare s considerm cazul n care drumul trece prin vrful k i cazul n care drumul nu trece prin k. Sunt valabile aceleai observaii ca la Propoziia 3, iar algoritmul are o form similar:
DA for k=1,n for i=1,n for j=1,n D(i,j) D(i,j) D(i,k)D(k,j) Timpul de executare este evident de ordinul O(n3).

86

n continuare ne vor interesa numai drumurile ce pleac dintr-un vrf x0 fixat. Este de ateptat ca timpul de executare s scad. Mai precis, cutm d(x) = lungimea drumului minim de la x0 la x, pentru orice vrf x. n plus, dorim s determinm i cte un astfel de drum. Prezentm n continuare algoritmul lui Dijkstra pentru problema enunat. Pentru simplificare, presupunem c orice vrf este accesibil din x0. Pentru regsirea drumurilor vom folosi vectorul tata. Perechiile (d(x),tata(x)) sunt iniializate astfel: - (0,0) pentru x=x0; - (et(<x0,x>),x0) dac <x0,x>M; altfel. - + Fie T = mulimea vrfurilor x pentru care d(x) are valoarea final.
T {x0} while TV Fie xV\T cu d(x) minim T T{x} for toi xT if d(x)>d(x)+et(<x,x>) then d(x) d(x)+et(<x,x>); tata(x)x

Pentru a demonstra corectitudinea algoritmului, vom arta prin inducie dup |T| c: 1) xT : d(x) = lungimea celui mai scurt drum de la x0 la x; 2) xT : d(x) = lungimea celui mai scurt drum de la x0 la x, ce trece numai prin vrfuri din T.
T
x

Pentru |T|=1, concluzia este evident. x0 |T| |T|+1: Fie x' vrful nou adugat. x' Demonstrm cele dou afirmaii de mai sus: 1) d(x) este cel final: Presupunem prin absurd c exist un drum de la x0 la x de lungime mai mic dect d(x). Acest drum trebuie s treac printr-un vrf yT.
x0 yT x

Evident d(y)<d(x). Contradicie, pentru c a fost ales x cu d(x) minim. 2) Conform actualizrilor (*) efectuate de algoritm. 1) 2) Observaii. Timpul de executare este de ordinul O(n2). Pe baza vectorului tata, putem regsi pentru orice xV drumul minim ce l leag de x0 n timp liniar. Regsirea tuturor acestor drumuri necesit un timp de ordinul O(n2), deci complexitatea n timp a algoritmului nu crete. Dac dorim numai drumul minim de la x0 la un vrf x1 dat, ne oprim cnd x=x1; aceasta nu implic ns o reducere a ordinul de mrime al timpului de calcul. Algoritmul de mai sus poate fi ncadrat la metoda Greedy, dar i la metoda irului cresctor de mulimi.

3) 4)

87

Metoda Branch and Bound


Prezentare general
Metoda Branch and Bound se aplic problemelor care care pot fi reprezentate pe un arbore: se ncepe prin a lua una dintre mai multe decizii posibile, dup care suntem pui n situaia de a alege din nou ntre mai multe decizii; vom alege una dintre ele etc. Vrfurile arborelui corespund strilor posibile n dezvoltarea soluiei. Deosebim dou tipuri de probleme: 1) Se caut un anumit vrf, numit vrf rezultat, care evident este final (nu are descendeni). 2) Exist mai multe vrfuri finale, care reprezint soluii posibile, dintre care cutm de exemplu pe cel care optimizeaz o anumit funcie. Exemplul 1. Jocul 15 (Perspico). Un numr de 15 plcue ptrate sunt incorporate ntr-un cadru 44, o poziie fiind liber. Fiecare plcu este etichetat cu unul dintre numerele 1,2,...,15. Prin configuraie nelegem o plasare oarecare a plcuelor n cadru. Orice plcu adiacent cu locul liber poate fi mutat pe acest loc liber. Dndu-se o configuraie iniial i una final, se cere o succesiune de mutri prin care s ajungem din configuraia iniial n cea final. Configuraiile iniial i final pot fi de exemplu: 1 5 9 13 2 6 14 3 7 10 15 4 8 11 12 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12

unde locul liber mai poate fi considerat drept coninnd plcua imaginar cu eticheta 16. Prezentm nti o condiie de existen a unei succesiuni de mutri prin care se poate trece de la configuraia iniial n cea final. Cele 16 locauri sunt considerate ca fiind ordonate de la stnga la dreapta i de jos n sus. Pentru plcua etichetat cu i definim valoarea n(i) ca fiind numrul locaurilor care urmeaz celei pe care se afl plcua i care conin o plcu a crei etichet este mai mic dect i. De exemplu pentru configuraia iniial de mai sus avem: n(8)=1; n(4)=0; n(16)=10; n(15)=1 etc. Fie l i c linia i coloana pe care apare locul liber. Fie x{0,1} definit astfel: x=0 dac i numai dac l+c este par. Se poate demonstra urmtorul rezultat: Propoziie. Fiind dat o configuraie iniial, putem trece din ea la configuraia final de mai sus n(1)+n(2)+...+n(16)+x este par. n continuare vom presupune c putem trece de la configuraia iniial la cea final. Cum locul liber poate fi mutat spre N, S, E, V (fr a iei din cadru), rezult c fiecare configuraie (stare) are cel mult 4 descendeni. Se observ c arborele astfel construit este infinit. Strile finale sunt stri rezultat i corespund configuraiei finale.
88

Exemplul 2. Circuitul hamiltonian de cost minim. Se consider un graf orientat cu arcele etichetate cu costuri pozitive. Inexistena unui arc ntre dou vrfuri este identificat prin "prezena" sa cu costul +. Presupunem c graful este dat prin matricea C a costurilor sale. Se cere s se determine, dac exist, un circuit hamiltonian de cost minim. S considerm de exemplu graful dat de matricea de costuri: 3 7 2 5 1 9 C = 4 8 3 6 2 6 Arborele spaiului de stri, n care vrfurile corespund vrfurilor din graf, iar muchiile reprezint arce din graf, este urmtorul:
1

(1,2)
2

(1,3)
3

(1,4)
4

(2,3)
5

(2,4)
6

(3,2)
7

(3,4)
8

(4,2)
9

(4,3)
10

(3,4)
11

(4,5)
12

(2,4)
13

(4,2)
14

(2,3)
15

(3,2)
16

subnelegndu-se c se pleac din vrful 1 i c din frunze se revine la acest vrf. Revenim la descrierea metodei Branch and Bound. tim c i metoda backtracking este aplicabil problemelor reprezentabile pe arbori. Exist ns multe deosebiri, dintre care menionm urmtoarele: - ordinea de parcurgere a arborelui; - modul n care sunt eliminai subarborii care nu pot conduce la o soluie; - faptul c arborele poate fi infinit (prin natura sa sau prin faptul c mai multe vrfuri pot corespunde la o aceeai stare). n general arborele de stri este construit dinamic. Este folosit o list L de vrfuri active, adic de stri care sunt susceptibile de a fi dezvoltate pentru a ajunge la soluie (soluii). Iniial, lista L conine rdcina arborelui, care este vrful curent. La fiecare pas, din L alegem un vrf (care nu este neaprat un fiu al vrfului curent!), care devine noul vrf curent.

89

Cnd un vrf activ devine vrf curent, sunt generai toi fiii si, care devin vrfuri active (sunt inclui n L). Apoi din nou este selectat un vrf curent. Legat de modul prin care alegem un vrf activ drept vrf curent, deci implicit legat de modul de parcurgere a arborelui, facem urmtoarele remarci: parcurgerea DF nu este adecvat, deoarece pe de o parte arborele poate fi infinit, iar pe de alt parte soluia cutat poate fi de exemplu un fiu al rdcinii diferit de primul fiu i parcurgerea n adncime ar fi ineficient: se parcurg inutil stri, n loc de a avansa direct spre soluie; parcurgerea pe lime conduce totdeauna la soluie (dac aceasta exist), dar poate fi ineficient dac vrfurile au muli fii. Metoda Branch and Bound ncearc un "compromis" ntre cele dou parcurgeri menionate mai sus, atand vrfurilor active cte un cost pozitiv, ce intenioneaz s fie o msur a gradului de "apropiere" a vrfului de o soluie. Alegerea acestui cost este decisiv pentru a obine un timp de executare ct mai bun i depinde att de problem, dar mai ales de abilitatea programatorului. Observaie. Totdeauna costul unui vrf va fi mai mic dect cel al descendenilor (fiilor). De fiecare dat drept vrf curent este ales cel de cost minim (cel considerat ca fiind cel mai "aproape" de soluie). De aceea L va fi n general un min-ansamblu: costul fiecrui vrf este mai mic dect costul descendenilor. Din analiza teoretic a problemei deducem o valoare lim care este o aproximaie prin adaos a minimului cutat: atunci cnd costul unui vrf depete lim, atunci vrful curent este ignorat: nu este luat n considerare i deci este eliminat ntregul subarbore pentru care este rdcin. Dac nu cunoatem o astfel de valoare lim, o iniializm cu +. Se poate defini o funcie de cost ideal, pentru care c(x) este dat de: nivelul pe care se afl vrful x dac x este vrf rezultat; + dac x este vrf final, diferit de vrf rezultat; min {c(y) | y fiu al lui x } dac x nu este vrf final. Aceast funcie este ideal din dou puncte de vedere: nu poate fi calculat dac arborele este infinit; n plus, chiar dac arborele este finit, el trebuie parcurs n ntregime, ceea ce este exact ce dorim s evitm; dac totui am cunoate aceast funcie, soluia poate fi determinat imediat: plecm din rdcin i coborm mereu spre un vrf cu acelai cost, pn ajungem n vrful rezultat.

Neputnd lucra cu funcia ideal de mai sus, vom alege o aproximaie a lui c, care trebuie s satisfac condiiile: 1) n continuare, dac y este fiu al lui x avem (x)<(y); 2) (x) s poat fi calculat doar pe baza informailor din drumul de la rdcin la x ; 3) este indicat ca c pentru a ne asigura c dac (x)>lim, atunci i c(x)>lim, deci x nu va mai fi dezvoltat. O prim modalitate de a asigura compromisul ntre parcurgerile n adncime i pe lime este de a alege funcia astfel nct, pentru o valoare natural k, s fie ndeplinit condiia: pentru orice vrf x situat pe un nivel nx i orice vrf situat pe un nivel nynx+k, s avem (x)> (y), indiferent dac y este sau nu descendent al lui x.

90

Condiia de mai sus spune c niciodat nu poate deveni activ un vrf aflat pe un nivel nynx+k dac n L apare un vrf situat pe nivelul nx, adic nu putem merge "prea mult" n adncime. Dac aceast condiie este ndeplinit, este valabil urmtoarea propoziie: Propoziie. Dac exist soluie, ea va fi atins ntr-un timp finit, chiar dac arborele este infinit. Putem aplica cele de mai sus pentru jocul Perspico, alegnd: (x) = suma dintre lungimea drumului de la rdcin la x i numrul de plcue care nu sunt la locul lor (aici k=15).

Algoritmul Branch & Bound pentru probleme de optim


S presupunem c dorim s determinm vrful final de cost minim i drumul de la rdcin la el. Fie lim aproximarea prin adaos considerat mai sus. Algoritmul este urmtorul (rad este rdcina arborelui, iar ifinal este vrful rezultat):
i rad; L {i}; min lim; calculez (rad); tata(i) 0 while L i L {este scos vrful i cu (i) minim din min-ansamblul L} for toi j fii ai lui i calculez (j); calcule locale asupra lui j; tata(j) i if j este vrf final then if (j)<min then min (j); ifinal j elimin din L vrfurile k cu (k)min else if (j)<min then j L if min=+lim then write('Nu exist soluie') else writeln(min); i ifinal while i 0 write(i); i tata(i)

(*)

Observaie. La (*) am inut cont de faptul c dac j este descendent al lui i, atunci
(i)<(j).

Vom aplica algoritmul de mai sus pentru problema circuitului hamiltonian de cost minim, pe exemplul considerat mai sus. Pentru orice vrf x din arborele de stri, valoarea c(x) dat de funcia de cost ideal este: lungimea circuitului corespunztor lui x dac x este frunz min {c(y) | y fiu al lui x } altfel. Fiecrui vrf x i vom ataa o matrice de costuri Mx (numai dac nu este frunz) i valoarea (x). Observaie. Dac micorm toate elementele unei linii sau coloane cu , orice circuit hamiltonian va avea costul micorat cu , deoarece n orice circuit hamiltonian din orice vrf pleac exact un arc i n orice vrf sosete exact un arc. Conform acestei observaii, vom lucra cu matrici de costuri reduse (n care pe orice linie sau coloan apare cel puin un zero, exceptnd cazul cnd linia sau coloana conine numai ).

91

Pentru rdcina rad=1 plecm de la matricea de costuri C. Matricea ataat va fi matricea redus obinut din C, iar (1) = cantitatea cu care s-a redus C. n general, pentru un vrf y oarecare al crui tat este x i muchia (x,y) este etichetat cu (i,j): dac y este vrf terminal, (x) va fi chiar c(y), adic costul real al circuitului; n caz contrar, plecnd de la Mx i (x) procedm astfel: - elementele liniei i devin , deoarece mergem sigur ctre vrful j din graf; - elementele coloanei j devin , deoarece am ajuns sigur n vrful j din graf; - Mx(j,1) , pentru a nu reveni prematur n rdcina 1; - reducem noua matrice Mx i obinem My; fie r cantitatea cu care s-a redus Mx. Vom lua (y) (x)+r+Mx(i,j). Concret, pentru exemplul dat, calculele se desfoar astfel: Pentru rdcin: - reduc liniile n ordine cu 2, 1, 3, 2; - reduc prima coloan cu 1; - n acest mod obin (1)=9, iar matricea M1 este:
1 5 0 3 0 8 M1 = 0 5 0 3 0 4

Acum min9; L={1}. Este extras vrful 1 i sunt considerai fiii si. Pentru vrful 2: - plec de la M1 i pun pe linia 1 i coloana 2; - reduc linia 3 cu 3; - n acest mod obin (2)=3+9+1=13, iar matricea M2 este: Pentru vrful 3: - plec de la M1 i pun pe linia 1 i coloana 3; - reduc linia 2 cu 3; - n acest mod obin (3)=3+9+5=17, iar matricea M3 este: Pentru vrful 4: - plec de la M1 i pun pe linia 1 i coloana 4; - nu este necesar vreo reducere; - n acest mod obin (4)=0+9+0=9, iar matricea M4 este

M2 = 0 0

0 8 0 1

0 5 M3 = 5 0 3 0 3 0 M4 = 0 5 0 4

Acum L={2,3,4} cu (2)=13, (3)=17, (4)=9. Devine activ vrful 4. Pentru vrful 9: - plec de la M4 i pun pe linia 4 i coloana 2; - nu este necesar vreo reducere; - n acest mod obin (9)=0+9+0=9, iar matricea M9 este:

M9 = 0

92

Pentru vrful 10: - plec de la M4 i pun pe linia 4 i coloana 3; - reduc linia 2 cu 3, iar linia 3 cu 5; 0 - n acest mod obin (10)=8+9+4=21, iar matricea M10 este: M10 =
0

Acum L={2,3,9,10} cu (2)=13, (3)=17, (9)=9, (10)=21. Devine activ vrful 9. Singurul su descendent este 15, care este frunz. (15)=c(15)=9 (costul real al circuitului). Sunt eliminate din L vrfurile cu costurile mai mari dect 9, deci L devine vid. min rmne egal cu 9, va fi produs la ieire circuitul cutat (1,4,2,3,1) i algoritmul se oprete.

93

Algoritmi probabiliti
Este vorba de algoritmi n care este posibil continuarea calculelor prin alegerea (aleatoare) a uneia dintre mai multe variante posibile. Pentru aceasta va fi folosit o funcie random, prezent n orice limbaj de progamare. Drept urmare, pentru aceleai date de intrare, la executri diferite vom obine rezultate diferite. Vom ncepe prin prezentarea ctorva probleme specifice, iar apoi vom prezenta o clasificare a algoritmilor probabiliti. Exemplul 1. Acul lui Buffon. Se consider o mulime de linii paralele astfel nct oricare dou linii vecine sunt la distan de o unitate. Un ac (segment) de lungime o jumtate de unitate este aruncat aleator i se numr de cte ori a intersectat vreo linie. Se poate demonstra c probabilitatea ca acul s intersecteze o linie este 1/. Practic, dup un numr "suficient de mare" de ncercri, raportul ntre: - numrul total de ncercri i - numrul cazurilor n care acul a intersectat vreo linie va fi "suficient de aproape" de . Exemplul 2. Fie G un graf neorientat. Vom nelege prin mutare eliminarea unui vrf mpreun cu toi vecinii si. Se cere numrul minim de mutri prin care se pot elimina toate vrfurile. Aceast problem a fost propus la o olimpiad colar. Specificul acestor olimpiade const n faptul c programul este rulat pe 10 exemple i se contabilizeaz numrul de exemple pentru care rezultatul furnizat este corect. Pentru a "aduna" puncte, nu este necesar s ne gndim la o soluie efectiv, ci este suficient s aplicm urmtorul algoritm probabilist: - se alege aleator un vrf i se elimin mpreun cu vecinii si; - se repet pasul anterior pe noul graf pn cnd sunt eliminate toate vrfurile; - se memoreaz numrul de mutri efectuat. Se reia algoritmul (fr a depi timpul maxim fixat pentru un test) i se pstreaz cel mai mic numr de mutri. Dincolo de cadrul n care a fost propus problema, strategia de mai sus este aplicabil atunci cnd se cere doar o aproximaie suficient de bun a rezultatului dorit i/sau trebuie s ne ncadrm ntr-un timp dat. Exemplul 3. Se dau n texte (n foarte mare) cu urmtoarele proprieti: - exist un unic text t0 care apare de cel puin 10% ori; - celelalte texte sunt distincte. Se cere determinarea textului t0. Problema a fost propus la un concurs studenesc A.C.M. Un algoritmul probabilistic eficient este urmtorul:

94

repeat

generez aleator i,j{1,...,n} diferite if ti=tj then write ti; stop


until false

Probabilitatea ca alegerea unui indice s conduc la textul t0 este 1/10, deci probabilitatea ca s obinem o pereche de indici (i,j) cu ti=tj=t0 este 1/100. Cu alte cuvinte, teoretic sunt suficiente 100 de ncercri, independent de valoarea lui n. Exemplul 4. Problema celor n dame. Am prezentat o rezolvare a acestei probleme folosind metoda backtracking. Implementarea i executarea algoritmului corespunztor arat c se ajunge la o soluie n timp "rezonabil" doar pentru valori mici ale lui n (n20). Un algoritm probabilist pentru aceast problem, care furnizeaz rapid o soluie chiar pentru valori ale lui n mai mari dect 100 este urmtorul: - plasm o dam pe prima linie; - presupunnd c am plasat neantagonist cte o dam pe liniile 1,...,k-1, facem un inventar al poziiilor posibile pentru dama de pe linia k i alegem aleator una dintre ele. Exista dou posibiliti: 1) Am reuit s plasm o dam pe linia n am determinat o soluie, deci o listm i oprim programul; 2) Am ajuns la o linie k i nu exist poziii posibile. Atunci relum ntreg algoritmul (deci nu ne ntoarcem la linia precedent ca la backtracking). Pentru implementarea algoritmului, trebuie inut o eviden a coloanelor i diagonalelor ocupate (pe care nu putem plasa o dam). Pentru aceasta vom folosi vectorii booleeni NV_SE[-n+1..n-1] , NE_SV[2..2n] i C[1..n] ale cror valori ne spun dac o diagonal NV-SE, o diagonal NE-SV sau o coloan sunt libere (nu exist plasat o dam pe diagonala sau coloana respectiv). 0 1 -1 n-1 2 3 n+1 n+2

NV_SE

NE_SV

-n+1

2n

Observaie. Dac sunt pe linia k, pot plasa o dam pe coloana i dac: (k,i) : Ci liber & NV_SEi-k liber & NE_SVi+k liber. n algoritmul care urmeaz, soluia este obinut n vectorul x=(x1,...xn).

95

repeat repeat

iniializarea componentelor celor 3 vectori booleeni cu valoarea true


k 1

inventar al poziiilor i{1,...,n} cu (k,i) pun aceste poziii n primele na componente ale unui vector a if na>0 then aleg aleator i{1,...,na}; i ai
xk i ; NV_SEi-k false; NE_SVi+k false Ci false; k k+1 until na=0 k=n+1 until k=n+1 write(x)

Observaie. Este indicat s ne mulumim s plasm damele pe primele aprox. 90% din linii, iar pentru restul liniilor s folosim backtracking.

96

Algoritmi genetici
Algoritmii genetici constituie o generalizare a algoritmilor probabiliti. Problema general: Fie f:D R. Se caut max{ f(x) | xD }. Presupunem c orice xD poate fi codificat ca: x=(x1,...,xr) cu xi{0,1}, i=1,...,r. x se numete cromozom. Nu vom lucra cu un singur cromozom, ci cu o populaie de n cromozomi, care se transform prin trecerea de la o generaie la alta. Observaie. O populaie este un multiset (un element poate s apar de mai multe ori). Schimbarea de generaie se face prin selecia unei subpopulaii i modificarea acesteia prin operaiile de ncruciare (crossover) i mutaie, ce vor fi descrise mai jos. Algoritmul se ncheie dac s-a efectuat un numr dat de schimbri de configuraii sau dac s-a gsit o aproximare suficient de bun a maximului. Considerm n continuare prima variant. Notaii: P - populaia curent; P={p1,...,pn}, deci n=|P|; r - lungimea cromozomilor; valmax - valoarea maxim curent ; xmax - punctul pentru care este atins valmax; nmax - numrul maxim admis de schimbri de configuraii; pc - probabilitatea de crossover; pm - probabilitatea de mutaie. Se mai folosete o funcie J:X R pentru evaluarea performanelor cromozomilor din P. O modalitate simpl de crossover este: cromozom 1 cromozom 2 adic din doi prini se obin 2 descendeni. O modalitate simpl de mutaie este alegerea aleatoare a unei poziii din cromozom i modificarea ei: 01.

97

Algoritmul general este urmtorul:


P aleator; valmax - for t=1,nmax determin cel mai bun pP (conform funciei J) if J(p)>valmax then xmax p; valmax J(p) . . . . . . . . . . . . . . . . . . . . . . Q for i=1,n aleg k[0,1] aleator if k<pc then P P\{pi}; Q Q {pi} aleg aleator perechi de elemente din Q i aplic crossover (dac Q nu este par, un element rmne neschimbat) P P Q . . . . . . . . . . . . . . . . . . . . . . Q for i=1,n aleg k[0,1] aleator if k<pm then P P\{pi}; Q Q {pi} pentru fiecare qQ aplic operatorul de mutaie P P Q

Observaii: de obicei pc este de ordinul 10-1 (de exemplu 0.3), iar pm de ordinul 10-3 (de exemplu 0.007). Experienele arat c alegerea lui pc i pm nu are foarte mare importan. Alegerea lor se face i n funcie de lungimea r a cromozomilor; de obicei cel mai performant individ este reinut i pentru viitoarea configuraie; n funcia de evaluare (dar nu numai) pot fi folosite distane ca de exemplu: cea euclidian; Hamming - numrul de poziii pe care cromozomii difer; Levenstein - numrul minim de tergeri + adugri + modificri pentru a trece de la un cromozom la cellalt.

98

Aplicaii
1) Ghicirea unei submulimi (interactiv) O submulime a lui {1,2...,r} poate fi reprezentat ca un vector x{0,1}r cu semnificaia c i face parte din submulime dac i numai dac xi=1. Programul a fost rulat pentru n=r=25; pc=0.3; pm=0.2. J(p) = numrul de poziii pe care p coincide cu p0 cutat. Variante folosite pentru crossover: - clasic; - selectm doi indici p1<p2 i interschimbm poriunile corespunztoare din cromozomi. Ne oprim dac J(p)n-2. 2) Ghicirea unei permutri (interactiv) Programul a fost rulat pentru n=10, r=10, pc=0.3. Funcia J i variantele de crossover sunt aceleai ca n exemplul precedent. Mutaie: determinm aleator j,k{1,...,n} i interschimbm p[j] cu p[k]. 3) Maximul unei funcii. Dorim s determinm max{f(x) | x [0,1]}. Fiecare cromozom se scrie ca x=0,c1...cr cu ci{0,1}, i=1,...r. Programul a fost rulat pentru n=30, r=20, pc=0.25, pm=0.1. Variante folosite pentru crossover: - clasic; - modificm ultimele k poziii aleator; - selectm doi indici p1<p2 i interschimbm poriunile corespunztoare din cromozomi.

99

Variante pentru selecie i pentru alegerea operatorilor


Pentru crossover se mai pot folosi urmtoarele variante: cu k>2 puncte de tietur; cu amestec.

La selecia subpopulaiei putem folosi o selecie Monte Carlo. n T = J(pi) i=1 n q i = pi ,i=1,...,n q i = 1 T i=1 Pot folosi o rulet: q1 q2

Rotesc ruleta de n (sau de un numr dat) de ori.

100

Algoritmi nedeterminiti
Este vorba de algoritmi secveniali care, spre deosebire de cei obinuii, admit i urmtoarele instruciuni suplimentare: 1) success i failure, care arat c algoritmul se termin cu succes, respectiv cu eec. Ele nlocuiesc instruciunea stop. Forma lor arat c vom studia doar probleme de decizie, pentru care rezultatul poate fi doar afirmativ sau negativ (dup cum se va vedea, foarte multe probleme pot fi aduse la aceasta form). 2) choice(A), unde A este o mulime finit; este o funcie care ntoarce ca rezultat un element oarecare al lui A. Se consider c cele 3 instruciuni nou introduse necesit un timp constant. Maina abstract care execut un astfel de algoritm, cnd ntlnete o instructiune choice lucreaz astfel: - dac exist o valoare din A care conduce la o instruciune success, va fi aleas o altfel de valoare; - n caz contrar, va fi aleas o valoare oarecare. Cu alte cuvinte, un algoritm nedeterminist se termin cu eec dac nu exist o modalitate de a efectua alegeri care s conduc la o instruciune success. Funcionarea mainii abstracte se aseamn calculului paralel. De cte ori este ntlnit o instruciune choice, intr n aciune attea procesoare cte elemente are mulimea A. Algoritmul se termin cu succes dac unul dintre procesoarele active ajunge la o instruciune success. Timpul de executare va fi, n cazul unei terminri cu succes, timpul necesar ajungerii la respectiva instruciune success . Mai precizm c ne intereseaz doar terminrile cu succes. Exemplul 1. Se cere s se verifice dac un numr x apare sau nu n mulimea {a1,...,an}. tim c timpul de executare pentru algoritmul determinist pentru aceast problem este de ordinul O(n), adic liniar. Putem scrie urmtorul algoritm nedeterminist:
i choice({1,2,...,n}) if ai=x then write i; success else failure

care rezolv problema n timp constant.

101

Exemplul 2. Se cere s se ordoneze cresctor vectorul a=(a1,...,an). tim c cel mai bun algoritm determinist pentru aceast problem necesit un timp de ordinul O(n.log n). Ideea algoritmului nedeterminist este urmtoarea: copiem elementele vectorului a ntr-un vector auxiliar b ntr-o ordine oarecare, dup care verificm dac elementele lui b apar n ordine cresctoare.
for i=1,n bi

{ iniializarea lui b }

for i=1,n j choice({1,2,...,n}) if bj= then bj ai { fiecare ai trece pe o poziie nou din b } else failure for i=1,n-1 if bi>bi+1 then failure write(b); success

Timpul de executare al acestui algoritm este O(n), deci liniar. Se observ c: am obinut un timp de executare mai bun; problema sortrii a fost tratat ca o problem de decizie.

Exemplul 3. Problema validrii. Fie F(x1,...,xn) o expresie boolean n forma normal conjunctiv (FNC): F=C1...Ck , unde C1,...,Ck sunt disjuncii de variabile de forma xj sau xj' (xj' este negaia lui xj). Se cere s se determine dac exist a1,...,an{0,1} cu F(a1,...,an)=1. Problema va fi referit n continuare sub numele VALID. O instan a problemei este: F(x1,x2,x3)=(x1x2x3)(x1'x2'x3'). Putem scrie urmtorul algoritm nedeterminist simplu care rezolv problema:
for i=1,n xi choice({0,1}) if F(x1,...,xn)=1 then success else failure

Timpul este proporional cu lungimea formulei, deci liniar. Observaie. Nu se cunoate un algoritm determinist polinomial pentru problema validrii ! tim c doar algoritmii polinomiali sunt eficieni, deci scopul este de a elabora algoritmi polinomiali. Este evident c nu exist algoritmi polinomiali pentru orice problem, de exemplu cei pentru care numrul datelor de ieire nu este polinomial: determinarea tuturor submulimilor unei mulimi finite, generarea permutrilor etc. De aceea cutm s delimitm clasa problemelor pentru care ncercm s elaborm algoritmi polinomiali.

102

Introducem clasele de probleme (de decizie) urmtoare: P - clasa problemelor pentru care exist algoritmi determiniti polinomiali; NP - clasa problemelor pentru care exist algoritmi nedeterminiti polinomiali. Vom studia problema existenei algoritmilor (determiniti) polinomiali doar pentru problemele din NP. Evident PNP. Este ns incluziunea strict sau P=NP ? Precizm de la nceput c aceast problem este deschis! n 1976, Samuel Cook a obinut un rezultat care a simplificat problema i a prut promitor n rezolvarea ei: Teorem. P=NP VALIDP Teorema spune c dac reuim s gsim un algoritm determinist polinomial pentru VALID, atunci va exista un algoritm determinist polinomial pentru orice problem din NP. ntruct nu s-a reuit s se obin un algoritm polinomial pentru VALID, s-a ncercat s se rezolve probleme "echivalente" cu VALID. Mai precis s-a definit clasa NPC .

NPC NP P

Clasa de probleme NPC este definit astfel: 1) VALID NPC 2) Pentru o problem P, P NPC dac: 2.1) Se cunoate pentru P un algoritm nedeterminist polinomial; 2.2) VALID se poate reduce la P n timp determinist polinomial. Problemele din NPC se numesc NPcomplete. Observaie. Este suficient s artm pentru o singura problema NPcomplet c admite un algoritm polinomial, pentru a obine egalitatea P=NP. Lista problemelor NPcomplete a depit 1000, dar pentru nici una dintre ele nu s-a reuit s se obin un algoritm determinist polinomial. Drept urmare, aa cum am menionat anterior, problema egalitii P=NP rmne o problema deschis. Prezentm n continuare una dintre multele probleme NPcomplete.

103

Problema clicii maximale. Fie G=(X,M) un graf neorientat. YX se numete clic dac pentru i,jY avem (i,j)M. Se caut ordinul unei clici maximale. Problema de decizie corespunztoare este urmtoarea: Pentru un k dat, exist n G o clica de ordin k ? Pentru aceast problem vom scrie procedura clica(G,k), care furnizeaz rspunsul n timp nedeterminist polinomial. Presupunnd cunoscut acest lucru, algoritmul pentru problema clicii maximale va fi:
for k=n,1,-1 clica(G,k)

care este tot polinomial.


procedure clica(G,k) for i=1,n ales(i) 0 {elementul i nu a fost ales} { pentru fiecare i aleg un element j din {1,...,n} pe care l pun n bi } for i=1,k j choice({1,...,n}) if ales(j)=1 then failure else ales(j) 1; bi j { s-au ales k vrfuri distincte b1,...,bk } for i=1,k for j=1,k if ij & (bi,bj)M then failure write(k); success end; Timpul este evident polinomial CLICA(k), deci i CLICA NP.

Artm n continuare c VALID se reduce la CLICA n timp determinist polinomial. Fie F(x1,...,xn) o expresie boolean n forma normal conjunctiv (FNC): F=C1...Ck , unde C1,...,Ck sunt disjuncii de variabile de forma xj sau xj', numii literali. Atam lui F graful G=(X,M) astfel: X = {(,i) | literal din Ci} M = {[(,i),(,j)] | '}, adic i pot fi satisfcui concomitent. De exemplu, pentru F(x1,x2,x3)=(x1x2x3)(x1'x2'x3'), graful este:
(x1,1) (x2,1) (x3,1) (x1',2) (x2',2) (x3',2)

Construcia grafului necesit un timp determinist polinomial.

104

Mai trebuie demonstrat c: n G exist o clic de ordin k F este validabil. ncepem cu demonstrarea necesitii. Fie S = {(i,i) | i=1,...k} o clic de ordin k. Fie S1 = {i | i=1,...,k}. Conform construciei lui M, nu este posibil ca i,i' s apar simultan n S1. Alegem fiecare ai astfel: 1 dac xiS1, adic i=xi 0 dac xi'S1 arbitrar n caz contrar. Pentru aceasta alegere, F(a1,...,an)=1, fiecare conjuncie avnd valoarea 1. Pentru exemplul considerat: S={(x1,1),(x3',2)} ; S1={x1,x3'} ; a1=1 ; a2 arbitrar ; a3=0. Continum cu demonstrarea suficienei. Conform ipotezei, exist a1,...,an cu Ci(a1,...,an)=1, i=1,...,k. Pentru fiecare i=1,...,k, exist un literal i din Ci care are valoarea 1. Fie S = {(i,i) | i=1,...,k}. S este o clic. ntr-adevr, pentru (i,i),(j,j)S diferite rezult ij i ij deoarece pentru a=(a1,...,an) avem i=j=1. ncheiem prin a prezenta enunul altor probleme NP-complete. 1) Fie G=(X,M) un graf. YX se numete k-acoperire dac |Y|=k i pentru orice (i,j)M avem iY sau iY. Exist o k-acoperire? 2) Problema ciclului hamiltonian. 3) Problema colorrii hrilor. 4) Fie A o mulime i fie s:AZ+. Caut BA cu s(A\B)=s(B).

105

10
10.1. Algoritmi probabiliti

ALTE TIPURI DE ALGORITMI

n multe probleme, n timpul rezolvrii, putem ajunge la un moment dat n situaia de a avea de ales ntre mai multe variante de continuare. Am vzut c n aceast situaie putem analiza pe rnd variantele, putem ncerca s determinm varianta optim i s o urmm etc. Algoritmii probabiliti adopt o alt abordare: se alege aleator una dintre variante. Vom vedea c n unele situaii aceast abordare poate conduce la determinarea mai rapid a unei soluii (exacte sau aproximative). Pentru alegerea aleatoare se presupune c avem la dispoziie o funcie random, care ntoarce o valoare aleatoare dintr-un interval [a,b) de numere reale sau dintr-o secven a..b de numere ntregi consecutive. Este evident c la executri diferite ale unui algoritm probabilist, rezultatele sunt n general diferite. Exist trei categorii mari de algoritmi probabiliti, pe care le prezentm n continuare. Algoritmi numerici

Algoritmii de acest tip sunt caracterizai prin urmtoarele: - urmresc determinarea aproximativ a unei valori; - cu ct timpul alocat executrii algoritmului este mai mare, precizia rezultatului se mbuntete. Exemplul 1. Acul lui Buffon. Se consider o mulime de linii paralele astfel nct oricare dou linii vecine sunt la distan de o unitate. Un ac (segment) de lungime o jumtate de unitate este aruncat aleator i se numr de cte ori a intersectat vreo linie. Se poate demonstra c probabilitatea ca acul s intersecteze o linie este 1/. Practic, dup un numr "suficient de mare" de ncercri, raportul ntre: - numrul total de ncercri i - numrul cazurilor n care acul a intersectat vreo linie va fi "suficient de aproape" de .

106

94

10. ALTE TIPURI DE ALGORITMI

Exemplul 2. Se arunc repetat cu o sgeat ntr-un panou ptrat. Se presupune c sgeata nimerete totdeauna panoul. Atunci raportul dintre: - numrul cazurilor n care sgeata nimerete n cercul nscris n ptrat i - numrul total de ncercri tinde la /4, numr egal cu raportul dintre aria cercului nscris n ptrat i aria ptratului. Exemplul 3. Dat fiind o funcie f:[a,b][c,d], se cere determinarea aproximativ a valorii I= f x)dx . (
a b

Un algoritm probabilist de tip numeric pentru determinarea valorii I este urmtorul:


s 0 for i=1,n x random([a,b]); s s+f(x) s s.(b-a)/n write(s)

Algoritmi Monte Carlo

Algoritmii de acest tip urmresc determinarea unei soluii exacte i sunt caracterizai prin urmtoarele: - furnizeaz totdeauna un rezultat, care ns nu este neaprat corect; probabilitatea ca rezultatul s fie corect crete pe msur ce timpul disponibil crete. Exemplul 4. Se consider vectorul x=(x1,...,xn) cu elemente distincte. Se cere determinarea unui element al vectorului care s fie mai mare sau egal cu media aritmetic a celor n numere. Problema are sens dac valoarea lui n este foarte mare, iar timpul avut la dispoziie este mic (n caz contrar alegem, n timp liniar, cel mai mare element al vectorului, care satisface evident condiia dat). Un algoritm probabilist, de tipul Monte Carlo, este urmtorul: alegem aleator un element al vectorului i repetm aceast operaie fr a depi timpul disponibil, pstrnd ntr-o variabil v cel mai mare dintre elementele alese. Rezultatul ntors va fi v. S presupunem c n timpul disponibil am analizat k elemente ale vectorului; v este cel mai mare dintre ele. Valoarea v nu ndeplinete condiia din enun numai n cazul cnd toate cele k elemente alese sunt mai mici dect media

107

10.1. Algoritmi probabiliti

95

aritmetic a elementelor lui x. Cum probabilitatea ca un element s fie mai mic dect media aritmetic este 1/2, probabilitatea ca toate elementele (deci i v) s fie mai mici ca media aritmetic este
1 2k

. Rezult c probabilitatea ca valoarea


1 2k

ntoars de algoritm s fie corect este 1 probabilitate este mai mare dect 0,999999.

. De exemplu pentru k=20, aceast

Exemplul 5. Fie G un graf neorientat. Vom nelege prin mutare eliminarea unui vrf mpreun cu toi vecinii si. Se cere numrul minim de mutri prin care se pot elimina toate vrfurile. Aceast problem a fost propus la o olimpiad colar pe timpul cnd programul era rulat pe 10 exemple i se contabiliza numrul de exemple pentru care rezultatul furnizat este corect. Pentru a "aduna" puncte, nu este necesar s ne gndim la o soluie efectiv, ci este suficient s aplicm urmtorul algoritm probabilist: - se alege aleator un vrf i se elimin mpreun cu vecinii si; - se repet pasul anterior pe noul graf pn cnd sunt eliminate toate vrfurile; - se memoreaz numrul de mutri efectuat. Se reia algoritmul (fr a depi timpul maxim fixat pentru un test) i se pstreaz cel mai mic numr de mutri. Algoritmi Las Vegas

Algoritmii de acest tip urmresc, ca i algoritmii Monte Carlo, determinarea unei soluii exacte i sunt caracterizai prin urmtoarele: - nu furnizeaz totdeauna un rezultat, dar dac furnizeaz un rezultat atunci acesta este corect; probabilitatea ca rezultatul s fie corect crete pe msur ce timpul disponibil crete. Exemplul 5. Se dau n texte (n foarte mare) cu urmtoarele proprieti: - exist un unic text t0 care apare de cel puin 10% ori; - celelalte texte sunt distincte. Se cere determinarea textului t0. Problema a fost propus la un concurs studenesc A.C.M. Un algoritm probabilist eficient este urmtorul:

108

96

10. ALTE TIPURI DE ALGORITMI

repeat i random(1..n); j random(1..n); if ij & ti=tj then write ti; stop until false

Probabilitatea ca alegerea unui indice s conduc la textul t0 este 1/10, deci probabilitatea ca s obinem o pereche de indici (i,j) cu ti=tj=t0 este 1/100. Cu alte cuvinte, teoretic sunt suficiente 100 de ncercri, independent de valoarea lui n. Pe de alt parte este posibil ca algoritmul s nu produc vreun rezultat ntr-un interval limitat de timp. Exemplul 6. Problema celor n dame. Am prezentat o rezolvare a acestei probleme folosind metoda backtracking. Implementarea i executarea algoritmului corespunztor arat c se ajunge la o soluie n timp "rezonabil" doar pentru valori mici ale lui n (n20). Un algoritm probabilist pentru aceast problem, care furnizeaz rapid o soluie chiar pentru valori ale lui n mai mari dect 100 este urmtorul: - plasm o dam pe prima linie; - presupunnd c am plasat neantagonist cte o dam pe liniile 1,...,k-1, facem un inventar al poziiilor posibile pentru dama de pe linia k i alegem aleator una dintre ele. Exista dou posibiliti: 1) Am reuit s plasm o dam pe linia n. Atunci am determinat o soluie, deci o listm i oprim programul; 2) Am ajuns la o linie k i nu exist poziii posibile. Atunci relum ntreg algoritmul (deci nu ne ntoarcem la linia precedent ca la backtracking). Pentru implementarea algoritmului, trebuie inut o eviden a coloanelor i diagonalelor ocupate (pe care nu putem plasa o dam). Pentru aceasta vom folosi vectorii booleeni NV_SE[-n+1..n-1] , NE_SV[2..2n] i C[1..n] ale cror valori ne spun dac o diagonal NV-SE, o diagonal NE-SV sau o coloan sunt libere (nu exist plasat o dam pe diagonala sau coloana respectiv).

109

10.2. Algoritmi genetici

97

0 1 -1

n-1

2 3

n+1 n+2

NV_SE

NE_SV

-n+1

2n

Observaie. Dac suntem pe linia k, putem plasa o dam pe coloana i dac este ndeplinit condiia: (k,i) : Ci liber & NV_SEi-k liber & NE_SVi+k liber. n algoritmul care urmeaz, soluia este obinut n vectorul
x=(x1,...xn). repeat repeat

iniializm componentele celor 3 vectori booleeni cu valoarea true


k 1

facem inventarul poziiilor i{1,...,n} cu (k,i) plasm aceste poziii n primele na componente ale unui vector a
if na>0 then aleg aleator i{1,...,na}; i ai xk i ; NV_SEi-k false; NE_SVi+k false Ci false; k k+1 until na=0 k=n+1 until k=n+1 write(x)

10.2. Algoritmi genetici


Algoritmii genetici sunt tot algoritmi probabiliti, dar de o natur complet diferit, dup cum vom vedea n continuare. Algoritmii genetici reprezint tehnici de cutare i optimizare. Denumirea lor se datoreaz prelurii unor mecanisme din biologie: motenirea genetic i evoluia natural pentru populaii de indivizi.

110

98

10. ALTE TIPURI DE ALGORITMI

Problema general: Fie f:D R. Se caut max{ f(x) | xD }. Presupunem c mulimea D poate fi pus n coresponden biunivoc cu o mulime C {0,1}r, adic orice element al lui D poate fi codificat ca: x=(x1,...,xr) cu xi{0,1}, i=1,...,r. Vectorul x se numete cromozom. Nu vom lucra cu un singur cromozom, ci cu o populaie de n cromozomi (indivizi), care se transform prin trecerea de la o generaie la alta. Observm deci c spre deosebire de algoritmii iterativi uzuali de optimizare, n care la fiecare etap se trece de la un element din C la urmtorul, n algoritmii genetici la fiecare etap se trece de la o submulime a lui C la o alt submulime a lui C. O alt trstur a algoritmilor genetici este c la trecerea de la o populaie la urmtoarea, cromozomii se combin ntre ei. Observaie. O populaie este un multiset (un element poate s apar de mai multe ori). Schimbarea de generaie se face prin selecia din populaia curent a unei subpopulaii i modificarea acesteia prin operaiile de ncruciare (crossover) i mutaie, ce vor fi descrise mai jos. Toate populaiile succesive au acelai numr de indivizi. Algoritmul se ncheie dac s-a efectuat un numr dat de schimbri de configuraii sau dac dup un numr de schimbri de generaie maximul curent rmne neschimbat. Considerm n continuare prima variant. Notaii: P - populaia curent; P={p1,...,pn} valmax - valoarea maxim curent ; xmax - punctul pentru care este atins valmax; nmax - numrul maxim admis de schimbri de configuraii; pc - probabilitatea de ncruciare; pm - probabilitatea de mutaie; r - lungimea cromozomilor; n=|P|. Se mai folosete o funcie J:C R pentru evaluarea performanelor cromozomilor din P. n general J este corespondenta funciei f:D R. Algoritmul general este urmtorul:
P aleator; valmax - for t=1,nmax se calculeaz valorile J(p), pP i se actualizeaz eventual valorile xmax i valmax

etapa de selecie etapa de ncruciare etapa de mutaie

111

10.2. Algoritmi genetici

99

Scopul principal al celor trei etape este de a ne apropia ct mai mult de maxim, dar i de a acoperi prin cutri ntregul domeniu de definiie, pentru a obine maximul general i nu unul local. Etapa de selecie urmrete pstrarea (chiar multipl) a celor mai performani indivizi (cromozomi) ai populaiei curente, dar incluznd i factorul aleator. Pentru selecie se folosete de obicei algoritmul Monte Carlo descris n continuare.
J xi) ( probabilitatea de S i =1 selecie a cromozomului pi. Deci s1+...+sn=1. Mai considerm valoarea s0=0. De n ori procedm astfel: - generm un numr aleator x n intervalul [0,1); - este selectat acel cromozom pi pentru care s0+...+si-1x<s0+...+si. Cei n cromozomi selectai vor constitui noua populaie dup etapa de selecie. Se

Fie P={p1,...,pn} i S= J xi). Fie si= (

observ c un cromozom poate fi selectat de mai multe ori i de aceea o populaie este un multiset. Algoritmul de mai sus mai este numit i algoritmul ruletei, deoarece lucrurile se petrec exact ca la o rulet mprit n sectoare corespunztoare cromozomilor, de mrimi proporionale cu valorile s1,...,sn ; la fiecare rotire (aleatoare) a ruletei se ajunge n dreptul unui cromozom. Etapa de ncruciare (crossover ) const n selectarea unei subpopulaii a populaiei curente i recombinarea a cte doi indivizi din subpopulaie. Este folosit un parametru pc[0,1) numit probabilitatea de ncruciare. Prin recombinarea (ncruciarea) a doi cromozomi se obin doi descendeni ce au caracteristici ale ambilor prini. O modalitate simpl de ncruciare a doi cromozomi este cea cu un punct de tietur, n care din "prinii" : x=(x1,...,xk,xk+1,...,xr) i y=(y1,...,yk,yk+1,...,yr) se obin "descendenii": x'=(y1,...,yk,xk+1,...,xr) i y'=(x1,...,xk,yk+1,...,yr) unde k este ales aleator din secvena 1..r-1. n etapa de ncruciare extragem mai nti o subpopulaie Q a lui P, apoi ncrucim cte doi indivizi din Q, dup care adugm noul Q lui P:
Q for i=1,n x random([0,1)) if x<pc then P P \ {pi}; Q Q {pi}

112

100

10. ALTE TIPURI DE ALGORITMI

alegem aleator perechi de elemente din Q i le ncrucim (dac Q nu este par, un element rmne neschimbat)
P P Q

Exist o mare varietate de modaliti de ncruciare a doi indivizi. Ne mrginim la cele ce folosesc puncte de tietur. Considerm cromozomii:

Dac vom folosi dou puncte de tietur vor rezulta descendenii:

iar dac vom folosi trei puncte de tietur vom obine descendenii:

cu precizarea c punctele de tietur sunt alese aleator. Etapa de mutaie const n selectarea unei subpopulaii a populaiei curente i aplicarea unor mici perturbri cromozomilor din aceast subpopulaie. Este folosit un parametru pm[0,1) numit probabilitatea de mutaie. O modalitate simpl de mutaie a unui cromozom const n alegerea aleatoare a unei poziii din cromozom i modificarea ei: 01. n etapa de mutaie extragem mai nti o subpopulaie Q a lui P, apoi aplicm mutaia fiecrui cromozom din Q, dup care adugm noul Q lui P:
Q for i=1,n x random([0,1)) if k<pc then P P \ {pi}; Q Q {pi} pentru fiecare qQ aplicm operatorul de mutaie P P Q

Observaii: - de obicei pc este de ordinul 10-1 (de exemplu 0.3), iar pm de ordinul 10-3 (de exemplu 0.007). Experienele arat c alegerea lui pc i pm nu are foarte mare importan. Alegerea lor se face i n funcie de lungimea r a cromozomilor;

113

10.2. Algoritmi genetici

101

- de regul, n etapa de selecie, cel mai performant individ este reinut i pentru viitoarea configuraie; - n funcia de evaluare (dar nu numai) pot fi folosite distane ca de exemplu: cea euclidian; Hamming - numrul de poziii pe care cromozomii difer; Levenstein - numrul minim de tergeri + adugri + modificri pentru a trece de la un cromozom la cellalt. Aplicaii

1) Ghicirea interactiv a unei submulimi p0 a lui {1,2...,r}. Interactivitatea const n faptul c pentru fiecare submulime p "calculatorul" ntoarce numrul de poziii pe care p i p0 coincid. O submulime a lui {1,2...,r} poate fi reprezentat ca un vector x{0,1}r cu semnificaia c i face parte din submulime dac i numai dac xi=1. Programul a fost rulat pentru n=r=25, pc=0.3, pn=0.2. J(p) = numrul de poziii pe care p coincide cu p0 cutat. Variante folosite pentru ncruciare: - cea clasic; - selectm doi indici p1<p2 i interschimbm poriunile corespunztoare din cromozomi. Ne oprim dac J(p)n-2. Determinarea soluiei exacte poate fi realizat apoi ncercnd variantele posibile. 1) Ghicirea unei permutri (interactiv) Programul a fost rulat pentru n=10, r=10, pc=0.3. Funcia J i variantele de crossover sunt aceleai ca n exemplul precedent. Mutaia aplicat unui cromozom p const n a determina aleator indicii j,k{1,...,n} i a interschimba p[j] cu p[k]. 2) Maximul unei funcii. Dorim s determinm valoarea x0 care maximizeaz funcia f:[a,b]R. Pentru x0 sunt cerute k cifre zecimale. Fie r cel mai mic numr natural cu (b-a)10k2r. Atunci vom lucra cu cromozomi c=(c1,c2,...,cr) de lungime r, unde fiecare ci{0,1}. Valoarea x[a,b] corespunztoare lui c se calculeaz astfel: - fie x' numrul zecimal egal cu (crcr-1...c2c1)2;

114

102

10. ALTE TIPURI DE ALGORITMI

x=a+

b a
2r 1

x'.

Programul a fost rulat pentru n=30, r=20, pc=0.25, pm=0.1. Variante folosite pentru crossover: - cea clasic; - modificm ultimele k poziii aleator; - selectm doi indici p1<p2 i interschimbm poriunile corespunztoare secvenei de indici p1..p2.

10.3. Principiul lui Dirichlet


Prezentm n acest subcapitol un principiu simplu, cu multe aplicaii, dar care nu are generalitatea algoritmilor probabiliti i genetici, studiai anterior. Principiul lui Dirichlet are un enun simplu i care nu necesit demonstraie: Dac m>kn obiecte sunt plasate n n csue, atunci va exista o csu ce va conine mai mult de k obiecte. Prezentm n continuare cteva aplicaii ale acestui principiu. Exemplul 1. Exist un numr de n perechi de pantofi de mrimi diferite, dar nearanjai pe perechi. Care este numrul minim de pantofi care trebuie cercetai pentru a forma o pereche ? Rspunsul este imediat. Folosim cte o cutie (iniial goal) pentru fiecare mrime. Este evident c dup aezarea n cutii a n+1 pantofi, o cutie va conine doi pantofi (m=n+1, k=1). Exemplul 2. Se consider un vector a=(a1,...,an) de numere naturale. Se caut indicii i<j cu ai+...+aj multiplu de n. n continuare, pentru orice numr natural x, vom nota prin x clasa sa de echivalen modulo n. Considerm sumele sk=a1+...+ak pentru k=1,...,n. Fie sk clasele de echivalen corespunztoare. Deosebim dou situaii: 1) dac exist k cu sk=0, atunci o soluie este (i,j)=(1,k);

115

10.3. Principiul lui Dirichlet

103

2) n caz contrar, este clar c s1,...,sn{1,2,...,n-1}. Conform principiului lui Dirichlet, vor exista indicii k<l cu sk=sl. Atunci o soluie este (i,j)=(k+1,l). Exemplul 3. Pentru un numr natural n dat, se caut un multiplu N al su n a crei scriere obinuit (n baza 10) apar numai cifrele 0 i 1. Problema este asemntoare celei precedente. Pentru k=1,...,n considerm numrul sk a crui scriere n baza 10 este format din k de 1, adic s1=1, s2=11 etc.; fie sk clasa de echivalen modulo n corespunztoare. La fel ca mai sus, deosebim dou situaii: 1) dac exist k cu sk=0, atunci o soluie este chiar sk; 2) n caz contrar, este clar c s1,...,sn{1,2,...,n-1}. Conform principiului lui Dirichlet, vor exista indicii k<l cu sk=sl. Atunci o soluie este sl-sk, adic numrul n a crei scriere apar l-k de 1 urmai de k de 0. Exemplul 4. Teorema lui Erds. Se dau (m-1)(n-1)+1 numere naturale oarecare. S se arate c printre ele exist m care se divid unul pe altul, sau exist n care nu se divid ntre ele. Vom aplica tot principiul lui Dirichlet, dar n plan. Se ordoneaz mai nti cresctor numerele date. Considerm un caroiaj cu n-1 linii i m-1 coloane. Vom presupune c exist i linia imaginar cu numrul de ordine 0, pe care este plasat numrul 1. Considerm pe rnd numerele (n ordine cresctoare). Fiecare numr va fi plasat pe linia i cu i minim i cu proprietatea c numrul curent se divide cu un numr aflat pe linia anterioar. Pentru exemplificare, s considerm c m=5, n=4, iar numerele sunt: 3,5,9,12,14,15,33,x,... Dup plasarea primelor 8 numere, suntem n situaia: 3 9 24 5 12 14 15 33

Dac de exemplu x=72, atunci el ar trebui plasat pe a patra linie (inexistent) i am obine n=4 numere care se divid unul pe altul (de exemplu 3, 12, 24, 72).

116

104

10. ALTE TIPURI DE ALGORITMI

Dac de exemplu x=35, atunci el ar trebui plasat pe a doua linie i am obine m=5 numere care nu se divid ntre ele: 9, 12, 15, 33, 35. Este important de notat c pe fiecare linie numerele plasate apar n ordine cresctoare. Principiul lui Dirichlet ne asigur c cel mai trziu dup plasarea ultimului numr vom iei din "cutie": aici caroiajul (n-1)(m-1). O variant a principiului lui Dirichlet este urmtoarea: Fie k1,...kn , K=(k1+...+kn)/n i x un numr oarecare. Atunci: dac x<K i cu x<ki dac x>K i cu x>ki Exemplul 5. Dac vrfurile unui decagon sunt etichetate distinct cu numerele 0,1,...,9 ntr-o ordine oarecare, atunci exist trei vrfuri consecutive pentru care suma etichetelor este mai mare dect 13. Fie xi{0,1,...,9} eticheta vrfului i, pentru i=1,...,10. Considerm numerele:
k1=x1+x2+x3 k2=x2+x3+x4

. . . . .

k9=x9+x10+x1 k10=x10+x1+x2

Atunci K=(k1+...+kn)/n = 3(0+1+...+9)/10 = 13,5. Conform principiului lui Dirichlet, va exista i cu ki>13,5>13. Exemplul 6. Se consider m calculatoare i n imprimante (m>n). Fie =numrul minim de legturi calculatorimprimant ce trebuie stabilite, astfel nct dac orice n calculatoare doresc s scrie simultan, acest lucru s fie este posibil. Se cere s se arate c n(m-n+1). Fie ki numrul de calculatoare legate la imprimanta i. Numrul de legturi este deci =k1+...+kn. Dac <n(m-n+1), atunci (k1+...+kn)/n<m-n+1. Conform variantei principiului lui Dirichlet, exist o imprimant i legat la cel mult m-n calculatoare, deci care nu este legat la cel puin n calculatoare; dac acestea vor s scrie simultan, nu vor reui. Contradicie. Exerciiu. S se descrie o modalitate prin care problema poate fi rezolvat cu exact n(m-n+1) legturi.

117

Expresii aritmetice. Evaluare i generarea codului


Ne vom ocupa de expresiile aritmetice n care intervin operatorii binari +,-,*,/ i cuprinderea ntre paranteze. Vom considera c expresiile cu care lucrm sunt corecte. Pentru simplificare, vom presupune c variabilele care apar n expresii sunt litere. Scopul propus const n evaluarea expresiilor (presupunnd c variabilele au valori stabilite oarecare) i n generarea codului corespunztor expresiei, ntr-un limbaj de asamblare ad-hoc. tim c: o expresie este o "sum" de termeni (ntre care intervin numai operatorii de adunare i scdere); un termen este un "produs" de factori (ntre care intervin numai operatorii de nmulire i mprire); un factor este fie o variabil, fie o expresie ntre paranteze. Gramatica independent de context corespunztoare, cu simbolul iniial E, este:
E X T F T+E | T-E | T T*F | T/F | F liter | (E)

Primul pas n rezolvarea problemelor enunate const n construcia arborelui binar ataat expresiei. Exemplu. Expresiei aritmetice ((a-b)*(a+c))/(d-(e+f)) i corespunde arborele:
1 2 3

10

11

12

13

unde etichetele vrfurilor sunt et=(/,*,-,-,+,d,+,a,b,a,c,e,f). Am vzut c acest arbore binar poate fi construit pe baza algoritmului de analiz sintactic. Vom prezenta ns i un program Java care construiete direct acest arbore; la intrare poate s apar orice expresie aritmetic (cu condiia s fie corect).
118

Evaluarea expresiilor aritmetice


Pe baza expresiei aritmetice vom construi arborele asociat. Este clar c pentru evaluarea expresiei, acest arbore trebuie parcurs n postordine; evident, trebuie cunoscute valorile variabilelor ce intervin n expresie. Construcia arborelui urmeaz ntocmai definiia expresiilor aritmetice: va fi scris cte o metod pentru expresie, termen i factor. Fiecare vrf (obiect de tipul clasei varf) are cmpurile st, dr i et desemnnd respectiv descendentul stng, cel drept i eticheta sa. Se presupune c variabilele sunt litere mici de la nceputul alfabetului i pn la o liter last citit de la intrare. Deci numai pentru literele de la 'a' pn la last vor fi citite valorile lor. Mai este folosit o variabil ch care joac rolul unui "spion" ctre caracterul urmtor din expresie. Vom folosi asociativitatea la stnga a operatorilor. De exemplu dac un termen ncepe cu un "produs" de factori pentru care am construit arborele de rdcin x i urmeaz un factor pentru care am construit obiectul y, x va deveni descendent stng al lui y, care va fi rdcina noului arbore corespunztor termenului:
y

Prezentm programul Java pentru construirea arborelui ataat expresiei i evaluarea expresiei:
class Expresie { public static void main(String[] s) { varf Ob = new varf(); varf.rad = Ob.expresie(); IO.writeln("Val. expresiei: " + Ob.valoare(varf.rad)); } } class varf { static varf rad; static char last,ch; static double[] val; char et; varf st,dr; varf() { IO.write("Ultima litera: "); last = IO.readch(); val = new double[last-'a'+1]; for (char c='a'; c<=last; c++) { IO.write(c + "= "); val[c-'a'] = IO.read(); } ch = IO.readch(); }

119

varf(char e) {et =e; } varf factor() { varf x; if ((ch>='a') && (ch<=last)) { x=new varf(ch); ch = IO.readch(); } else { ch = IO.readch(); x = expresie(); ch = IO.readch(); } return x; } varf termen() { varf x,y; x = factor(); while ( (ch=='*') || (ch=='/') ) { y = new varf(ch); y.st = x; ch = IO.readch(); y.dr = factor(); x=y; } return x; } varf expresie() { varf x,y; x = termen(); while ( (ch=='+') || (ch=='-') ) { y = new varf(ch); y.st = x; ch = IO.readch(); y.dr = termen(); x=y; } return x; } double valoare(varf x) { if (x.st == null) return val[x.et - 'a']; else if (x.et == '+') return valoare(x.st) else if (x.et == '-') return valoare(x.st) else if (x.et == '*') return valoare(x.st) else if (x.et == '/') return valoare(x.st) else return 9999; } }

+ * /

valoare( valoare( valoare( valoare(

x.dr); x.dr); x.dr); x.dr);

120

Generarea codului pentru expresii aritmetice


Presupunem c nu avem restricii de memorie (deci putem introduce oricte variabile suplimentare) i c dispunem de un registru r pentru efectuarea operaiilor. Instruciunile disponibile i semnificaiile lor sunt urmtoarele: 1) LOAD x 2) STORE x 3) x
r x r x r r x, unde {+,-,*,/}.

Observm c toate instruciunile au dou cmpuri. Se consider c toate aceste instruciuni necesit acelai timp de executare. Nu vom lua n considerare nici una dintre proprietile algebrice ale operaiilor (comutativitate, asociativitate, distributivitate), nici posibilitatea reducerilor unor termeni i nici eventuala apariie a unor subexpresii care se repet. Dorim s construi un un cod (o succesiune de instruciuni de tipurile de mai sus), a crui executare s aduc n registrul r valoarea expresiei aritmetice. Dorim de asemenea ca acest cod s fie optim, adic s fie format dintr-un numr minim de instruciuni. Observaie. n orice cod neredundant: 1) numrul de instruciuni de tipul 3) este egal cu numrul vrfurilor interne, deci cu numrul operatorilor ce apar n expresie; prin urmare acest numr este fix; 2) orice instruciune LOAD, afar de prima, este precedat imediat de o instruciune STORE. Codul ncepe cu o instruciune LOAD. Cu alte cuvinte, numrul instruciunilor LOAD este mai mare cu o unitate dect cel al instruciunilor STORE: #(LOAD)=#(STORE)+1. De aceea un cod optim este un cod cu un numr minim de instruciuni STORE . Este evident c pentru construirea codului, arborele trebuie parcurs n postordine. Atam fiecrui vrf x codul Cx n care n prima instruciune lipsete cmpul
LOAD.

Vrfurilor terminale (frunzelor) x le atam codul: _ et(x). Pentru vrfurile neterminale x vom folosi codurile ataate subarborelui stng st i subarborelui drept dr. Deosebim urmtoarele cazuri: 1) dac descendentul drept al lui x este o frunz, codul Cx va fi:
Cst et(x) Cdr

2) dac descendentul drept al lui x nu este o frunz, se observ c este mai convenabil (din punctul de vedere al numrului de instruciuni) s ncepem cu evaluarea subarborelui drept:
Cdr STORE T LOAD Cst et(x) T unde T este o variabil suplimentar.

121

Variabilele suplimentare vor fi notate cu Tk, unde k este iniial 0 i crete cu o unitate la fiecare construcie a unui nou cod pentru un vrf al crui descendent drept nu este o frunz. Pentru exemplul considerat mai sus obinem succesiv:
C8: C9: C4: C10: C11: C5: C2: + + STORE LOAD * a b a b a c a c a c T1 a b T1 d e C1: C13: C7: C3: + + STORE LOAD + STORE LOAD STORE LOAD STORE LOAD * / f e f e f T2 d T2 e f T2 d T2 T3 a T1 a b T1 T3

C6: C12:

n programul Java prezentat mai sus efectum urmtorele modificri: n metoda principal adugm instruciunea:

IO.writeln("Codul atasat:\n" + "LOAD\t" + Ob.cod(varf.rad)); - n clasa varf adaugm cmpul static k pentru evidena variabilelor suplimentare: static int k; precum i metoda cod care produce codul ataat expresiei, conform parcurgerii n

postordine:
String cod(varf x) { int k1; if (x.dr==null) return x.et + ""; if (x.dr.dr==null) return cod(x.st) +"\n" + x.et + "\t" +cod(x.dr); k1=k; k++; return cod(x.dr) + "\nSTORE\tT" + k+ "\nLOAD\t" + cod(x.st) + "\n" + x.et + "\tT" + k;

122

Mai rmne de demonstrat optimalitatea codului obinut n modul descris mai sus. Propoziie. n orice cod ataat unui arbore binar, #(STORE) este cel puin egal cu numrul vrfurilor al cror descendent drept nu este frunz. Vom face demonstraia prin inducie dup numrul n al vrfurilor din arbore. Pentru n=1, afirmaia este evident adevrat. Presupunem afirmaia din enunul propoziiei adevrat pentru orice arbore cu cel mult n vrfuri i considern un arbore cu n+1 vrfuri. Dac descendentul drept al rdcinii este o frunz, atunci numrul vrfurilor al cror descendent drept nu este frunz coincide cu numrul vrfurilor din subarborele stng al cror descendent drept nu este frunz; cum acest subarbore are cel mult n vrfuri, putem aplica ipoteza de inducie. Dac descendentul drept al rdcinii nu este o frunz, atunci fie: n= numrul vrfurilor din arbore al cror descendent drept nu este frunz; s= numrul de instruciuni STORE din codul ataat arborelui; n1= numrul vrfurilor din subarborele stng al cror descendent drept nu este frunz; s1= numrul de instruciuni STORE din codul ataat subarborelui stng; n2= numrul vrfurilor din subarborele drept al cror descendent drept nu este frunz; s2= numrul de instruciuni STORE din codul ataat subarborelui drept. Conform ipotezei de inducie avem: s1n1 i s2n2. Evident, n=n1+n2+1. Pe de alt parte n codul ataat arborelui mai trebuie introdus cel puin o instruciune STORE, deci ss1+s2+1. Rezult sn1+n2+1=n. Optimalitatea algoritmului prezentat rezult acum din faptul c el introduce o nou instruciune STORE numai pentru vrfurile al cror descendent drept nu este o frunz.

123

12

NP-COMPLETITUDINE

tim c doar algoritmii polinomiali sunt eficieni, deci urmrim totdeauna s elaborm astfel de algoritmi. Este evident ns c nu exist algoritmi polinomiali pentru orice problem, ca de exemplu n cazul n care numrul datelor de ieire nu este polinomial: determinarea tuturor submulimilor unei mulimi finite, generarea permutrilor etc. De aceea cutm s delimitm clasa problemelor pentru care ncercm s elaborm algoritmi polinomiali. n acest scop introducem noiunea de algoritm nedeterminist. Algoritmii nedeterminiti sunt algoritmi secveniali care, spre deosebire de cei obinuii, admit i urmtoarele instruciuni suplimentare: 1) success i failure, care arat c algoritmul se termin cu succes, respectiv cu eec. Ele nlocuiesc instruciunea stop. Forma lor arat c vom studia doar probleme de decizie, pentru care rezultatul poate fi doar afirmativ sau negativ (dup cum vom vedea, foarte multe probleme pot fi aduse la aceasta form). 2) choice(A), unde A este o mulime finit; este o funcie care ntoarce ca rezultat un element oarecare al lui A. Se consider c cele trei instruciuni nou introduse necesit un timp constant. Maina abstract care execut un astfel de algoritm, cnd ntlnete o instruciune choice lucreaz astfel: - dac exist o valoare din A care conduce la o instruciune success, va fi aleas o altfel de valoare; - n caz contrar, va fi aleas o valoare oarecare. Cu alte cuvinte, un algoritm nedeterminist se termin cu eec dac nu exist o modalitate de a efectua alegeri care s conduc la o instruciune success. Funcionarea mainii abstracte se aseamn calculului paralel. De cte ori este ntlnit o instruciune choice, intr n aciune attea procesoare cte

124

114

12. NP-COMPLETITUDINE

elemente are mulimea A. Algoritmul se termin cu succes dac unul dintre procesoarele active ajunge la o instruciune success. Timpul de executare va fi, n cazul unei terminri cu succes, timpul necesar ajungerii la respectiva instruciune success. Mai precizm c ne intereseaz doar terminrile cu succes. Exemplul 1. Se cere s se verifice dac un numr x apare sau nu n mulimea {a1,...,an}. tim c timpul de executare pentru algoritmul determinist pentru aceast problem este de ordinul O(n), adic liniar. Putem scrie urmtorul algoritm nedeterminist:
i choice({1,2,...,n}) if ai=x then write i; success else failure

care rezolv problema n timp constant. Exemplul 2. Se cere s se ordoneze cresctor vectorul a=(a1,...,an). tim c cel mai bun algoritm determinist pentru aceast problem necesit un timp de ordinul O(n.log n). Ideea algoritmului nedeterminist este urmtoarea: copiem elementele vectorului a ntr-un vector auxiliar b ntr-o ordine oarecare, dup care verificm dac elementele lui b apar n ordine cresctoare.
for i=1,n bi

{ iniializarea lui b }

for i=1,n j choice({1,2,...,n}) if bj= then bj ai { fiecare ai trece pe o poziie nou din b } else failure for i=1,n-1 if bi>bi+1 then failure write(b); success

Timpul de executare al acestui algoritm este O(n), deci liniar. Se observ c: - am obinut un timp de executare mai bun; - problema sortrii a fost tratat ca o problem de decizie.

125

12. NP-COMPLETITUDINE

115

Exemplul 3. Problema validrii. Fie F(x1,...,xn) o expresie boolean n forma normal conjunctiv (FNC): F=C1...Ck , unde C1,...,Ck sunt disjuncii de variabile de forma xj sau xj ( xj este negaia lui xj). Se cere s se determine dac exist
a1,...,an{0,1} cu F(a1,...,an)=1.

Problema va fi referit n continuare sub numele VALID. Un exemplu de instan a problemei este: F(x1,x2,x3)=(x1x2x3)( x1 x 2 x 3 ). Un algoritm nedeterminist simplu care rezolv problema este urmtorul:
for i=1,n xi choice({0,1}) if F(x1,...,xn)=1 then success else failure

Timpul este proporional cu lungimea formulei, deci liniar. Observaie. Nu se cunoate un algoritm determinist polinomial pentru problema validrii ! Introducem clasele de probleme (de decizie) urmtoare: P - clasa problemelor pentru care exist algoritmi determiniti polinomiali; NP - clasa problemelor pentru care exist algoritmi nedeterminiti polinomiali. Vom studia problema existenei algoritmilor (determiniti) polinomiali doar pentru problemele din NP. Evident PNP. Este ns incluziunea strict sau P=NP ? Precizm de la nceput c aceast problem este deschis! n 1976, Samuel Cook a obinut un rezultat care a simplificat problema i care a prut promitor n rezolvarea ei: Teorem. P=NP VALIDP. Teorema spune c dac reuim s gsim un algoritm determinist polinomial pentru VALID, atunci va exista un algoritm determinist polinomial pentru orice problem din NP.

126

116

12. NP-COMPLETITUDINE

ntruct nu s-a reuit s se obin un algoritm polinomial pentru VALID, sa ncercat s se rezolve probleme "echivalente" cu VALID. Mai precis s-a definit clasa NPC . Clasa de probleme NPC este definit astfel: 1) VALID NPC 2) Pentru o problem P, P NPC dac: 2.1) se cunoate pentru P un algoritm nedeterminist polinomial; 2.2) VALID se poate reduce la P n timp determinist polinomial. Problemele din NPC se numesc NPcomplete. NPC NP P Observaie. Este suficient s artm pentru o singur problem NP complet c admite un algoritm polinomial, pentru a obine egalitatea P=NP. Lista problemelor NPcomplete a depit 1000, dar pentru nici una dintre ele nu s-a reuit s se obin un algoritm determinist polinomial. Drept urmare, aa cum am menionat anterior, problema egalitii P=NP rmne o problema deschis. Prezentm n continuare una dintre multele probleme NPcomplete. Problema clicii maximale. Fie G=(X,M) un graf neorientat. YX se numete clic dac pentru i,jY avem (i,j)M. Se caut ordinul unei clici maximale. Problema de decizie corespunztoare este urmtoarea: Pentru un k dat, exist n G o clic de ordin k ? Pentru aceast problem vom scrie procedura clica(G,k), care furnizeaz rspunsul n timp nedeterminist polinomial. Presupunnd cunoscut acest lucru, algoritmul pentru problema clicii maximale va fi:
for k=n,1,-1 clica(G,k)

care este tot polinomial.

127

12. NP-COMPLETITUDINE

117

procedure clica(G,k) for i=1,n ales(i) 0 {elementul i nu a fost ales} { pentru fiecare i alegem un element j{1,...,n} pe care l punem n bi } for i=1,k j choice({1,...,n}) if ales(j)=1 then failure else ales(j) 1; bi j { s-au ales k vrfuri distincte b1,...,bk } for i=1,k for j=1,k if ij & (bi,bj)M then failure write(k); success end;

Timpul este evident polinomial. Prin urmare CLICA(k) NP, deci i CLICA NP. Artm n continuare c VALID se reduce la CLICA n timp determinist polinomial. Fie F(x1,...,xn) o expresie boolean n forma normal conjunctiv (FNC): F=C1...Ck , unde C1,...,Ck sunt disjuncii de variabile de forma xj sau xj , numii literali. Atam lui F graful G=(X,M) astfel: X = {(,i) | literal din Ci} M = {[(,i),(,j)] | }, adic i pot fi satisfcui concomitent. De exemplu, pentru F(x1,x2,x3) = (x1x2x3) ( x1 x 2 x 3 ), graful este:
(x1,1) (x2,1) (x3,1) ( x1 ,2) ( x2 ,2) ( x3 ,2)

Construcia grafului necesit un timp determinist polinomial. Mai trebuie demonstrat c: n G exist o clic de ordin k F este validabil.

128

118

12. NP-COMPLETITUDINE

ncepem cu demonstrarea necesitii. Fie S = {(i,i) | i=1,...,k} o clic de ordin k. Fie S1 = {i | i=1,...,k}. Conform construciei lui M, nu este posibil ca i i i s apar simultan n
S1.

Alegem fiecare ai astfel: 1 dac xiS1, adic i=xi 0 dac xi S1 arbitrar n caz contrar. Pentru aceast alegere, F(a1,...,an)=1, fiecare conjuncie avnd valoarea 1. Pentru exemplul considerat: S={(x1,1),( x 3 ,2)} ; S1={x1, x 3 } ; a1=1 ; a2 arbitrar ; a3=0. Continum cu demonstrarea suficienei. Conform ipotezei, exist a1,...,an cu Ci(a1,...,an)=1, i=1,...,k. Pentru fiecare i=1,...,k, exist un literal i din Ci care are valoarea 1. Fie S = {(i,i) | i=1,...,k}. S este o clic. ntr-adevr, pentru (i,i),(j,j)S diferite rezult ij i i j , deoarece pentru x=(a1,...,an) avem i=j=1. ncheiem prin a prezenta enunul altor probleme NP-complete. 1) Fie G=(X,M) un graf. YX se numete k-acoperire dac |Y|=k i dac pentru orice (i,j)M avem iY sau iY. Exist o k-acoperire? 2) Problema ciclului hamiltonian. 3) Problema colorrii hrilor. 4) Fie A o mulime i fie s:AZ+. Cutm BA cu proprietatea c suma elementelor lui B s fie egal cu suma elementelor lui A\B.

129

Analiza sintactic
Toate gramaticile cu care vom lucra n acest sunt independente de context; de aceea nu vom mai specifica explicit acest lucru. Fie G=(VN,VT,S,P) o gramatic (independent de context). Analiza sintactic const n urmtoarele: * - se d w VT ; - se cere s se determine dac wL(G) i, n caz afirmativ, s se determine produciile care trebuie aplicate pentru a obine o derivare stng a lui w din S. Observaie. Dac produciile au numere de ordine, irul numerelor de ordine ale produciilor ce trebuie aplicate este numit sintaxa stng a lui w. Ea este important, deoarece pe baza ei se poate construi uor arborele de derivare asociat lui w. Vom folosi urmtoarele convenii de notaii:
a,b,c,... VT A,B,C,... VN * x,y,z,u,v,w,... VT ,,,... (VN VT)*

i se va specifica explicit cnd ele sunt nclcate. n continuare considerm c n G nu apar neterminale "nefolositoare", adic: din orice neterminal deriv un cuvnt format numai din terminale (neterminalul este observabil); - pentru orice neterminal exist o derivare din S n care apare acel neterminal (neterminalul este accesibil). tim c pentru orice gramatic independent de context exist una echivalent de acelai tip, n care nu apar neterminale nefolositoare, deci presupunerea fcut nu este restrictiv. De asemenea, toate derivrile care vor aprea n continuare sunt derivri stngi (tim c existena unei derivri este echivalent cu existena unei derivri stngi). n sfrit, nchiderea tranzitiv i reflexiv a relaiei de derivare va fi notat tot prin , pentru a nu ncrca scrierea. Definim dou funcii cu care vom lucra n continuare. : (VNVT)* P(VT{}) (prin P am notat mulimea prilor), dat de: () = {aVT|ax} {|dac }. Funcia mai poart numele FIRST. * Caz particular: pentru VT , ()={a}, unde a este prima liter din (dac ) sau este (dac =).
* Definiia funciei se poate extinde la mulimi de cuvinte din (VN VT) . Pentru * L (VN VT) definim: (L)={()|L}.

130

Definim funcia :VN P (VT {}) prin:


( A) = {a VT {} | , cu S A cu a ( )}
= {a VT | , cu S Aa } { | daca cu S A}
*

Definiie. G este o gramatic LL(1) dac:


S wA w wx S wA w wy

( x) = ( y )
adic, dat fiind un cuvnt wA i un terminal a, exist cel mult o producie (cu A n membrul stng) care se poate aplica astfel nct n final s se obin un cuvnt format din terminale n care imediat dup w s apar a. Teorem. G este de tip LL(1) A , A P distincte, avem ( ( A)) ( ( A)) = . Fie
S wA w wx S wA w wy

( x) = ( y ) Cum , conform ipotezei, avem: ( ( A)) ( ( A)) = . Dar ( x ) ( ) ( ( A)) ( y ) ( ) ( ( A)) ( x ) = ( y ) , deci ( ( A)) ( ( A)) . Contradicie.
Fie A , A P distincte. Presupunem c a VT {}, a ( ( A)) ( ( A)) . Dac a VT avem patru situaii: 1)
a ( ) ( ) deci ax1 , ax 2

w, cu S wA (nu exist neterminale nefolositoare)


S wA w wax1 y1

S wA w wax 2 y 2

= .

Contradicie.

(ax1 y1 ) = (ax 2 y 2 ) = {a}

131

2) adic

a ( A) ( )
; ax , deci ax

w, cu S wA i ay
S wA w w way
w waxay

= . Contradicie.

( ay ) = ( axay ) = {a}
3) 4)
a ( ) ( A)
a ( A), ,

Se face la fel ca la 2).

Deci w, cu S wA cu ax
S wA w w wax
w w wax

= . Contradicie.

( ax ) = ( ax ) = {a}
Dac a =

Atunci avem ( A) i deci w, cu S wA i ( ) , adic .


S wA w w w = w
w w w = w
*

= .

Contradicie.

=
Corolar. G este LL(1) { A 1 ,..., A n } P , ( i ) ( j ) =

pentru i j; dac i , atunci ( j ) ( A) = j i .

132

Traductorul sintactic
Este asemntor unui automat push-down. Are ns n plus o band de ieire i funcioneaz pe baza unui tabel de analiz sintactic conform unui algoritm sintactic.

Band de intrare

Banda de ieire Tabel de analiz sintactic X

Memorie push-down

$ Dac G este o gramatic LL(1), atunci: pe banda de intrare apar cuvinte w VT* ; pe banda de ieire apar iruri formate din numerele de ordine ale produciilor din gramatic; n memoria pushdown apar cuvinte de forma $ cu (V N VT ) * . Prin configuraie nelegem un triplet (x, , ) unde: x VT* este cuvntul care a mai rmas de citit de pe banda de intrare; este cuvntul din memoria p.d.; este irul aflat pe banda de ieire.

Configuraia iniial este (w, S$, ) unde w este cuvntul care trebuie analizat sintactic. Trecerea de la o configuraie la alta este dat de algoritmul sintactic pe baza tabelului de analiz sintactic.

133

Tabelul de analiz sintactic


(V N VT {$}) (VT {}) .

Tabelul

de

analiz

sintactic

este

funcie

definit

pe

Pentru gramaticile de tipul LL(1) vom defini acest tabel conform regulilor: 1) Fie A i P . Definim M ( A, a ) = ( , i ) pentru a ( ) \ {} . Dac n plus ( ) , definim M ( A, b) = ( , i ), b ( A) . 2) M ( a, a ) = SALT 3) M ($, ) = DA 4) n rest, M ( , ) =NU. Observaie: Fie A i P . Atunci M ( A, a ) = ( , i ) a ( ( A)) Aceast observaie arat, pe baza corolarului precedent, c funcia M este bine definit. Exemplu: S aAS 1) S b 2) Aa 3) A bSA 4) M S A a b $ a aAS,1 a,3 SALT b b,2 bSA,4 SALT DA

Algoritmul sintactic
Pentru gramaticile LL(1) definim urmtorul algoritm sintactic: 1) ( x, X , ) | ( x, , i ) dac M ( X , ( x )) = ( , i ) , adic ( x ) ( ( X )) 2) (ax, a , ) | ( x, , ) , ceea ce corespunde situaiei M ( a, a ) = SALT 3) Algoritmul se termin dac: 3.1) se ajunge la ( ,$, ) ; se aplic deci M ($, ) = DA 3.2) se ajunge la ( x, X , ) cu M ( X , ( x )) = NU. Exemplu. Pentru gramatica de mai sus: ( abbab , S $, ) | ( abbab , aAS $,1) | (bbab , AS $,1) | (bbab,bSAS$,14) | (bab,SAS$,14) | (bab,bAS$,142) | (ab,AS$,142) | (ab,aS$,1423) | (b,S$,1423) | (b,b$,14232) | (,$,14232) deci w=abbabL(G) i sintaxa sa stng este =14232.

134

Fie acum un tabel de analiz sintactic M oarecare i fie A un algoritm sintactic construit pe baza acestui tabel. Atunci pentru w VT definim ,dac ( w, S $, ) | ( ,$, ) A(w) = nedefinit , n caz contrar Definiie. Algoritmul i tabelul sunt corecte dac: * 1) w L (G ) ( w, S $, ) | ( ,$, ) 2) Dac da, S w . Definiie. Algoritmul A este corect dac : 1) L(G ) = {w VT | A( w) definit} 2) Dac A(w) = , atunci este sintaxa stng a lui w (adic S w ).

Definiie. Tabelul M este corect dac algoritmul construit pe baza lui este corect. Remarcm c n cazul unui tabel i a unui algoritm corecte, algoritmul se oprete ntr-una dintre situaiile: 1) w L(G), adic w este corect sintactic; n acelai timp este furnizat sintaxa lui; 2) w L(G), adic w nu este corect sintactic; n aceast situaie va trebui produs un mesaj de eroare. Teorem. Algoritmul sintactic i tabelul de analiz sintactic definite mai sus pentru gramaticile LL(1) sunt corecte. Demonstraia cuprinde doi pai. * Artm mai nti c din (xy, S$, ) | (y, $, ) rezult S x :

Demonstraia se face prin inducie dup numrul n de schimbri de configuraie. Pentru n = 1 (xy, S$, ) | (y, $, ) implic x = , = i, M(S, (y)) = (, i) unde
S i P . Rezult S x . n n+1 * (xy, S$, ) | (ay, $, ) | (y, $, ), unde a VT {}. Implic x = xa. n

* Conform ipotezei de inducie, din (x(ay), S$, ) | (ay, $, ) rezult S x' . n Dac a = , atunci = A, = , =i, A i P, M ( A, ( y )) = ( , i ) . Rezult S x' = xA x = x . Dac a , atunci = a, = i S x' = x' a = x .
'
i

'

135

Mai artm c:

* S x ( xy, S $, ) | (y, $, ) y cu (y) (), unde este fie , fie ncepe cu un neterminal. n = 1 : S i x . Fie y cu (y) (). i * 1) x : (xy, S$, )| (xy, x$, ) pentru c (xy) (x(S)) | (y, $, ) 2) x = : (y, S$, )| (y, $, i) pentru c (y) ( ) ((S)). n n+1

S x1 A 1 x cu = i (am pus n eviden ultimul pas)


n

'

A i x 2 2

x1 x1 x

A x2

1 2 1

Din 2 nceput al lui rezult c 2 = sau 2 ncepe cu un neterminal. * Fie y cu (y) (). Trebuie ( xy , S $, ) | (y, $, ). * Cf. ipotezei de inducie, pentru z cu (z) (A1), avem : (x, z, S$, )| (z, A1$, ). Lum z = x2y. Atunci (x2y) (A1) deoarece (x2y) (x2) (A1) pt. c A1 x221 = x2. * Deci (x1x2y, S$, ) | (x2y, A1$, ). =x ? * Mai trebuie (x2y, A1$, ) | (x2y, x221$, i) | (y, $, ). ? dac (x2y) (x22(A)): 1) dac x2 , evident 2) dac x2 = trebuie (y) (2(A)). Dar (y) () = (21) = (2(1))

(2(A)) pentru c S x1 A 1 .
nlocuind acum y = = n paii notai cu , sse obine rezultatul din enunul teoremei.

136

Aplicaie
Considerm urmtoarea gramatic ce genereaz expresiile aritmetice corecte n care apare variabila a, operaiile binare + i *, precum i cuprinderea ntre paranteze.
S S T T F F

S+T T T*F F (S) a

Este evident c aceast gramatic nu este de tipul LL(1). Ea poate fi adus ns la o gramatic de tipul LL(1), folosind faptul c ansamblul de producii:
A A A

este echivalent cu ansamblul de producii:


A A' A' A' A'

Obinem astfel urmtoarea gramatic echivalent de tipul LL(1):


(1) (2) (3) (4) (5) (6) (7) (8) S E E T U U F F

TE +TE FU *FU (S) a

Tabelul de analiz sintactic este urmtorul:


S E T U F a ( ) + * $ a TE,1 FU,4 a,8 SALT ( TE,1 FU,4
,6 ,6

)
,3

+ +TE,2

,3

*FU,5

,6

(S),7 SALT SALT SALT SALT DA

137

S considerm cuvntul w=(a*a) i s aplicm algoritmul sintactic:


[(a*a),S$,] [(a*a),TE$,1] [(a*a),FUE$,14] [(a*a),(E)UE$,147] [a*a),E)UE$,147] [a*a),TE)UE$,1471] [a*a),FUE)UE$,14714] [a*a),aUE)UE$,147148] [*a),UE)UE$,147148] [*a),*FUE)UE$,1471485] [a),FUE)UE$,1471485] [a),aUE)UE$,14714858] [),UE)UE$,14714858] [),E)UE$,147148586] [),)UE$,1471485863] [,UE$,1471485863] [,E$,14714858636] [,$,147148586363]

Rezult c w este corect ( adic wL(G) ) i sintaxa sa stng este =147148586363, pe baza cruia putem construi arborele de derivare. Observaie. De fapt am redus problema la determinarea funciilor i , sarcin ce nu este deloc facil!

138

1. Interclasare optim de iruri ordonate Pentru n iruri ordonate cresctor se dau lungimile acestora. Se dorete s se obin un singur ir ordonat care conine toate elementele irurilor iniiale, interclasnd iruri dou cte dou. S se determine ordinea n care trebuie efectuate aceste interclasri astfel nct numrul de deplasri s fie minim (pentru interclasarea a dou iruri de lungimi m i respectiv n sunt necesare m+n deplasri). S se afieze i numrul total de deplasri efectuate. Observatie: Se cere doar ordinea interclasarilor, nu si interclasarea propriu-zisa. Indicaie: Strategia Greedy pentru rezolvarea acestei probleme este urmtoarea: la fiecare pas se combin cele mai scurte dou iruri disponibile la momentul respectiv.
procedure INTERCLASARE_OPTIMA for i :=1 to n do adauga( (Li, i), A) for i:= n+1 to 2n-1 (Lj, j) := extrage_minim(A) (Lk, k) := extrage_minim(A) adauga((Lj + Lk, i), A) writeln 'interclasare S,j, ' de lungime ', Lj, ' cu S',k, ' de lungime ', Lk

Pentru a reine perechile (lungime, numr ir) se va folosi un min-ansamblu n raport cu lungimea irurilor (aia2i i aia2i+1 ,dac fiii exist). Elementul minim va fi a[1]. La adugarea unui element nou nu se va recrea ntreg ansamblu. Se poate folosi o metod nou (fa de cele fcute la sortarea cu ansamble) de a gsi locul n ansamblu pentru noul element introdus, interschimbnd elementul cu printele dac printele este mai mare (deci nu este verificat condiia de min-ansamblu) i urcnd astfel n arbore pn este gsit locul elementului nou introdus. O alt modalitate de a evita crearea ansamblului din nou dup fiecare interclasare este s nu extragem cel de al doilea minim din ansamblu, ci sa l nlocuim cu noul ir obinut, apelnd apoi procedura de refacere a ansamblului din rdcina, innd cont c subarborii drept i stng verific ambii condiia de ansamblu ( combin(1,n) ). Procedura devine
procedure INTERCLASARE_OPTIMA A := creare_ansamblu((Li, i), i = 1,..,n ) for i:= n+1 to 2n-1 (Lj, j) := extrage_minim(A) (Lk, k) := minim(A) inlocuieste_minim(A, (Lj + Lk, i)) combin(A,1,n) writeln 'interclasare S,j, ' de lungime ', Lj, ' cu S',k, ' de lungime ', Lk

2. Algoritmul lui Prim de determinare a unui arbore parial de cost minim. 3. Numere frumoase

139

Numerele frumoase sunt numerele care au ca factori primi doar pe 2, 3 i 5. Dat un numr natural n (1 n 1500) afiai primele n numere frumoase (n ordine). Indicaie: Se reine la fiecare moment cel mai mic multiplu de 2 nc neadugat i indicele din tablou care conine elementul din care s-a obinut prin nmulire cu 2. La fel pentru 3 i 5. SAU 3. Se dau n i k naturale, k n. S se construiasc o matrice nn care ndeplinete simultan condiiile: - conine toate numerele de la 1 la n2 o singur dat - pe fiecare linie numerele sunt aezate n ordine cresctoare, de la stnga la dreapta - suma elementelor de pe coloana k s fie minim.

140

Clasa String Cteva metode ale clasei String care pot fi utile n problemele date sunt
public int length()

Returneaz lungimea irului


public char charAt(int index)

Returneaz caracterul de pe poziia index din ir. Poziiile n ir sunt numerotate de la 0 la lungimea irului minus 1. Dac index nu se ncadreaz ntre aceste limite, metoda arunc excepie (IndexOutOfBoundsException)
public String substring(int beginIndex, int endIndex)

Returneaz un nou ir care reprezint subirul care ncepe pe poziia beginIndex a irului curent i se termin pe poziia endIndex - 1.
public String substring(int beginIndex)

Returneaz un nou ir care reprezint subirul care conine caracterele de la poziia beginIndex a irului curent pn la sfrit (se termin pe poziia length()-1)
public boolean equals(Object anObject)

Compar irul curent cu un obiect. Rezultatul returnat este true dac argumentul este un obiect nenul de tip String i reprezint aceeai secven de caractere ca i irul curent, i false n caz contrar .
public int indexOf(int ch)

Returneaz poziia primei apariii a caracterului ch n ir i -1 n cazul n care caracterul ch nu se gsete n ir Programare Dinamic 1. Se d un ir de cuvinte. S se determine cel mai lung subir al irului dat cu proprietatea c oricare dou cuvinte consecutive din acest subir verific relaia: ultimele dou litere din primul cuvnt coincid cu primele dou litere din urmtorul cuvnt. (Subirul unui ir este format din elemente ale irului, nu neaprat consecutive, dar care respect ordinea din irul iniial. Un subir al irului a1, a2, , an este de forma ai1, ai2, , aik, cu 1i1<i2<<ikn) De exemplu, pentru irul seara, carte, teorema, temperatura, rar, mare, arbore cel mai lung subir care verific cerinele este carte, temperatura, rar, arbore

141

2. Se consider un ir de n piese de domino. O pies de domino are form dreptunghiular i are nscris pe ea dou numere, cuprinse ntre 1 i 6. O pies poate fi ntoars (rotit cu 1800), inversndu-se astfel ordinea celor dou numere nscrise pe ea.

Conform regulilor la domino, un lan este un subir al irului de piese iniial constituit din piese care respect urmtoarea condiie: pentru oricare dou piese consecutive din lan, al doilea numr nscris pe prima din cele dou piese coincide cu primul numr nscris pe cea de a doua pies. S se determine cel mai lung lan care se poate obine cu piesele din irul dat . Exemplu: pentru n = 8 i irul de piese (1,5), (1,3), (2,6), (2,5), (4,3), (6,4), (2,4), (1,6) lungimea celui mai lung lan este 5, un astfel de lan fiind format cu piesele de pe poziiile 1(inversat), 2, 5(inversat), 6(inversat), 8(inversat) i anume (5,1), (1,3), (3,4), (4,6), (6,1) 3. Se dau cuvintele dintr-un dicionar. S se descompun un cuvnt dat ntr-un numr minim de cuvinte existente n dicionar. Exemplu: pentru dicionarul cu cuvintele a, apa, apar, ne, par, i, ine descompunerea minim pentru cuvntul aparine este format din cuvintele apar, ine; descompunerea minim pentru cuvntul apar este format doar din cuvntul apar, deoarece acesta este n dicionar. 4. Se consider alfabetul A format din literele mici ncepnd cu a i terminnd cu litera mic last citit de la intrare. Pe acest alfabet este dat o lege de compoziie printr-o matrice t (nu se presupune c legea este asociativ sau comutativ). Fiind dat un cuvnt x de lungime n peste alfabetul A i o liter dest din A, s se determine dac exist o parantezare a literelor cuvntului astfel nct rezultatul efecturii calculelor s fie dest

Divide et Impera 1. Problema tieturilor: Se d o plac dreptunghiular cu lungimea L i nlimea h n care exist n guri punctiforme, poziiile gurilor fiind date prin coordonatele carteziene ale acestora (x, y). S se determine placa de arie maxim care se poate decupa din placa iniial i care nu conine n interior nici o gaur. Sunt permise numai tieturi verticale i orizontale complete (de la o latur la latura opus)

142

Sistemul de axe se poate considera cu originea n colul din stnga-jos al plcii. Un dreptunghi va fi identificat prin coordonatele colului din stnga-jos (x1,y1) i coordonatele colului din dreapta-sus (x2,y2) . 2. Problema plierii: Se consider un vector de lungime n. Definim plierea vectorului prin suprapunerea unei jumti numit donatoare peste cealalt numit receptoare, cu precizarea c dac numrul de elemente este impar, numrul din mijloc este eliminat. n acest mod se ajunge la un subir ale crui elemente au numerotarea corespunztoare jumtii receptoare. Plierea se poate aplica n mod repetat pn se ajunge la un subir de lungime 1, numit element final. a) S se precizeze toate elementele finale b) Dat un element i, s se scrie o succesiune de plieri prin care se ajunge la el dac i este element final; dac i nu este final se va afia un mesaj corespunztor. De exemplu, vectorul (1, 2, 3, 4, 5) se poate plia n dou moduri (1, 2) sau (4, 5), elementul 3 fiind eliminat. 3. Se dau n cri de matematic (M), romn (R), fizic (F) i informatic (I) i trei rafturi corespunztoare tipurilor crilor: RR raftul crilor de romn RMI raftul crilor de matematic sau de informatic RF raftul crilor de fizic Pentru fiecare carte se citesc numele crii i tipul ei. Crile sunt aezate n teanc pe un raft A n ordinea n care sunt citite. Avnd la dispoziie dou rafturi B i X, s se transfere crile de pe raftul A pe rafturile RR, RMI i RF dup urmtoarele reguli: - pe rafturile A i B se pot pune sau lua oricte cri - pe raftul X se poate pune sau lua o singur carte - pe RR, RMI i RF se pot pune cri, nu i lua - la orice mutare avem acces la o singur carte cea din vrful teancului - tipul crii se poate citi numai n B i numai cnd este o singur carte pe acest raft - n final crile pe RR, RMI i RF trebuie s fie n aceeai ordine n care erau pe A. a) S se afieze mutrile pentru transferul crilor fr s se fac deosebire ntre crile de matematic i informatic. Se vor afia numrul de cri mutate i rafturile ntre care se mut, iar cnd se face un transfer al unei cri din B pe raftul corespunztor tipului se va afia titlul crii care se repartizeaz i tipul ei (vezi exemplul) b) Rezolvai problema astfel nct n final crile de matematic s apar n raftul RMI naintea celor de informatic (respectnd ordinea n care erau n A). Exemplu: Pentru crile de intrare (date cu nume i tip) Mate1 de tip M Fizica1 de tip F Rom1 de tip R Info1 de tip I

143

Info2 de tip I Mate2 de tip M Rom2 de tip R Fizica2 de tip F O posibil soluie la punctul a) ar fi (o carte s-a afiat sub forma nume tip) initial A=[Mate1 - M, Fizica1 - F, Rom1 - R, Info1 - I, Info2 - I, Mate2 - M, Rom2 - R, Fizica2 - F] Sir mutari 7 carti A->B, 1 carti A->X, 7 carti B->A , Repartizez Mate1 M, 6 carti A->B, 1 carti A->X, 6 carti B->A, Repartizez Fizica1 - F 5 carti A->B, 1 carti A->X, 5 carti B->A, Repartizez Rom1 R, 4 carti A->B, 1 carti A->X, 4 carti B->A, Repartizez Info1 I, 3 carti A->B, 1 carti A->X, 3 carti B->A, Repartizez Info2 - I 2 carti A->B, 1 carti A->X, 2 carti B->A, Repartizez Mate2 - M, 1 carti A->B , 1 carti A->X, 1 carti B->A, Repartizez Rom2 - R, 1 carti A->B, Repartizez Fizica2 - F, 1 carti X->B, 1 carti X->B, 1 carti X->B, 1 carti X->B, 1 carti X->B, 1 carti X->B, 1 carti X->B,

Rafturi carti dupa repartizare RF: [Fizica1 - F, Fizica2 - F] RMI: [Mate1 - M, Info1 - I, Info2 - I, Mate2 - I] RR: [Rom1 - R, Rom2 - R]

Observaii pentru implementare n Java Rafturile se pot memora ca stive, deoarece putem pune sau lua cri de la un singur cap al raftului (mutm mereu cartea din capul teancului). n Java exist clasa Stack (din pachetul java.util) pentru lucru cu stive. Pentru a introduce un element n stiv exist metoda push, pentru a elimina vrful stivei metoda pop (care returneaz elementul eliminat)

144

Pn la varianta 5.0 ntr-o stiv (obiect de tip Stack) se puteau introduce obiecte de orice tip. La extragerea unui element din stiv programatorul trebuia s tie ce tip de elemente a introdus n stiv i s fac o conversie explicit la acel tip. Chiar dac elementele introduse erau de acelai tip, tot era nevoie de conversie explicit, elementul extras dintr-o stiv fiind de tip Object. Spre exemplu, puteam introduce n stiv alternativ numere ntregi i iruri de caractere
Stack stiva=new Stack(); for (int i=0;i<5;i++){ stiva.push(new Integer(i)); stiva.push("valoarea "+i); } String sirTop=(String)stiva.pop(); System.out.println(sirTop); Integer x=(Integer)stiva.pop(); System.out.println(x);

Acest cod este corect i n varianta 5.0, doar la compilare se va semnala ca observaie faptul c programul folosete operaii nesigure. ncepnd cu aceast variant s-a introdus posibilitatea de a defini de la declararea stivei tipul elementelor care se vor introduce n stiv. Pe parcursul programului n stiv se vor putea introduce doar elemente de acest tip, iar la extragere nu mai este nevoie de conversie explicit, cunoscndu-se tipul elementului extras. Spre exemplu, s considerm clasa Carte care poate fi folosit pentru problema rafturilor
import java.util.Stack; class Carte{ String nume; int tip;//0-F,1-M,2-I, 3-R Carte(){ } Carte(String s, int t){ nume=s; tip=(byte)t; } public String toString(){ switch(tip){ case 0: return nume+" case 1: return nume+" case 2: return nume+" case 3: return nume+" } return ""; } }

F"; M"; I"; R";

145

class Rafturi{ public static void main(String arg[]){ Stack<Carte> raft=new Stack<Carte>(); raft.push(new raft.push(new raft.push(new raft.push(new Carte("romana",3)); Carte("fizica",0)); Carte("matematica",1)); Carte("informatica",2));

Carte c=raft.pop(); //nu mai este necesara conversie System.out.print("Ultima carte introdusa: "); System.out.println(c); System.out.print("Cartile ramase pe raft "); System.out.println(raft); } }

Observaie Metoda println apelat cu parametru un obiect invoc metoda


public String toString()

i afieaz rezultatul ntors de aceasta. Orice clas extinde clasa Object, care are metoda toString(). Suprascriind n clasa Carte aceast metod putem afia un obiect de tip Carte sub forma dorit. (Dac nu suprascriem aceast metod, ea va returna numele clasei i adresa de memorie corespunztoare obiectului) . Mai mult, metoda toString() a clasei Stack va apela metoda toString a fiecrui element al stivei (Nu mai este astfel nevoie s facem o metod de afiare a stivei, care s parcurg fiecare element al stivei i s-l afieze). Programul va afia Ultima carte introdusa: informatica - I Cartile ramase pe raft [romana - R, fizica - F, matematica - M]

Se vor prezenta 5 probleme din care obligatoriu 2 probleme de programare dinamic i 2 de divide et impera.

146