Documente Academic
Documente Profesional
Documente Cultură
Algoritmi
Algoritmi
ALGORITMI. TEHNICI I
LIMBAJE DE PROGRAMARE
Prefa
Cartea isi propune in primul rand sa fie un curs si nu o "enciclopedie" de algoritmi. Pornind de la structurile de date
cele mai uzuale si de la analiza eficientei algoritmilor, cartea se concentreaza pe principiile fundamentale de elaborare a
algoritmilor: greedy, divide et impera, programare dinamica, backtracking. Majoritatea algoritmilor selectati au o conotatie
estetica. Efortul necesar pentru intelegerea elementelor mai subtile este uneori considerabil. Ce este insa un algoritm "estetic"?
Putem raspunde foarte simplu: un algoritm este estetic daca exprima mult in cuvinte putine. Un algoritm estetic este oare in
mod necesar si eficient? Cartea raspunde si acestor intrebari.
In al doilea rand, cartea prezinta mecanismele interne esentiale ale limbajului Visual Basic si trateaza implementarea
algoritmilor in mod iterative cat si recursiv. Totusi, aceasta carte nu este un curs complet de Visual Basic. Algoritmii nu sunt
pur si simplu "transcrisi" din pseudo-cod in limbajul Visual Basic, ci sunt reganditi din punct de vedere al programarii orientate
pe obiect. Speram ca, dupa citirea cartii, veti dezvolta aplicatii de programare in mod iterative cat si recursiv si veti elabora
implementari ale altor structuri de date. Programele pot fi scrise si in limbajul C#. Acest limbaj se caracterizeaza, in principal,
prin introducerea claselor parametrice si a unui mecanism de tratare a exceptiilor foarte avansat, facilitati deosebit de
importante pentru dezvoltarea de biblioteci C#.
Fara a face concesii rigorii matematice, prezentarea este intuitiva, cu numeroase exemple. Am evitat, pe cat posibil,
situatia in care o carte de informatica incepe - spre disperarea ne-matematicienilor - cu celebrul "Fie ... ", sau cu o definitie. Am
incercat, pe de alta parte, sa evitam situatia cand totul "este evident", sau "se poate demonstra". Fiecare capitol este conceput
fluid, ca o mica poveste, cu putine referinte si note. Multe rezultate mai tehnice sunt obtinute ca exercitii. Algoritmii sunt
prezentati intr-un limbaj pseudo-cod compact, fara detalii inutile.
Presupunem ca cititorul nu are la baza cel putin un curs introductiv in programare, fiindu-i straini termeni precum
algoritm, recursivitate, functie, procedura si pseudo-cod. Exista mai multe modalitati de parcurgere a cartii. In functie de
interesul si pregatirea cititorului, acesta poate alege oricare din partile referitoare la elaborarea, analiza, sau implementarea
algoritmilor. Cu exceptia partilor de analiza a eficientei algoritmilor (unde sunt necesare elemente de matematici superioare),
cartea poate fi parcursa si de catre un elev de liceu. Pentru parcurgerea sectiunilor de implementare, este recomandabila
cunoasterea limbajului Visual Basic.
S-a dovedit utila si experienta autorului de peste douzeci de ani in dezvoltarea produselor software. Le multumesc
pentru aprecieri pro/contra asupra lucrrii membrilor catedrei de informatic de la Facultatea de tiine Economice i
Administraie Public.
Autorul,
Conf. univ. dr. Lupu Valeriu
Universitatea tefan cel Mare Suceava
Facultatea de tiine Economice i Administraie Public
Catedra de Informatic
Cuprins
Capitolul I
Capitolul II
Capitolul III
Capitolul IV
Capitolul V
Capitolul VI
Capitolul VII
Capitolul VIII
Capitolul IX
Capitolul X
Capitolul XI
Capitolul XII
Bibliografie
....................................................................................................................................135
CAPITOLUL I
DESCRIEREA ALGORITMILOR
1.1 Algoritm, program, programare
Apariia primelor calculatoare electronice a constituit un salt uria n direcia automatizrii
activitii umane. Nu exist astzi domeniu de activitate n care calculatorul s nu i arate utilitatea[18].
Calculatoarele pot fi folosite pentru a rezolva probleme, numai dac pentru rezolvarea acestora se
concep programe corespunztoare de rezolvare. Termenul de program (programare) a suferit schimbri n
scurta istorie a informaticii. Prin anii '60 problemele rezolvate cu ajutorul calculatorului erau simple i se
gseau algoritmi nu prea complicai pentru rezolvarea lor. Prin program se nelegea rezultatul scrierii
unui algoritm ntr-un limbaj de programare. Din cauza creterii complexitii problemelor, astzi pentru
rezolvarea unei probleme adesea vom concepe un sistem de mai multe programe.
Dar ce este un algoritm? O definiie matematic, riguroas, este greu de dat, chiar imposibil fr
a introduce i alte noiuni. Vom ncerca n continuare o descriere a ceea ce se nelege prin algoritm.
Ne vom familiariza cu aceast noiune prezentnd mai multe exemple de algoritmi i observnd ce
au ei n comun. Cel mai vechi exemplu este algoritmul lui Euclid, algoritm care determin cel mai mare
divizor comun a dou numere naturale. Evident, vom prezenta mai muli algoritmi, cei mai muli fiind
legai de probleme accesibile absolvenilor de liceu.
Vom constata c un algoritm este un text finit, o secven finit de propoziii ale unui limbaj. Din
cauz c este inventat special n acest scop, un astfel de limbaj este numit limbaj de descriere a
algoritmilor. Fiecare propoziie a limbajului precizeaz o anumit regul de calcul, aa cum se va
observa atunci cnd vom prezenta limbajul Pseudocod.
Oprindu-ne la semnificaia algoritmului, la efectul execuiei lui, vom observa c fiecare algoritm
definete o funcie matematic. De asemenea, din toate seciunile urmtoare va reiei foarte clar c un
algoritm este scris pentru rezolvarea unei probleme. Din mai multe exemple se va observa ns c, pentru
rezolvarea aceleai probleme, exist mai muli algoritmi.
Pentru fiecare problem P exist date presupuse cunoscute (date iniiale pentru algoritmul
corespunztor, A) i rezultate care se cer a fi gsite (date finale). Evident, problema s-ar putea s nu aib
sens pentru orice date iniiale. Vom spune c datele pentru care problema P are sens fac parte din
domeniul D al algoritmului A. Rezultatele obinute fac parte dintr-un domeniu R, astfel c executnd
algoritmul A cu datele de intrare xD vom obine rezultatele rR. Vom spune c A(x)=r i astfel
algoritmul A definete o funcie
A : D ---> R .
Algoritmii au urmtoarele caracteristici: generalitate, finitudine i unicitate.
Prin generalitate se nelege faptul c un algoritm este aplicabil pentru orice date iniiale xD.
Deci un algoritm A nu rezolv problema P cu nite date de intrare, ci o rezolv n general, oricare ar fi
aceste date. Astfel, algoritmul de rezolvare a unui sistem liniar de n ecuaii cu n necunoscute prin metoda
lui Gauss, rezolv orice sistem liniar i nu un singur sistem concret.
Prin finitudine se nelege c textul algoritmului este finit, compus dintr-un numr finit de
propoziii. Mai mult, numrul transformrilor ce trebuie aplicate unei informaii admisibile xD pentru a
obine rezultatul final corespunztor este finit.
Prin unicitate se nelege c toate transformrile prin care trece informaia iniial pentru a obine
rezultatul rR sunt bine determinate de regulile algoritmului. Aceasta nseamn c fiecare pas din
execuia algoritmului d rezultate bine determinate i precizeaz n mod unic pasul urmtor. Altfel spus,
ori de cte ori am executa algoritmul, pornind de la aceeai informaie admisibil xD, transformrile
prin care se trece i rezultatele obinute sunt aceleai.
n descrierea algoritmilor se folosesc mai multe limbaje de descriere, dintre care cele mai des
folosite sunt:
- limbajul schemelor logice;
6
- limbajul Pseudocod.
n continuare vom folosi pentru descrierea algoritmilor limbajul Pseudocod care va fi definit n
cele ce urmeaz. n ultima vreme schemele logice sunt tot mai puin folosite n descrierea algoritmilor i
nu sunt deloc potrivite n cazul problemelor complexe. Prezentm ns i schemele logice, care se mai
folosesc n manualele de liceu, ntruct cu ajutorul lor vom preciza n continuare semantica propoziiilor
Pseudocod.
1.2 Scheme logice
Schema logic este un mijloc de descriere a algoritmilor prin reprezentare grafic. Regulile de
calcul ale algoritmului sunt descrise prin blocuri (figuri geometrice) reprezentnd operaiile (paii)
algoritmului, iar ordinea lor de aplicare (succesiunea operaiilor) este indicat prin sgei. Fiecrui tip de
operaie i este consacrat o figur geometric (un bloc tip) n interiorul creia se va nscrie operaia din
pasul respectiv.
Prin execuia unui algoritm descris printr-o schem logic se nelege efectuarea tuturor
operaiilor precizate prin blocurile schemei logice, n ordinea indicat de sgei.
n descrierea unui algoritm, deci i ntr-o schem logic, intervin variabile care marcheaz att
datele cunoscute iniial, ct i rezultatele dorite, precum i alte rezultate intermediare necesare n
rezolvarea problemei. ntruct variabila joac un rol central n programare este bine s definim acest
concept. Variabila definete o mrime care i poate schimba valoarea n timp. Ea are un nume i,
eventual, o valoare. Este posibil ca variabila nc s nu fi primit valoare, situaie n care vom spune c ea
este neiniializat. Valorile pe care le poate lua variabila aparin unei mulimi D pe care o vom numi
domeniul variabilei. n concluzie vom nelege prin variabil tripletul
(nume, domeniul D, valoare)
unde valoare aparine mulimii D {nedefinit}.
Blocurile delimitatoare Start i Stop (Fig.1.2.1. a i 1.2.1. b) vor marca nceputul respectiv
sfritul unui algoritm dat printr-o schem logic. Descrierea unui algoritm prin schem logic va ncepe
cu un singur bloc Start i se va termina cu cel puin un bloc Stop.
Blocurile de intrare/ieire Citete i Tiprete (Fig. 1.2.1. c i d) indic introducerea unor Date
de intrare respectiv extragerea unor Rezultate finale. Ele permit precizarea datelor iniiale cunoscute n
problem i tiprirea rezultatelor cerute de problem. Blocul Citete iniializeaz variabilele din lista de
intrare cu valori corespunztoare, iar blocul Tiprete va preciza rezultatele obinute (la execuia pe
calculator cere afiarea pe ecran a valorilor expresiilor din lista de ieire).
Blocurile de atribuire (calcul) se utilizeaz n descrierea operaiilor de atribuire (:=). Printr-o
astfel de operaie, unei variabile var i se atribuie valoarea calculat a unei expresii expr (Fig.1.2.1. e).
Blocurile de decizie marcheaz punctele de ramificaie ale algoritmului n etapa de decizie. Ramificarea
poate fi dubl (blocul logic, Fig.1.2.1.f) sau tripl (blocul aritmetic, Fig. 1.2.1.g). Blocul de decizie logic
indic ramura pe care se va continua execuia algoritmului n funcie de ndeplinirea (ramura Da) sau
nendeplinirea (ramura Nu) unei condiii. Condiia care se va nscrie n blocul de decizie logic va fi o
expresie logic a crei valoare poate fi una dintre valorile "adevrat" sau "fals". Blocul de decizie
aritmetic va hotr ramura de continuare a algoritmului n funcie de semnul valorii expresiei aritmetice
nscrise n acest bloc, care poate fi negativ, nul sau pozitiv.
Blocurile de conectare marcheaz ntreruperile sgeilor de legtur dintre blocuri, dac din
diverse motive s-au efectuat astfel de ntreruperi (Fig.1.2.1.h).
Pentru exemplificare vom da n continuare dou scheme logice, corespunztoare unor algoritmi
pentru rezolvarea problemelor P1.2.1 i P1.2.2.
P1.2.1. S se rezolve ecuaia de grad doi aX2+bX+c=0 (a,b,cR _i a0).
Metoda de rezolvare a ecuaiei de gradul doi este cunoscut. Ecuaia poate avea rdcini reale, respectiv
complexe, situaie recunoscut dup semnul discriminantului d = b2 - 4ac.
Algoritmul de rezolvare a problemei va citi mai nti datele problemei, marcate prin variabilele a,
b i c. Va calcula apoi discriminantul d i va continua n funcie de valoarea lui d, aa cum se poate vedea
n fig.1.2.2.
P1.2.2. S se calculeze suma elementelor pozitive ale unui ir de numere reale dat.
Schema logic (dat n Fig.1.2.3) va conine imediat dup blocul START un bloc de citire, care
precizeaz datele cunoscute n problem, apoi o parte care calculeaz suma cerut i un bloc de tiprire a
sumei gsite, naintea blocului STOP. Partea care calculeaz suma S cerut are un bloc pentru iniializarea
8
cu 0 a acestei sume, apoi blocuri pentru parcurgerea numerelor: x 1, x2xn i adunarea celor pozitive la
suma S. Pentru aceast parcurgere se folosete o variabil contor i, care este iniializat cu 1 i crete
mereu cu 1 pentru a atinge valoarea n, indicele ultimului numr dat.
Schemele logice dau o reprezentare grafic a algoritmilor cu ajutorul unor blocuri de calcul.
Execuia urmeaz sensul indicat de sgeat, putnd avea loc reveniri n orice punct din schema logic.
Din acest motiv se poate obine o schem logic nclcit, greu de urmrit. Rezult importana compunerii
unor scheme logice structurate (D-scheme, dup Djikstra), care s conin numai anumite structuri
standard de calcul i n care drumurile de la START la STOP s fie uor de urmrit.
1.3. Limbajul PSEUDOCOD
Limbajul Pseudocod este un limbaj inventat n scopul proiectrii algoritmilor i este format din
propoziii asemntoare propoziiilor limbii romne, care corespund structurilor de calcul folosite n
construirea algoritmilor. Acesta va fi limbajul folosit de noi n proiectarea algoritmilor i va fi definit n
cele ce urmeaz. innd seama c obinerea unui algoritm pentru rezolvarea unei probleme nu este
ntotdeauna o sarcin simpl, c n acest scop sunt folosite anumite metode pe care le vom descrie n
capitolele urmtoare, n etapele intermediare din obinerea algoritmului vom folosi propoziii curente din
limba romn. Acestea sunt considerate elemente nefinisate din algoritm, asupra crora trebuie s se
revin i le vom numi propoziii nestandard. Deci limbajul Pseudocod are dou tipuri de propoziii:
propoziii standard, care vor fi prezentate fiecare cu sintaxa i semnificaia (semantica) ei i propoziii
nestandard. Aa cum se va arta mai trziu, propoziiile nestandard sunt texte care descriu pri ale
algoritmului nc incomplet elaborate, nefinisate, asupra crora urmeaz s se revin.
Pe lng aceste propoziii standard i nestandard, n textul algoritmului vom mai introduce
propoziii explicative, numite comentarii. Pentru a le distinge de celelalte propoziii, comentariile vor fi
nchise ntre acolade. Rolul lor va fi explicat puin mai trziu.
Propoziiile standard ale limbajului Pseudocod folosite n aceast lucrare, corespund structurilor
de calcul prezentate n figura 1.3.1 i vor fi prezentate n continuare. Fiecare propoziie standard ncepe cu
un cuvnt cheie, aa cum se va vedea n cele ce urmeaz. Pentru a deosebi aceste cuvinte de celelalte
denumiri, construite de programator, n acest capitol vom scrie cuvintele cheie cu litere mari. Menionm
c i propoziiile simple se termin cu caracterul ';' n timp ce propoziiile compuse, deci cele n interiorul
crora se afl alte propoziii, au un marcaj de sfrit propriu. De asemenea, menionm c propoziiile
limbajului Pseudocod vor fi luate n seam n ordinea ntlnirii lor n text, asemenea oricrui text al limbii
romne.
Prin execuia unui algoritm descris n Pseudocod se nelege efectuarea operaiilor precizate de
propoziiile algoritmului, n ordinea citirii lor.
n figura 1.3.1, prin A, B s-au notat subscheme logice, adic secvene de oricte structuri
construite conform celor trei reguli menionate n continuare.
Structura secvenial (fig.1.3.1.a) este redat prin concatenarea propoziiilor, simple sau
compuse, ale limbajului Pseudocod, care vor fi executate n ordinea ntlnirii lor n text.
a) structura
secvenial
b) structura
c) structura
alternativ
repetitiv
Figura 1.3.1. Structurile elementare de calcul
9
Propoziiile simple din limbajul Pseudocod sunt CITETE, TIPARETE, FIE i apelul de
subprogram. Propoziiile compuse corespund structurilor alternative i repetitive.
Structura alternativ (fig.1.3.1.b) este redat n Pseudocod prin propoziia DAC, prezentat n
seciunea 1.3.2, iar structura repetitiv din fig.1.3.1.c este redat n Pseudocod prin propoziia CT
TIMP, prezentat n seciunea 1.3.3.
Bohm i Jacopini au demonstrat c orice algoritm poate fi descris folosind numai aceste trei
structuri de calcul.
Propoziiile DATE i REZULTATE sunt folosite n faza de specificare a problemelor, adic
enunarea riguroas a acestora. Propoziia DATE se folosete pentru precizarea datelor iniiale, deci a
datelor considerate cunoscute n problem (numite i date de intrare) i are sintaxa:
DATE list;
unde list conine toate numele variabilelor a cror valoare iniial este cunoscut. n general, prin list se
nelege o succesiune de elemente de acelai fel desprite prin virgul. Deci n propoziia DATE, n
dreapta acestui cuvnt se vor scrie acele variabile care marcheaz mrimile cunoscute n problem.
Pentru precizarea rezultatelor dorite se folosete propoziia standard
REZULTATE list;
n construcia "list" ce urmeaz dup cuvntul REZULTATE fiind trecute numele variabilelor care
marcheaz (conin) rezultatele cerute n problem.
Acum putem preciza mai exact ce nelegem prin cunoaterea complet a problemei de rezolvat.
Evident, o problem este cunoscut atunci cnd se tie care sunt datele cunoscute n problem i ce
rezultate trebuiesc obinute. Deci pentru cunoaterea unei probleme este necesar precizarea variabilelor
care marcheaz date considerate cunoscute n problem, care va fi reflectat printr-o propoziie DATE i
cunoaterea exact a cerinelor problemei, care se va reflecta prin propoziii REZULTATE. Variabilele
prezente n aceste propoziii au anumite semnificaii, presupuse cunoscute. Cunoaterea acestora, scrierea
lor explicit, formeaz ceea ce vom numi n continuare specificarea problemei. Specificarea unei
probleme este o activitate foarte important dar nu i simpl.
De exemplu, pentru rezolvarea ecuaiei de gradul al doilea, specificarea problemei, scris de un
nceptor, poate fi:
DATE a,b,c;
{ Coeficienii ecuaiei }
REZULTATE x1,x2;
{ Rdcinile ecuaiei }
Aceast specificaie este ns incomplet dac ecuaia nu are rdcini reale. n cazul n care rdcinile
sunt complexe putem nota prin x1, x2 partea real respectiv partea imaginar a rdcinilor. Sau pur i
simplu, nu ne intereseaz valoarea rdcinilor n acest caz, ci doar faptul c ecuaia nu are rdcini reale.
Cu alte cuvinte avem nevoie de un mesaj care s ne indice aceast situaie (vezi schema logic 1.2.2), sau
de un indicator, fie el ind. Acest indicator va lua valoarea 1 dac rdcinile sunt reale i valoarea 0 n caz
contrar. Deci specificaia corect a problemei va fi
DATE a,b,c;
{ Coeficienii ecuaiei }
REZULTATE ind,
{Un indicator: 1=rdcini reale, 0=complexe}
x1,x2;
{ Rdcinile ecuaiei, n cazul ind=1,}
{respectiv partea real i cea }
{imaginar n cazul ind=0}
Evident c specificarea problemei este o etap important pentru gsirea unei metode de rezolvare
i apoi n proiectarea algoritmului corespunztor. Nu se poate rezolva o problem dac aceasta nu este
bine cunoscut, adic nu avem scris specificarea problemei. Cunoate complet problema este prima
regul ce trebuie respectat pentru a obine ct mai repede un algoritm corect pentru rezolvarea ei.
1.3.1 Algoritmi liniari
Propoziiile CITETE i TIPRETE sunt folosite pentru iniializarea variabilelor de intrare cu
datele cunoscute n problem, respectiv pentru tiprirea (aflarea) rezultatelor obinute. n etapa de
10
11
{ Algoritmul 2: Rezolvarea }
{ ecuaiei de gradul doi }
{ a,b,c = Coeficienii ecuaiei }
CITETE a,b,c;
FIE delta:=b*b- 4*a*c;
DAC delta<0 ATUNCI ind:=0;
r:=radical din (- delta);
x1:=- b/(a+a);
x2:=r/(a+a);
ALTFEL ind:=1;
r:=radical din delta;
x1:=(-b- r)/(a+a);
x2:=(-b+r)/(a+a);
SFDAC
TIPRETE ind, x1,x2;
SFALGORITM
{ rdcini complexe }
{ rdcini reale }
12
S observm, de asemenea, c prima execuie a grupului A este obligatorie, abia dup modificarea
contorului verificndu-se condiia de continuare a execuiei lui A.
Ca exemplu, s descriem un algoritm care gsete minimul i maximul componentelor unui vector
de numere reale. Vom nota prin X acest vector, deci X = (x1, x2, ... , xn) .
Specificaia problemei este urmtoarea:
DATE n,(xi ,i=1,n);
REZULTATE valmin,valmax;
iar semnificaia acestor variabile se nelege din cele scrise mai sus. Pentru rezolvarea problemei vom
examina pe rnd cele n componente. Pentru a parcurge cele n componente avem nevoie de un contor care
s precizeze poziia la care am ajuns. Fie i acest contor. Uor se ajunge la urmtorul algoritm:
ALGORITMUL MAXMIN ESTE
{ Algoritmul 5: Calculul }
{ valorii minime i maxime }
CITETE n,(xi,i=1,n);
FIE valmin:=x1; valmax:=x1;
PENTRU i:=2,n EXECUT
DAC xi<valmin ATUNCI valmin:=xi SFDAC
DAC xi>valmax ATUNCI valmax:=xi SFDAC
SFPENTRU
TIPRETE valmin,valmax;
SFALGORITM
Un rol important n claritatea textului unui algoritm l au denumirile alese pentru variabile. Ele
trebuie s reflecte semnificaia variabilelor respective. Deci alege denumiri sugestive pentru variabile,
care s reflecte semnificaia lor.
n exemplul de mai sus denumirile valmin i valmax spun cititorului ce s-a notat prin aceste
variabile.
1.4 Calculul efectuat de un algoritm
Fie X1, X2, ..., Xn, variabilele ce apar n algoritmul A. n orice moment al execuiei algoritmului,
fiecare variabil are o anumit valoare, sau este nc neiniializat.
Vom numi stare a algoritmului A cu variabilele menionate vectorul
s = ( s1,s2,...,sn )
format din valorile curente ale celor n variabile ale algoritmului.
Este posibil ca variabila Xj s fie nc neiniializat, deci s nu aib valoare curent, caz n care sj
este nedefinit, lucru notat n continuare prin semnul ntrebrii '?'.
Prin executarea unei anumite instruciuni unele variabile i schimb valoarea, deci algoritmul i
schimb starea.
Se numete calcul efectuat de algoritmul A o secven de stri
s0, s1, s2, ..., sm
unde s0 este starea iniial cu toate variabilele neiniializate, iar sm este starea n care se ajunge dup
execuia ultimei propoziii din algoritm.
1.5 Rafinare n pai succesivi
Adeseori algoritmul de rezolvare a unei probleme este rezultatul unui proces complex, n care se
iau mai multe decizii i se precizeaz tot ceea ce iniial era neclar. Observaia este adevrat mai ales n
cazul problemelor complicate, dar i pentru probleme mai simple n procesul de nvmnt. Este vorba
de un proces de detaliere pas cu pas a specificaiei problemei, proces denumit i proiectare descendent,
sau rafinare n pai succesivi. Algoritmul apare n mai multe versiuni succesive, fiecare versiune fiind o
detaliere a versiunii precedente. n versiunile iniiale apar propoziii nestandard, clare pentru cititor, dar
14
neprecizate prin propoziii standard. Urmeaz ca n versiunile urmtoare s se revin asupra lor.
Algoritmul apare astfel n versiuni succesive, tot mai complet de la o versiune la alta.
Apare aici o alt regul important n proiectarea algoritmului: amn pe mai trziu detaliile
nesemnificative; concentreaz-i atenia la deciziile importante ale momentului.
15
CAPITOLUL II
SUBPROGRAME
Conceptul de SUBPROGRAM
Orice problem poate apare ca o subproblem S a unei probleme mai complexe C. Algoritmul de
rezolvare a problemei S devine n acest caz un SUBPROGRAM pentru algoritmul de rezolvare a
problemei C.
Pentru a defini un SUBPROGRAM vom folosi propoziia standard
SUBPROGRAMUL nume (lpf) ESTE:
unde nume este numele SUBPROGRAMului definit, iar lpf este lista parametrilor formali. Acetia sunt
formai din variabilele care marcheaz datele de intrare (cele presupuse cunoscute) i variabilele care
marcheaz datele de ieire (rezultatele obinute de SUBPROGRAM).
Aceast propoziie este urmat de textul efectiv al SUBPROGRAMului, text care precizeaz
calculele necesare rezolvrii subproblemei corespunztoare. Descrierea se va ncheia cu cuvntul
SFSUBPROGRAM sau SF-nume.
Dm ca exemplu un SUBPROGRAM cu numele MAXIM, care gsete maximul dintre
componentele vectorului X = (x1,x2, ..., xn).
Datele cunoscute pentru acest SUBPROGRAM sunt vectorul X i numrul n al componentelor
vectorului X. Ca rezultat vom obine maximul cerut, pe care-l vom nota cu max. Deci lista parametrilor
formali conine trei variabile, n, X i max. SUBPROGRAMul este dat n continuare.
SUBPROGRAMUL maxim(n,X,max) ESTE:
FIE max:=x1;
PENTRU i:=2;n EXECUT
DAC xi>max ATUNCI max:=xi SFDAC
SFPENTRU
SF-maxim
n cadrul multor algoritmi este necesar calculul valorilor unei funcii n diferite puncte. Este
necesar s definim funcia printr-un SUBPROGRAM de tip funcie.
Pentru definirea unui SUBPROGRAM de tip funcie se folosete un antet care precizeaz
numele funciei i variabilele de care depinde ea. SUBPROGRAMul are forma:
FUNCIA nume(lpf) ESTE:
{Antetul funciei}
text
{corpul funciei}
SF-nume
{marca de sfrit}
n corpul funciei trebuie s existe cel puin o atribuire n care numele funciei apare n partea
stng, deci prin care funcia primete o valoare.
Dm ca exemplu o funcie numar : R --> {2,3,4,5}, definit matematic astfel:
n Pseudocod descrierea este urmtoarea:
FUNCIA numar(x) ESTE:
DAC x<0.2 ATUNCI numar:=2 ALTFEL
DAC x<0.5 ATUNCI numar:=3 ALTFEL
DAC x<0.9 ATUNCI numar:=4
ALTFEL numar:=5
SFDAC
SFDAC
SFDAC
SF-numar
16
Am vzut c definiia unei funcii const dintr-un antet i dintr-un bloc care va defini aciunile
prin care se calculeaz valoarea funciei. n antet se precizeaz numele funciei i lista parametrilor
formali.
n concluzie, exist dou categorii de SUBPROGRAMi: de tip funcie i SUBPROGRAMi
propriu-zii, crora li se mai spune i proceduri. Importana lor va fi subliniat prin toate exemplele care
urmeaz n acest curs. n ncheiere menionm c subprogramele de tip funcie se folosesc n scopul
definirii funciilor, aa cum sunt cunoscute ele din matematic, n timp ce SUBPROGRAMii de tip
procedur se refer la rezolvarea unor probleme ce apar ca subprobleme, fiind algoritmi de sine stttori.
Apelul unui SUBPROGRAM
Am vzut c un SUBPROGRAM este dedicat rezolvrii unei subprobleme S a unei probleme mai
complexe C. Algoritmul corespunztor problemei C va folosi toate operaiile necesare rezolvrii
problemei S, deci va folosi ca parte ntregul SUBPROGRAM conceput pentru rezolvarea subproblemei S.
Spunem c el va apela acest SUBPROGRAM.
n Pseudocod apelul unei funcii se face scriind ntr-o expresie numele funciei urmat de lista
parametrilor actuali. Trebuie s existe o coresponden biunivoc ntre parametrii actuali i cei formali
folosii n definiia funciei. Dei denumirile variabilelor din cele dou liste pot s difere, rolul variabilelor
care se corespund este acelai. Mai exact, parametrul formal i parametrul actual corespunztor trebuie s
se refere la aceeai entitate, trebuie s aib aceeai semnificaie, s reprezinte aceeai structur de date.
Putem considera c n timpul execuiei algoritmului cei doi parametri devin identici.
Folosirea unui SUBPROGRAM n cadrul unui algoritm se face apelnd acest SUBPROGRAM
prin propoziia standard CHEAM nume (lpa);
unde nume este numele SUBPROGRAMului apelat iar lpa este lista parametrilor actuali. Aceast list
conine toate datele de intrare (cele cunoscute n subproblema corespunztoare) i toate rezultatele
obinute n SUBPROGRAM. i n acest caz ntre lista parametrilor formali din definiia
SUBPROGRAMului i lista parametrilor actuali din propoziia de apel trebuie s existe o coresponden
biunivoc, ca i n cazul funciilor. Ca o prim verificare a respectrii acestei corespondene, subliniem c
numrul parametrilor actuali trebuie s coincid cu numrul parametrilor formali.
Ca exemplu de apelare a funciilor, dm n continuare un program pentru a calcula a cta zi din
anul curent este ziua curent (zi,luna,an). El folosete un subprogram de tip funcie pentru a obine
numrul zilelor lunii cu numrul de ordine i i un altul pentru a verifica dac un an este bisect sau nu.
Aceste dou funcii sunt:
- NRZILE(i) furnizeaz numrul zilelor existente n luna i a unui an nebisect;
- BISECT(an) adevrat dac anul dintre paranteze este bisect.
Algoritmul este urmtorul:
ALGORITMUL NUMRZILE ESTE:
CITETE zi, luna, an;
FIE nr:=zi;
DAC luna>1 ATUNCI
PENTRU i:=1, Luna-1 EXECUT nr:=nr+NRZILE(i) SFPENTRU
SFDAC
DAC luna>2 ATUNCI
DAC BISECT(an) ATUNCI nr:=nr+1 SFDAC
SFDAC
TIPRETE nr;
SFALGORITM
S observm c n proiectarea acestui algoritm nu este necesar s cunoatem textul
SUBPROGRAMilor folosii, ci doar specificarea acestor SUBPROGRAMi, numele lor i lista
parametrilor formali. La acest nivel accentul trebuie s cad pe proiectarea algoritmului care apeleaz,
17
CITMUL(m,A);
REUNIUNE(m,A,n,B,k,R);
TIPMUL(m,A);
ORDON(m,A);
care sunt specificai mai jos (la locul definirii lor) prin comentarii, algoritmul de rezolvare a problemei de
mai sus este dat n continuare. ntruct operaiile respective se folosesc de mai multe ori (de 3 ori), am
definit un SUBPROGRAM TIPORDON(m,A) care ordoneaz mai nti elementele mulimii A i apoi le
tiprete.
ALGORITMUL OPER-MULTIMI ESTE:
{ A6: SUBPROGRAMi }
CHEAM CITMUL(m,A);
CHEAM CITMUL(n,B);
CHEAM CITMUL(p,C);
CHEAM TIPORDON(m,A);
CHEAM TIPORDON(n,B);
CHEAM TIPORDON(p,C);
CHEAM REUNIUNE(m,A,n,B,k,R);
CHEAM TIPORDON(k,R);
CHEAM REUNIUNE(n,B,p,C,k,R);
CHEAM TIPORDON(k,R);
CHEAM REUNIUNE(p,C,m,A,k,R);
CHEAM TIPORDON(k,R);
SFALGORITM
SUBPROGRAMii apelai mai sus sunt definii n continuare.
SUBPROGRAMUL CITMUL(n,M) ESTE:
{Citete n i M}
CITETE n;
{n=nr. elementelor mulimii}
CITETE (mi,i=1,n);
{M=mulimea cu elementele m1,m2,...,mn}
SF-CITMUL
SUBPROGRAMUL ORDON(n,M) ESTE:
REPET
FIE ind:=0;
PENTRU i:=1;n- 1 EXECUT
DAC mi>mi+1 ATUNCI
FIE t := mi;
mi:=mi+1; mi+1:=t;
ind:=1;
SFDAC
SFPENTRU
PNCND ind=0 SFREP
SF-ORDON
{ Ordoneaz i tiprete }
{ elementele mulimii M }
Dndu- se ordinea o1,o2, ..., on, a elevilor clasei, numele i mediile acestora, s se tipreasc
numele i mediile primilor k elevi n ordinea specificat.
SUBPROGRAMul TIPAR, dat n continuare, rezolv aceast problem.
SUBPROGRAMUL TIPAR(k, NUME, O) ESTE:
PENTRU i:=1;k EXECUT
Tiprete datele elevului de rang oi.
SFPENTRU
SF-TIPAR
Variabilele folosite pentru problema dat sunt urmtoarele:
- n reprezint numrul elevilor clasei;
- m este numrul disciplinelor la care elevii primesc note;
- NUME este vectorul care reine numele elevilor: NUMEi este numele elevului cu numrul
de ordine i;
- NOTE este matricea notelor elevilor, avnd n linii i m coloane;
NOTEi,j este nota elevului cu numele NUMEi la disciplina cu numrul de ordine j;
NOTE.j este coloana a j-a a matricei NOTE i reprezint notele elevilor la disciplina
j;
- MEDII este vectorul mediilor generale.
Algoritmul se d n continuare:
ALGORITMUL CLASAMENT ESTE:
{ Algoritmul 7: Ordonare}
CITETE m,
{numrul disciplinelor i}
n,
{al elevilor}
NUMEi, i=1,n,
{numele elevilor}
NOTEi,j, j=1,m, i=1,n;
{notele elevilor}
PENTRU i:=1;n EXECUT
{ calculeaz media general}
FIE S:=0;
{a elevului i}
PENTRU j:=1;m EXECUT S:=S+NOTEi,j SFPENTRU
FIE MEDIIi:=S/m
SFPENTRU
CHEAM ORDINE(n,MEDII,O);
CHEAM TIPAR(n,NUME,O);
PENTRU j:=1;m EXECUT
CHEAM ORDINE(n,NOTE.j,O);
CHEAM TIPAR(6,NUME,O);
SFPENTRU
SF-ALGORITM
Apel recursiv
n exemplele date se observ c apelul unui subprogram se face dup ce el a fost definit. Este ns
posibil ca un SUBPROGRAM s se apeleze pe el nsui. ntr-un astfel de caz spunem c apelul este
recursiv, iar SUBPROGRAMul respectiv este definit recursiv.
Ca exemplu, definim n continuare o funcie care calculeaz recursiv valoarea n!. Se va folosi
formula n! = n.(n- 1)! n cazul n>0 i faptul c 0!=1. Recursivitatea const n faptul c n definiia funciei
Factorial de n se folosete aceeai funcie Factorial dar de argument n-1. Deci funcia Factorial se
apeleaz pe ea nsi. Este important ca numrul apelurilor s fie finit, deci ca procedeul de calcul descris
s se termine.
FUNCTIA Factorial(n) ESTE:
DAC n=0 ATUNCI Factorial:=1
ALTFEL Factorial:= n*Factorial(n- 1)
21
SFDAC
SF-Factorial;
Tot ca exemplu de apel recursiv putem descrie o funcie ce calculeaz maximul a n numere
x1,x2,...,xn. Ea se bazeaz pe funcia MAXIM2 care calculeaz maximul a dou numere, descris n
continuare.
FUNCIA MAXIM2(a,b) ESTE:
DAC a<b ATUNCI MAXIM2:=b
ALTFEL MAXIM2:=a
SFDAC
SF-MAXIM2
Funcia MAXIM, care calculeaz maximul celor n numere este urmtoarea:
FUNCIA MAXIM(n,X) ESTE:
{Calculeaz maximul a n numere}
{X=vectorul cu numerele date}
DAC n=1
ATUNCI MAXIM:=x1
ALTFEL MAXIM:=MAXIM2( MAXIM(n-1,X), xn)
SFDAC
SF-MAXIM
22
CAPITOLUL III
METODE DE PROIECTARE A ALGORITMILOR
3.1 Elaborarea algoritmilor
Prin elaborarea (proiectarea) unui algoritm nelegem ntreaga activitate depus de la enunarea
problemei pn la realizarea algoritmului corespunztor rezolvrii acestei probleme.
n elaborarea unui algoritm deosebim urmtoarele activiti importante:
specificarea problemei;
descrierea metodei alese pentru rezolvarea problemei;
proiectarea propriu-zis. Ea const n descompunerea problemei n subprobleme,
obinerea algoritmului principal i a tuturor SUBPROGRAMilor apelai, conform
metodelor prezentate n seciunile urmtoare. Ea se termin cu descrierea algoritmului
principal i a SUBPROGRAMilor menionai, dar i cu precizarea denumirilor i
semnificaiilor variabilelor folosite;
verificarea algoritmului obinut.
3.2 Proiectarea ascendent i proiectarea descendent
Exist dou metode generale de proiectare a algoritmilor, a cror denumire provine din modul de
abordare a rezolvrii problemelor: metoda descendent i metoda ascendent. Proiectarea descendent
(top-down) pornete de la problema de rezolvat, pe care o descompune n pri rezolvabile separat. De
obicei aceste pri sunt subprobleme independente, care la rndul lor pot fi descompuse n subprobleme.
La prima descompunere accentul trebuie pus pe algoritmul (modulul) principal nu asupra subproblemelor.
La acest nivel nu ne intereseaz amnunte legate de rezolvarea subproblemelor, presupunem c le tim
rezolva, eventual c avem deja scrii SUBPROGRAMi pentru rezolvarea lor. Urmeaz s considerm pe
rnd fiecare subproblem n parte i s proiectm (n acelai mod) un SUBPROGRAM pentru rezolvarea
ei. n final, se va descrie SUBPROGRAMul de rezolvare al fiecrei subprobleme, dar i interaciunile
dintre aceti SUBPROGRAMi i ordinea n care ei sunt folosii.
Noiunea de modul va fi definit n seciunea urmtoare. Deocamdat nelegem prin modul orice
SUBPROGRAM sau algoritmul principal. Legtura dintre module se prezint cel mai bine sub forma unei
diagrame numit arbore de programare. Fiecrui modul i corespunde n arborele de programare un nod,
ai crui descendeni sunt toate modulele apelate direct. Nodul corespunztor algoritmului principal este
chiar nodul rdcin.
n multe cri metoda top-down este ntlnit i sub denumirea stepwise-refinement, adic
rafinare n pai succesivi. Este vorba de un proces de detaliere pas cu pas a specificaiei, denumit
proiectare descendent. Algoritmul apare n diferite versiuni succesive, fiecare fiind o detaliere a versiunii
precedente.
Scopul urmrit este acelai: concentrarea ateniei asupra prilor importante ale momentului i amnarea
detaliilor pentru mai trziu. Dac ar fi necesar s le deosebim am spune c metoda top-down se refer la
nivelul macro iar metoda rafinrii succesive la nivel micro. La nivel macro se dorete descompunerea
unei probleme complexe n subprobleme. La nivel micro se dorete obinerea unui modul n versiune
final. ntr-o versiune intermediar pot fi prezente numai prile importante ale acestuia, urmnd s se
revin asupra detaliilor n versiunile urmtoare (aa cum s-a artat n seciunea 1.5), dup ce aspectele
importante au fost rezolvate.
Avantajele proiectrii top-down (cunoscut i sub denumirea "Divide et impera") sunt multiple.
Avantajul principal const n faptul c ea permite programatorului s reduc complexitatea problemei,
subproblemele n care a fost descompus fiind mai simple, i s amne detaliile pentru mai trziu. n
momentul n care descompunem problema n subprobleme nu ne gndim cum se vor rezolva
subproblemele ci care sunt ele i conexiunile dintre ele.
23
Proiectarea descendent permite lucrul n echipe mari. Prin descompunerea problemei n mai
multe subprobleme, fiecare subproblem poate fi dat spre rezolvare unei subechipe. Fiecare subechip
nu cunoate dect subproblema pe care trebuie s o rezolve.
Metoda "Divide et Impera" poate fi folosit nu numai la mprirea problemei n subprobleme ci
i la mprirea datelor n grupe mai mici de date. Un astfel de procedeu este folosit de SUBPROGRAMul
Quicksort.
Metoda ascendent (bottom-up) pornete de la propoziiile limbajului i de la SUBPROGRAMi
existeni, pe care i asambleaz n ali SUBPROGRAMi pentru a ajunge n final la algoritmul dorit. Cu
alte cuvinte, n cazul metodei ascendente va fi scris mai nti SUBPROGRAMul apelat i apoi cel care
apeleaz. Ca rezultat al proiectrii ascendente se ajunge la o mulime de SUBPROGRAMi care se
apeleaz ntre ei. Este important s se cunoasc care SUBPROGRAM apeleaz pe care, lucru redat printro diagram de structur, ca i n cazul programrii descendente.
Aceast metod are marele dezavantaj c erorile de integrare vor fi detectate trziu, abia n faza de
integrare. Se poate ajunge abia acum la concluzia c unii SUBPROGRAMi, dei coreci, nu sunt utili.
De cele mai multe ori nu se practic o proiectare ascendent sau descendent pur ci o combinare
a lor, o proiectare mixt.
3.3 Proiectarea modular
Prin proiectare (programare) modular nelegem metoda de proiectare (programare) a unui
algoritm pentru rezolvarea unei probleme prin folosirea modulelor.
Dar ce este un modul? Modulul este considerat o unitate structural de sine stttoare, fie
program, fie subprogram, fie o unitate de program. Un modul poate conine sau poate fi coninut ntr-alt
modul. Un modul poate fi format din mai multe submodule. Astfel, n Pseudocod fiecare
SUBPROGRAM i algoritmul principal sunt considerate module. n limbajele de programare cu structur
de bloc UNIT-urile pot fi considerate module. La compilarea separat un grup de subprograme compilate
deodat constituie un modul, dar acest modul poate fi considerat ca o mulime de submodule din care este
compus.
Este ns important ca fiecare modul s-i aib rolul su bine precizat, s realizeze o funcie n
cadrul ntregului program. El apare n mod natural n descompunerea top-down.
Indiferent c privim modulul ca un singur SUBPROGRAM, un grup de SUBPROGRAMi, sau un
algoritm de sine stttor ce apeleaz ali SUBPROGRAMi, considerm modulele relativ independente,
dar cu posibiliti de comunicare ntre ele. Astfel, un modul nu trebuie s fie influenat de maniera n care
se lucreaz n interiorul altui modul. Orice modificare ulterioar n structura unui program, dac funcia
pe care o realizeaz un modul M nc este necesar, acest modul trebuie s fie util i folosit n continuare
fr modificri.
Rezult c programarea modular se bazeaz pe descompunerea problemei n subprobleme i
proiectarea i programarea separat a SUBPROGRAMilor corespunztori. De altfel, considerm c ntr-o
programare serioas nu se poate ajunge la implementare fr a avea n prealabil algoritmii descrii ntr-un
limbaj de descriere (la noi Pseudocod). Deci programarea modular se refer n primul rnd la proiectarea
modular a algoritmilor i apoi la traducerea lor n limbajul de programare ales, innd seama de
specificul acestui limbaj. Programarea modular este strns legat de programarea ascendent i de
programarea descendent, ambele presupunnd folosirea SUBPROGRAMilor pentru toate subproblemele
ntlnite.
Avantajele programrii modulare sunt multiple. Menionm n cele ce urmeaz cteva dintre ele.
Descompunerea unei probleme complexe n subprobleme este un mijloc convenabil i eficient de a reduce
complexitatea (Principiul Divide et impera acioneaz i n programare). Este evident c
probabilitatea apariiei erorilor n conceperea unui program crete cu mrimea programului, lucru
confirmat i de experiena practic. De asemenea, rezolvnd o problem mai simpl, testarea unui modul
se poate face mult mai uor dect testarea ntregului algoritm.
24
Apoi, faptul c trebuiesc proiectate mai multe subprograme pentru subproblemele ntlnite,
permite munca mai multor programatori. S-a ajuns astfel la munca n echip, modalitate prin care se
ajunge la scurtarea termenului de realizare a produsului program.
Modulele se pot refolosi ori de cte ori avem nevoie de ele. Astfel, s-a ajuns la compilarea
separat a subprogramelor i la pstrarea subprogramelor obinute n biblioteci de subprograme, de unde
ele se pot refolosi la nevoie. Sunt cunoscute astzi multe astfel de biblioteci de subprograme.
Reutilizabilitatea acestor subprograme este o proprietate foarte important n activitatea de programare.
Ea duce la mrirea productivitii n programare, dar i la creterea siguranei n realizarea unui produs
corect.
Uneori, n timpul proiectrii algoritmului sau a implementrii lui, se ajunge la concluzia c
proiectarea a fost incomplet sau c unele module sunt ineficiente. i n aceast situaie programarea
modular este avantajoas, ea permind nlocuirea modulului n cauz cu altul mai performant.
Una din activitile importante n realizarea unui program este verificarea corectitudinii acestuia.
Experiena a artat c modulele se pot verifica cu att mai uor cu ct sunt mai mici. Abilitatea omului de
a nelege i analiza corectitudinea unui SUBPROGRAM este mult mai mare pentru texte scurte. n unele
cri chiar se recomand a nu se folosi SUBPROGRAMi mai mari dect 50 de propoziii. Sigur c o astfel
de limit nu exist, dar se recomand descompunerea unui SUBPROGRAM n ali SUBPROGRAMi
oricnd acest lucru este posibil n mod natural, deci aceti noi SUBPROGRAMi rezolv subprobleme de
sine stttoare, sau realizeaz funcii bine definite.
3.4 Programarea structurat
Programarea structurat este un stil de programare aprut n urma experienei primilor ani de
activitate. Ea cere respectarea unei discipline de programare i folosirea riguroas a ctorva structuri de
calcul. Ca rezultat se va ajunge la un algoritm uor de urmrit, clar i corect.
Termenul programare, folosit n titlul acestei seciuni i consacrat n literatura de specialitate, este
folosit aici n sens larg i nu este identic cu cel de programare propriu-zis. Este vorba de ntreaga
activitate depus pentru obinerea unui program, deci att proiectarea algoritmului ct i traducerea
acestuia n limbajul de programare ales.
Bohm i Jacopini au demonstrat c orice algoritm poate fi compus din numai trei structuri de
calcul:
structura secvenial;
structura alternativ;
structura repetitiv.
Fiecare din aceste structuri, ca parte dintr-o schem logic, are o singur intrare i o singur ieire
i sunt prezentate n figura 1.3.1.
Knuth consider programarea structurat ca fiind un mijloc de a face produsele program mai uor
de citit. De asemenea, programarea structurat este definit ca fiind programarea n care abordarea este
top-down, organizarea muncii este fcut pe principiul echipei programatorului ef, iar n proiectarea
algoritmilor se folosesc cele trei structuri de calcul definite de Bohm-Jacopini.
Ali autori consider programarea structurat nu ca o simpl metod de programare ci ansamblul
tuturor metodelor de programare cunoscute. Dar programarea modular, programarea top-down, sau
bottom-up (ascendent sau descendent) au aprut naintea programrii structurate. Important este faptul
c programarea structurat presupune o disciplin n activitatea de programare.
Considerm c programarea structurat se poate ntlni:
la nivel micro, privind elaborarea unui SUBPROGRAM;
la nivel macro, privind dezvoltarea ntregului produs informatic (algoritm).
La nivel micro programarea structurat este cea n care autorul este atent la structura fiecrui
modul n parte, cernd claritate i ordine n scriere i respectarea structurilor de calcul definite mai sus.
25
26
CAPITOLUL IV
ANALIZA ALGORITMILOR
O anumit problem poate fi rezolvat cu ajutorul calculatorului numai dac se gsete un
algoritm pentru rezolvarea ei, care este dat calculatorului sub forma unui program. ntruct toate
problemele practice pe care le ntlnim se pot rezolva cu ajutorul calculatorului, s-ar putea crede c orice
problem este rezolvabil. Aceast afirmaie este ns fals. Se tie c nu exist un program care s
rezolve "problema terminrii programelor":
"Scriei un program care s decid dac un algoritm oarecare, dat, intr sau nu ntr-un ciclu infinit".
Chiar i problemele pentru care exist algoritmi corespunztori nu sunt neaprat rezolvabile cu
calculatorul. Este posibil ca timpul necesar execuiei acestor algoritmi, sau cantitatea de memorie
necesar, s nu permit folosirea lor n practic.
Evident, dac spaiul de memorie necesar programului este mai mare dect cantitatea de memorie
disponibil, programul nu poate fi executat. De asemenea, dac numrul calculelor ce trebuie efectuat este
foarte mare, programul poate fi inutil. Iar asta se poate ntmpla destul de uor n cazul problemelor ce
trebuiesc rezolvate n timp real (adic soluia trebuie obinut naintea unui timp critic).
Iat cteva motive pentru care este necesar s analizm algoritmii pe care-i concepem pentru
rezolvarea unei probleme.
Ne intereseaz s analizm un program din mai multe puncte de vedere:
1) Corectitudine;
2) Eficien;
3) Posibilitate de mbuntire;
4) Alte caliti pe care le are.
4.1 Corectitudinea programelor
Un program este corect dac el satisface specificaiile problemei. Nu ne intereseaz ct memorie
folosete acest program, din cte instruciuni este compus, sau ct timp de execuie necesit. Cu alte
cuvinte, un program este corect dac pentru acele date de intrare care satisfac specificaiile problemei
rezultatele obinute n urma execuiei sunt corecte.
Pentru orice program P deosebim trei tipuri de variabile, pe care le vom grupa n trei vectori X, Y
i Z. Componentele vectorului X desemneaz variabilele de intrare, deci datele presupuse cunoscute n
problema rezolvat prin programul P. Componentele vectorului Z sunt variabilele care reprezint
rezultatele cerute de problem. n sfrit, componentele vectorului Y sunt variabilele de lucru, care
noteaz diferitele rezultate intermediare necesare n program.
O problem nu are sens pentru orice date de intrare. Vom folosi predicatul R(X) pentru a preciza
datele pentru care problema are sens. R(X) se numete predicat de intrare sau precondiie. Pentru acele
valori ale lui X pentru care predicatul este adevrat problema are sens, pentru celelalte nu are sens s
executm programul P.
ntre rezultatele Z ale problemei i datele iniiale X (cunoscute n problem) exist anumite relaii.
Vom reda aceste relaii prin predicatul de ieire R(X,Z), numit i postcondiie. Acesta este corect pentru
acele valori a i b ale vectorilor X i Z pentru care rezultatele problemei sunt b n cazul cnd datele iniiale
sunt a i este fals n caz contrar. Deci, dac executnd programul cu datele iniiale a obinem rezultatele b'
i R(a,b') este fals, acest fapt este un indiciu c rezultatele obinute n program nu sunt corecte. Apare o
alt regul: fiecare variabil s aib semnificaia ei i s nu fie folosit n scopuri diferite.
27
Pentru un program complex, deci pentru o schem logic cu un numr foarte mare de drumuri
START-STOP, testarea ar fi o activitate complex, constnd dintr-un numr foarte mare de execuii. nc
un motiv pentru a practica programarea modular, caz n care testarea se face asupra unor module mai
mici i asupra interfeei dintre ele, aa cum se va meniona mai jos.
Stabilirea datelor de test dup textul programului are i unele dezavantaje. n primul rnd,
programul poate fi incomplet i s nu corespund specificaiilor. Pe drumurile existente el este corect, dar
lipsesc drumuri care, conform specificaiilor, ar trebui s existe. Lipsa acestor drumuri este o greeal
grav care nu va fi descoperit de datele de test care ne duc doar pe drumurile existente. Din aceast
cauz se recomand o testare mixt.
n textul unui program exist i drumuri moarte, pe care nu se poate merge oricare ar fi datele de
intrare, deci nu putem gsi date de test corespunztoare acestor drumuri. Adeseori aceste drumuri scot n
eviden erori prin simpla analiz a textului. Astfel, n succesiunea de propoziii Pseudocod
DAC n<2 ATUNCI
. . .
DAC n>3 ATUNCI A SFDAC
. . .
SFDAC
grupul A este inaccesibil oricare ar fi valoarea lui n. Este ns foarte frecvent eroarea de omisiune a unui
caracter; n cazul nostru tastarea numrului 2 n loc de 20, ceea ce schimb complet sensul textului
Pseudocod de mai sus.
Adesea este imposibil s se execute programul cu toate datele de test stabilite. n acest caz apare
problema alegerii acelei submulimi din aceste date care s aib ansa maxim de a depista erorile
prezente n program. Testarea minim care trebuie fcut const ntr-un numr de execuii a programului
care s ne asigure c fiecare instruciune din program a fost executat cel puin odat. Ea nseamn mult
mai puine execuii dect toate drumurile START-STOP.
Exist date de test care ne duc pe un anumit drum fr a depista erori existente n instruciunile
ntlnite i alte date de test care depisteaz aceste erori. nc un motiv pentru care se recomand o testare
mixt.
Ca ordine de folosire a datelor de test n timpul testrii, se recomand mai nti testarea dup
specificaii i apoi testarea dup textul programului.
Este necesar i testarea robusteei programului, care nseamn buna lui comportare la date de
intrare intenionat greite, pentru care problema nu are sens. Unele programe intr n aceste condiii n
ciclu infinit, altele se termin cu erori de execuie. Un program robust nu trebuie s fie afectat de datele de
intrare eronate. Comportarea cea mai normal n astfel de situaii ar fi semnalarea unor mesaje de eroare
corespunztoare.
La un produs program complex testarea este o activitate mult mai complicat. Este necesar o
testare separat a fiecrui modul n parte, o testare a interfeei dintre module i o testare a produsului n
ansamblu (testarea de integrare).
Testarea de integrare se refer la funcionarea programului realizat n ansamblu. Dup ce fiecare
modul n parte a fost testat i corectat, deci n ipoteza c fiecare modul n parte este corect, e necesar s se
verifice comportarea global a programului. n aceast etap gsirea erorilor, nlturarea cauzelor care lea generat i corectarea lor, poate fi
foarte dificil, mai ales atunci cnd ele provin dintr-o proiectare greit.
Execuia unui program se poate termina anormal datorit apariiei unor erori ca:
mpriri la zero;
alte erori ce provoac depiri;
neconcordana ntre parametri actuali i formali;
depirea dimensiunii tablourilor.
Dar chiar i la execuia normal a programului putem avea erori, unele foarte grave, obinnd
rezultate greite. n ambele situaii urmeaz depanarea programului, adic descoperirea cauzei erorilor
i nlturarea lor.
29
O metod util n depanarea programelor, n special pentru nceptori, este inserarea n program a
unor tipriri auxiliare. Mai ales n locurile vecine cu instruciunile care au provocat eroarea i pentru
variabilele implicate n producerea ei. Observarea valorilor unei variabile, a schimbrilor fcute n timpul
execuiei, pot dezvlui programatorului cauza erorii. Cu siguran i va arta c o anumit variabil ia alte
valori dect cele la care se ateapt el. De altfel, pe timpul testrii unui program, sunt utile semnalrile
oricror semne de eroare. Recomandm verificarea valorilor variabilelor imediat dup obinerea
acestora.
4.3 Complexitatea algoritmilor
n aceast seciune ne va interesa eficiena unui algoritm. Mai exact, ne intereseaz s comparm
ntre ei mai muli algoritmi care rezolv aceeai problem. Care dintre ei este mai bun? Evident, vom
compara numai algoritmi despre care tim c sunt coreci.
Putem compara doi algoritmi n raport cu:
cantitatea de memorie necesar;
viteza de lucru, deci timpul necesar rezolvrii problemei.
Dac n urm cu dou decenii volumul de memorie necesar rezolvrii unei probleme era un factor
important din cauza memoriei reduse existente la calculatoarele din acel timp, astzi acest factor a devenit
mai puin important. Calculatoarele actuale au memorie suficient de mare pentru marea majoritate a
algoritmilor ntlnii.
Timpul necesar execuiei unui program depinde de numrul operaiilor ce trebuiesc executate. Iar
numrul operaiilor efectuate depinde de datele de intrare, deci se schimb de la o execuie la alta.
Exist ns un cel mai ru caz, pentru acele date de intrare pentru care numrul operaiilor
efectuate este maxim. n acest caz vorbim de complexitate n cel mai ru caz.
De asemenea, putem vorbi de numrul mediu de operaii efectuate ntr-o execuie. Dac numrul
execuiilor posibile este finit atunci acest numr mediu este egal cu numrul operaiilor efectuate n toate
execuiile, mprit la numrul execuiilor. Sunt ns foarte puine programe cu aceast proprietate. Pentru
aproape toate programele, cel puin teoretic, numrul execuiilor posibile este infinit.
4.4 Documentarea programelor
n paralel cu elaborarea programului trebuie elaborat i o documentaie. Aceasta va conine toate
deciziile luate n crearea programului. Documentarea este activitatea de prezentare a programului celor
care vor fi interesai s obin informaii despre el. Acetia sunt n primul rnd persoanele care au realizat
programul, apoi persoanele care-l vor folosi i persoanele solicitate s fac ntreinerea acestuia.
Adeseori se ntlnesc programe fr nici o alt documentaie n afara textului propriu-zis al
programului. n graba de a termina ct mai repede, programul nu este nsoit de nici o documentaie i
frecvent nu sunt folosite nici comentarii n textul programului.
Sigur c nsi textul programului constituie o autodocumentare. Iar comentariile prezente n
program dau explicaii suplimentare despre program. Este ns necesar o documentaie complet, scris,
care va conine:
enunul iniial al problemei;
specificaia (vezi seciunea 4.1);
documentaia de proiectare (metoda de rezolvare aleas i proiectarea algoritmilor
folosii. Pentru fiecare algoritm va fi prezentat subproblema corespunztoare, cu
specificaia ei i rolul fiecrei variabile);
documentaia de programare, care va include textul programului;
datele de test folosite;
documentaie privind exploatarea programului;
modificri fcute n timpul ntreinerii programului.
30
Menionm c cele mai recente produse realizate de firmele consacrate au, pe lng documentaia
scris, i o autodocumentaie (funcii HELP).
Referitor la autodocumentare, folosirea comentariilor, alegerea cu grij a denumirii
variabilelor, ct i claritatea textului, obinut prin indentare i grij asupra structurii
programului, este util nu numai pe timpul elaborrii programului, dar mai ales pe timpul ntreinerii i
modificrilor ulterioare.
Denumirea variabilei s fie astfel aleas nct s redea ct mai bine semnificaia ei.
Cei care au dorit s refoloseasc programe scrise cu cteva luni n urm neleg foarte bine
diferena dintre un program nsoit de comentarii explicative i un program fr nici o explicaie. Uitarea
acioneaz asupra oricrei persoane i, chiar dac este posibil, descifrarea unui program cere timp i nu
este o sarcin prea uoar. Comentariile sunt recomandate, fiind un mijloc de autodocumentare a
programului surs.
Sigur c prima documentaie a oricrui program este textul surs propriu-zis. Este bine ca acest
text s poat fi citit ct mai uor, iar programarea structurat duce la un program mai uor de citit dect
unul lipsit de orice structur.
Este ns nevoie i de o documentaie nsoitoare scris, care constituie documentarea propriu-zis
a programului. Aceasta trebuie s redea toate deciziile fcute n timpul proiectrii, s prezinte diagrama de
structur a ntregului produs i fiecare parte separat. Pentru fiecare modul documentaia va conine:
numele acestuia;
datele de intrare;
datele de ieire;
funcia realizat de modulul respectiv;
variabilele folosite i semnificaia lor;
algoritmul propriu-zis.
Este necesar ca aceste informaii s se afle i sub forma unor comentarii n textul programului. De
asemenea, documentaia va conine i textul final al programului.
Este necesar i o documentaie de folosire a produsului realizat. Beneficiarul nu este interesat de
modul n care a fost realizat programul ci de modul n care l poate folosi.
O documentare complet a unui program poate fi util nu numai pentru folosirea i ntreinerea
programului. Componente ale unui produs existent pot fi utile i n realizarea altor produse. Este ns
necesar s se neleag ct mai uor ce funcii realizeaz aceste componente i cu ce performane.
Folosirea acestor componente existente, testate i documentate, duce evident la creterea productivitii n
realizarea noului produs.
4.5 Stil n programare
Fiecare programator are stilul s propriu de concepere i redactare a unui program. Este bine ca el
s respecte anumite reguli generale de programare, astfel nct programele elaborate s aib anumite
caliti.
Calitile pe care le poate avea un program sunt urmtoarele:
Corectitudine = proprietatea programului de a respecta specificaiile i a da rezultate corecte;
Extensibilitate = posibilitatea adaptrii programului la unele schimbri n specificaie;
Robustee = abilitatea de a recunoate situaiile n care problema ce se rezolv nu are sens i de a se
comporta n consecin (de exemplu, prin mesaje de eroare corespunztoare);
Reutilizabilitate = posibilitatea reutilizrii ntregului program sau a unor pri din el n alte aplicaii;
Compatibilitate = uurina de combinare cu alte produse program;
Portabilitate = posibilitatea de folosire a produsului program pe alte sisteme de calcul, diferite de cel pe
care a fost conceput;
Eficien = msura n care sunt bine folosite resursele sistemului de calcul;
Claritate = uurina citirii i nelegerii textului programului, a structurilor din care este compus i a
rolului denumirilor i prilor sale.
31
Un produs program este considerat de calitate dac are calitile de mai sus, dac lansat n
execuie d rezultate corecte, dac textul lui se poate citi i nelege, dac poate fi uor ntreinut i dac
este terminat la data fixat.
Stilul unui programator este dat de msura n care programul su are aceste caliti i de
vizibilitatea lor. Evident, pe lng aceste caliti, vizibile i n text, stilul de programare este dat i de
corectitudinea i robusteea produselor realizate. Programul trebuie s funcioneze i la introducerea unor
date greite (pentru care problema nu are sens), recunoscnd aceast situaie i semnalnd-o. n acelai
timp rezultatele obinute pentru date pentru care problema are sens trebuie s fie corecte. Iar
corectitudinea sau incorectitudinea programului este o consecin a modului n care programatorul a
respectat regulile de programare (vezi capitolul 8) i a experienei obinute n activitatea de programare.
Privind claritatea algoritmului trebuie s observm c indentarea (paragrafarea) este un alt mijloc
de a mri claritatea scrierii. Textul unui algoritm poate fi scris cuvnt dup cuvnt, completnd tot rndul
asemeni textului unui roman. Claritatea lui este mic, dup cum urmeaz:
PROGRAMUL CLASAMENT ESTE:
DATE m,n,NUMEi, i=1,n, NOTEi,j, j=1,m, i=1,n;
PENTRU i:=1,n EXECUT {calculeaz media general a elevului i}
FIE S:=0;
PENTRU j:=1,m EXECUT S:=S+NOTEi,j SFPENTRU
FIE MEDIIi:=S/M
SFPENTRU
PENTRU j:=1,m EXECUT
CHEAM ORDINE(n,NOTEj,O);
CHEAM TIPAR(n, NUME, O)
SFPENTRU
CHEAM ORDINE(n,MEDII,O);
CHEAM TIPAR(n,NUME,O)
SFALGORITM
Considerm c fiecare programator trebuie s respecte anumite reguli de scriere a programelor, cu
gndul la claritatea textului. n unele cri sunt date mai multe reguli de indentare. Astfel, Gries sugereaz
urmtoarele reguli:
- instruciunile unei secvene se vor scrie aliniate, ncepnd toate n aceeai coloan;
- instruciunile unei structuri de calcul (instruciuni compuse) se vor scrie ncepnd toate
din aceeai coloan, aflat cu 2-4 caractere la dreapta fa de nceputul instruciunii
compuse;
- pe o linie pot fi scrise mai multe instruciuni, cu condiia ca ele s aib ceva comun.
Astfel, 2-4 instruciuni scurte de atribuire pot fi scrise pe acelai rnd. Acest lucru se
recomand n vederea unei scrieri compacte a programului. E bine ca un program ce se
poate scrie pe o pagin, cu respectarea structurii lui, s nu fie ntins pe dou pagini !
Considerm c nu exist reguli de scriere obligatorii pentru toat lumea! Dar fiecare programator
trebuie s aib propriile lui reguli de scriere.
Tot privind claritatea scrierii programului, se recomand ca denumirile variabilelor s fie astfel
alese nct s reflecte semnificaia acestor variabile.
Un alt mijloc de a mri claritatea textului unui program const n inserarea comentariilor n text.
Comentariile sunt texte explicative nchise ntre acolade. Ele au rolul de a explica cititorului anumite
pri din program. Am spus deja c n proiectarea algoritmilor folosim i propoziii nestandard care vor fi
pe parcurs nlocuite cu propoziii standard. E bine ca aceste propoziii s rmn n text sub form de
comentarii.
Comentariile vor fi prezente:
n capul programului, pentru a prezenta titlul i scopul programului, perioada realizrii
lui i numele programatorului;
32
33
CAPITOLUL V
CLASE DE ALGORITMI
Cutarea i Sortarea sunt dou dintre cele mai des ntlnite subprobleme n programare. Ele
constituie o parte esenial din numeroasele procese de prelucrare a datelor. Operaiile de cutare i
sortare sunt executate frecvent de ctre oameni n viaa de zi cu zi, ca de exemplu cutarea unui cuvnt n
dicionar sau cutarea unui numr n cartea de telefon.
Cutarea este mult simplificat dac datele n care efectum aceast operaie sunt sortate
(ordonate, aranjate) ntr-o anumit ordine (cuvintele n ordine alfabetic, numerele n ordine cresctoare
sau descresctoare).
Sortarea datelor const n rearanjarea coleciei de date astfel nct un cmp al elementelor
coleciei s respecte o anumit ordine. De exemplu n cartea de telefon fiecare element (abonat) are un
cmp de nume, unul de adres i unul pentru numrul de telefon. Colecia aceasta respect ordinea
alfabetic dup cmpul de nume.
Dac datele pe care dorim s le ordonm, adic s le sortm, sunt n memoria intern, atunci
procesul de rearanjare a coleciei l vom numi sortare intern, iar dac datele se afl ntr-un fiier
(colecie de date de acelai fel aflate pe suport extern), atunci procesul l vom numi sortare extern.
Fiecare element al coleciei de date se numete articol iar acesta la rndul su este compus din
unul sau mai multe componente. O cheie C este asociat fiecrui articol i este de obicei unul dintre
componente. Spunem c o colecie de n articole este ordonat cresctor dup cheia C dac C(i) C(j)
pentru 1i<jn, iar dac C(i) C(j) atunci irul este ordonat descresctor.
5.1 Algoritmi de cutare
n acest subcapitol vom studia cteva tehnici elementare de cutare i vom presupune c datele se
afl n memoria intern, ntr-un ir de articole. Vom cuta un articol dup un cmp al acestuia pe care l
vom considera cheie de cutare. n urma procesului de cutare va rezulta poziia elementului cutat (dac
acesta exist).
Notnd cu k1, k2, ...., kn cheile corespunztoare articolelor i cu a cheia pe care o cutm, problema
revine la a gsi (dac exist) poziia p cu proprietatea a = kp.
De obicei articolele sunt pstrate n ordinea cresctoare a cheilor, deci vom presupune c
k1 < k2 < .... < kn .
Uneori este util s aflm nu numai dac exist un articol cu cheia dorit ci i s gsim n caz contrar locul
n care ar trebui inserat un nou articol avnd cheia specificat, astfel nct s se pstreze ordinea existent.
Deci problema cutrii are urmtoarea specificare:
Date a,n,(ki, i=1,n);
Precondiia: nN, n1 i k1 < k2 < .... < kn ;
Rezultate p;
Postcondiia: (p=1 i a k1) sau (p=n+1 i a > kn) sau (1<pn) i (kp-1 < a kp).
Pentru rezolvarea acestei probleme vom descrie mai muli SUBPROGRAMi.
O prim metod este cutarea secvenial, n care sunt examinate succesiv toate cheile.
{nN, n1 i}
{k1 < k2 < .... < kn}
{Se caut p astfel ca:}
{(p=1 i a k1) sau (p=n+1 i a>kn)}
{sau (1<pn) i (kp-1 < a kp)}
{Cazul "nc negasit"}
Fie p:=0;
34
sfdac
sf-BinarySearch
n funcia BinarySearch descris mai sus, variabilele St i Dr reprezint capetele intervalului de
cutare, iar m reprezint mijlocul acestui interval.
Se observ c funcia BinarySearch se apeleaz recursiv. Se poate nltura uor recursivitatea, aa
cum se poate vedea n urmtoarea funcie:
Funcia BinSeaNerec (a,n,K,St,Dr) este:
Cttimp Dr-St>1 execut
m:=(St+Dr) Div 2;
Dac aK[m]
atunci Dr:=m
altfel St:=m
sfdac
sfct
BinSeaNerec:=Dr
sf-BinSeaNerec
5.2 Sortare intern
Prin sortare intern vom nelege o rearanjare a unei colecii aflate n memoria intern astfel
nct cheile articolelor s fie ordonate cresctor (eventual descresctor).
Din punct de vedere al complexitii algoritmilor problema revine la ordonarea cheilor. Deci
specificarea problemei de sortare intern este urmtoarea:
Date n,K;
{K=(k1,k2,...,kn)}
Precondiia: kiR, i=1,n
Rezultate K';
Postcondiia: K' este o permutare a lui K, dar ordonat cresctor.
Deci k1 k2 ... kn.
O prim tehnic numit "Selecie" se bazeaz pe urmtoarea idee: se determin poziia
elementului cu cheie de valoare minim (respectiv maxim), dup care acesta se va interschimba cu
primul element. Acest procedeu se repet pentru subcolecia rmas, pn cnd mai rmne doar
elementul maxim.
SUBPROGRAMul Selectie(n,K) este:
{Se face o permutare a celor}
{n componente ale vectorului K astfel}
{ca k1 k2 .... kn }
Pentru i:=1; n-1 execut
Fie ind:=i;
Pentru j:=i+1; n execut
Dac kj < kind atunci ind:=j sfdac
sfpentru
Dac i<ind atunci t:=ki; ki:=kind; kind:=t sfdac
sfpentru
sf-Selectie
Se observ c numrul de comparri este:
(n-1)+(n-2)+...+2+1=n(n-1)/2
indiferent de natura datelor.
A treia metod care va fi prezentat, numit "BubbleSort", compar dou cte dou elemente
consecutive iar n cazul n care acestea nu se afl n relaia dorit, ele vor fi interschimbate. Procesul de
comparare se va ncheia n momentul n care toate perechile de elemente consecutive sunt n relaia de
ordine dorit.
36
ordonarea unui subir se poate rezolva elementar fr a mai fi necesar desprirea lui n alte dou
subiruri (lungimea subirului este cel mult 2).
Algoritmul corespunztor este prezentat n seciunea urmtoare sub forma unei proceduri
recursive care ordoneaz un subir preciznd limitele acestuia.
5.3 Interclasare
Fiind date dou colecii de date, ordonate cresctor (sau descresctor) dup o cheie, se cere s se
obin o colecie care s fie de asemenea ordonat cresctor (respectiv descresctor) dup aceeai cheie i
care s fie format din articolele coleciilor date. Acest lucru se poate obine direct (fr o sortare a
coleciei finale) prin parcurgerea secvenial a celor dou colecii, simultan cu generarea coleciei cerute.
Prin compararea a dou elemente din listele de intrare se va decide care element va fi adugat n lista de
ieire.
Deci ne intereseaz un algoritm de rezolvare a problemei ce are urmtoarea specificare:
Date m, (xi, i=1,m), n, (yi, i=1,n);
Precondiia: {x1 x2 ... xm} i {y1 y2 ... yn}
Rezultate k, (zi, i=1,k);
Postcondiia: {k=m+n} i {z1 z2 ... zk} i (z1,z2,..., zk) este o permutare a valorilor
(x1, ..., xm,y1,..., yn)
O soluie posibil ar fi depunerea componentelor vectorului X i a componentelor vectorului Y n
vectorul Z, realiznd astfel a doua parte din postcondiie. Ordonnd apoi componentele vectorului Z
obinem soluia dorit. Acest algoritm, dei corect, este ineficient i, n plus, nu este util n sortrile
externe (vezi seciunea 5.4). Este important ca la o singur trecere prin vectorii X i Y s se obin
vectorul Z. Acest lucru este realizat de urmtorul algoritm de interclasare:
SUBPROGRAMul Interclasare(m,X,n,Y,k,Z) este:
{X are cele m}
{componente ordonate nedescresctor}
{La fel Y cu n componente. Cele m+n valori}
{se depun n Z, tot ordonate nedescresctor}
Fie i:=1; j:=1; k:=0;
Cttimp (i<=m) i (j<=n) execut
{Exist componente}
Dac xiyj
atunci Cheam PUNE(i,xi,k,Z)
{i n X}
altfel Cheam PUNE(j,yj,k,Z)
{i n Y}
sfdac
sfct
Cttimp (i<=m) execut
{Exist componente}
Cheam PUNE(i,xi,k,Z)
{numai n X}
sfct
Cttimp (j<=n) execut
{Exist componente}
Cheam PUNE(j,yj,k,Z)
{numai n Y}
sfct
sf-Interclasare
Aici s-a folosit SUBPROGRAMul PUNE(ind,val,k,Z) care pune n vectorul Z valoarea val i
mrete indicele ind cu 1, subalgortim dat n continuare.
SUBPROGRAMul PUNE(ind,val,k,Z) este:
k:=k+1;
zk:=val;
ind:=ind+1
sf-PUNE
38
{Adaug val}
{n vectorul Z cu}
{k componente i}
{mrete ind cu 1}
sortarea echilibrat;
sortarea polifazic;
sortarea n cascad.
Evident c sortarea depinde i de configuraia calculatorului folosit, dar i de suportul pe care se
afl fiierul de sortat i fiierele intermediare create.
Principial sortarea extern presupune parcurgerea a dou etape importante:
a) Divizarea fiierului de sortat F, n n fiiere H1, H2, ..., Hn, cu sortarea intern a acestora;
b) Interclasarea acestor fiiere sortate pentru a ajunge la fiierul dorit G.
40
CAPITOLUL VI
EVOLUIA LIMBAJELOR DE PROGRAMARE
Un limbaj de programare este un sistem de convenii adoptate pentru realizarea unei comunicri
ntre programator i calculator . Limbajele folosite pentru programarea unui calculator sunt extrem de
asemntoare limbajelor naturale . Ele sunt compuse din :
cuvinte (rezervate);
punctuaie;
propoziii i fraze;
reguli sintactice etc.
Aa cum pentru nsuirea unei limbi strine trebuie nvate cuvintele acesteia i regulile cu care
pot fi manevrate tot aa pentru nsuirea unui limbaj de programare trebuie studiate cuvintele i semnele
care l compun mpreun mpreun cu regulile de manevrare a lor.
De-a lungul timpului,oamenii au inventat masini pentru a calcula cat mai eficient.Inaintea
calculatoarelor performante din zilele noastre,au existat alte masini de calcul.
Momentul iniial al istoriei calculatoarelor este, de obicei legat de numele matematicianului
englez Charles Babbage. El a propus n anul 1830 o Main Analitic care a anticipat n mod fascinant
structura calculatoarelor actuale. Ideile sale au devansat cu peste 100 de ani posibilitiile tehnologice ale
vremii sale. naintea a mai fost ncercri n acest domeniu ale lui Leibnitz i Pascal (sec al XVII-lea) .
Urmtorul moment de referin este anul 1937, cnd Howard Aiken, de la Universitatea Harvard a propus
Calculatorul cu secven de Comand Automat, bazat pe o combinaie ntre ideile lui Babbage i
calculatoarele elertromecanice, produse de firma IBM. Construcia acestuia a nceput n anul 1939 i s-a
terminat n anul 1944, fiind denumit Mark I . El a fost n principal primul calculator electromecanic, fiind
alctuit din comutatoare i relee.
nlocuirea releelor cu tuburi electronice a constituit un important pas nainte. Rezultatul a fost
concretizat n calculatorul ENIAC ( Electronic Numerical Integrator And Computer ), primul calculator
electronic digital. El conine circa 18.000 de tuburi electronice i executa 5.000 de adunri pe secund,
avnd o memorie de 20 de numere reprezentate n zecimal. Programarea sa se realiza prin poziionarea a
circa 6.000 de comutatoare, cu mai multe poziii. O semnificaie aparte o are faptul c n arhitectura
calculatoarelor Mark I i ENIAC, intrau mai multe elemente de calcul, ce lucrau n paralel la o problem
comun, fiind dirijate de o singur unitate de comand . Aceast soluie a fost aleas datorit vitezei
reduse a fiecrei uniti de calcul, n parte. La versiunea urmtoare s-a renunat la aceast structur
paralel de calcul, deoarece s-a considerat c viteza unei uniti de calcul, realizat cu circuite
electronice, este suficient . Soluia prelucrrii paralele a fost reluat ulterior dup anii 80 pentru mrirea
performanelor unui sistem de calcul; astfel n 1996 Firma INTEL a realizat un supercalculator ce
folosete peste 7000 de procesoare PENTIUM utiliznd tehnica de calcul masiv (utilizat pentru
simularea testelor nucleare, n cercetri genetice, spaiale, meteorologice).
De remarcat c la realizarea primelor calculatoare, n calitate de consultant al echipei, a lucrat i
matematicianul John von Neumann, unul dintre matematicienii importani ai secolului XX. De altfel, la
realizarea calculatorului EDVAC ( primul calculator cu circuite electronice ) el a stabilit 5 caracteristii
principale ale calculatorului cu program memorat :
Trebuie s posede un mediu de intrare, prin intermediul cruia s se poat introduce un
numr nelimitat de operanzi i instruciuni .
Trebuie s posede o memorie, din care s se citeasc instruciunile i operanzii i n care
s se poat memora rezultatele.
Trebuie s posede o seciune de calcul, capabil s efectueze operaii aritmetice i
logice, asupra operanzilor din memorie.
Trebuie de asemenea s posede un mediu de ieire, prin intermediul cruia un numr
nelimitat de rezultate s poat fi obinute de ctre utilizator.
41
acea dat, extraordinar. Dac aveai un sistem 8080 sau Z80, cu sistem de operare CP/M, cu 64 kilobii de
RAM i o pereche de uniti de disc flexsibil de 8", aveai ultimul strigt al modei calculatoarelor i l
fceai verde de invidie pe orice pasionat. Un singur lucru le putea depi invidia i ctiga ura: s ai un
disc i o imprimat; ambele necesitau o cheltuial exorbitant.Discurile acelor timpuri merit puin
atenie. Primul tip larg rspndit mpreun cu microcalculatoarele aveau discuri de 14" (comparai-le cu
cele de 3,5" disponibile astzi) i un timp de acces suficient pentru o pauz de cafea.
Apple Computer, binecunoscut ca avndu-i nceputurile ntr-un garaj, a aprut n 1976. Apple a fost
fondat de legendarii Steve Jobs i Steve Wozniack, i este recunoscut drept compania care a pus bazele
industriei calculatoarelor personale. Dei povestea lui Visilac i a calculatorului Apple II este bine
cunoscut, merit s o spunem nc o dat, pentru c arat motivele care au generat revoluia
calculatoarelor personale.La mijlocul anilor `70, dac doreai s faci ncercri de genul i dac calculnd
pe mainframe, trebuia s scrii un program, s-l depanezi, s ncerci un set de date, s verifici rezultatele,
s ncerci un set de date mai complex s.a.m.d. Era un procedeu cel puin laborios i nu foarte practic, cu
excepia cazului n care priviziunele aveau importan pentru corporaie i aveai suficient timp la
dispoziie. Aceast situaie a motivat doi studeni de la Harvard Business School s fac primul program
de calcul tabelar: Visicalc.Apple II avea la baz un procesor Motorola 6502 (proiectat pe 8 bii), pn la
128 kilobii de RAM i utiliza un casetofon pentru a stoca date i programe. Apple a ncheiat o nelegere
cu realizatorii lui Visicalc pentru a obine exclusivitatea programului pe Apple II. Acestui program i se
acord meritul de a fi catapultat Apple de la un venit de 800.000 de dolari n 1977 la puin sub 48 de
milioane n 1979.Utilizatorii cumprau Apple II doar pentru a rula Visicalc, i o dat cu el un raft ntreg
de aplicaii, care ofereau utilizatorilor, pentru prima dat la un pre rezonabil, putere de calcul accesibil
i dedicata
IBM preia controlul
Calculatoarele despre care am vorbit, mainile CP/M i Apple, nu erau numite calculatoare
personale acesta nu a fost un termen recunoscut pn n august 1981, data de natere a calculatorului
IMB PC a fost creat de pia, datorit acelor sisteme de microcalculatoare care au fcut posibil existena
calculatorului IBM PC.Dei microprocesorul care a stat la baz calculatorului IBM PC a fost produs n
1974, calculatorul IBM PC a fost produs abia n 1981. Intel 8088 era un microprocesor pe 16 bii, care
putea lucra cu mai mult memorie i mai rapid dect predecesorii si. IBM a delegat o companie
necunoscut, numit Microsoft, pentru a realiza un sistem de operare. Restul este, aa cum o spun ei,
istorie. IBM PC a devenit un standard, n realitate o serie de standarde care au adus la vnzarea de
aproximativ 100 de milioane de calculatoare personale din 1981. puterea marketing-ului IBM a dus la
succesul lui IBM PC. IBM avea bani i poziia pe pia astfel nct s fac calculatorul IBM PC acceptat
n corporaii. Dei e uor s critici IBM pentru greelile, destul de multe, fcute n dezvoltarea pieei
calculatoarelor personale i lipsa de receptivitate fa de o pia care cretea mai rapid dect putea acoperi
IBM, fr amestecul lui IBM, aceast pia ar fi crescut mult mai ncet i mai fragmentat.
Calculatorul IBM PC a continuat tendina dat de Apple II, aducnd puterea de calcul la ndemna
utilizatorilor. Posibilitatea de a-i mbunti i mri productivitatea personal a fost o atracie att de
mare, nct oamenii au trecut peste orice pentru a-i cumpra un calculator personal. Ei au pclit
bugetele departamentale cumprndu-le ca maini de scris sau chiar pltind diferena din propriul
buzunar.
Multe companii au avut reineri n a urma tendina de introducere a calculatoarelor personale, dar
au descoperit ulterior c acestea erau folosite din plin de concurena. n aceste companii, de obicei,
Centrul de Calcul era uluit cnd descoperea invazia calculatoarelor personale. Fanaticii mainframe-urilor
erau probabil cei mai surprini cnd aflau ce se ntmplase.
Aparent peste noapte, Centrul de Calcul pierdea un procent destul de mare din prelucrrile de date
ale companiei. Teritoriul pe care credeau c l stpnesc era brusc invadat. Ceea ce era probabil cel mai
tulburtor pentru ei era c utilizatorii de calculatoare personale vorbeau despre informaii i nu doar
despre coloane de date.
43
Utilizatorii au descoperit c puteau combina i prelucra cum doresc datele. Puteau realiza rapoarte
despre ceea ce i interesa. Pe de alt parte, dac ai fi cerut la Centrul de Calcul un raport, i-ar fi dat doar
un raport standard aa cum le genera mainframe-ul. (Rapoartele standard consumau o mic pdure de
hrtie, cnd toi utilizatorii doreau doar o pagin ).
Astfel a aprut o nou tendin: aceea de a a-i realiza singur calculele. Atunci cnd utilizatorii
doreau s fac simulri financiare de tipul i dac, ei nu mai trebuiau s mearg, cu plria n mn
(metamorfic vorbind) la Centrul de Calcul. Puteau s-i porneasc calculatorul personal, s ruleze
programul de calcul tabelar i s realizeze o duzin de scenarii, n timpul n care Centrul de Calcul ar fi
luat n considerare cererea lor.Deja nu mai exista nici o posibilitate pentru Centrul de Calcul de a
schimba lucrurile. Corporaiile aveau toate motivele s susin noua tendin i n acelai timp destule
motive de ngrijorare pentru anarhia care se crea. Distribuirea datelor prin companii, cum vei vedea, avea
multe implicaii i exista marele risc de a scpa totul de sub control.
Revoluia calculatoarelor personale, mai mult dect orice, a forat Centrele de Calcul s-i
regndeasc rolul i tehnologia pe care o foloseau. Ele nu au avut cu adevrat de ales i au devenit
Servicii de gestiunea de informaie (Management Information Service) sau IT (Information Tehnology)
sau orice altceva care coninea cuvntul informaie. De asemenea, au trebuit s urmeze sau cel puin s se
obinuiasc cu valul tehnologiilor aduse de calculatoarelor personale.
nceputul conectrii
Pe timpul CP/M-ului, preul perifericilor de calitate era exorbitant. Un disc de 14" i 10MB, care
consuma 5 amperi i fcea zgomot ca un avion care decola, era tot att de scump ca i un calculator. O
imprimat matriceal, care nici nu se apropia de calitatea unei letter-quality, era o resurs preioas. n
momentul lansrii calculatorului IBM PC preurile sczuser, dar erau nc destul de mari. Pe scurt,
perifericele calculatoarelor personale erau ca aurul: rare i scumpe.
Nu era practic ca fiecare calculator s aib disc i imprimat, dei fr ele productivitatea calculatoarelor
personale era mai mic. O alt problem era folosirea n comun a datelor. Dac aveai nevoie de un
document creat de altcineva, trebuia s iei dischet, s-i pui pantofii de sport i s alergi la acel
microcalculator s-l iei. De aici, numele acestui tip de partajare a datelor: reea sportiv.
Reeaua sportiv
Acest tip de reea a ridicat multe probleme. Cum puteai s fii sigur c documentele cu care lucrai
erau la zi, dac diverse copii modificate de un numr oarecare de oamenii circulau pe diverse dischete?
Cum poi opri furtul documentelor? Cum poi opri furtul documentelor? i dac ultima versiune, i
singura, a unui document se afl pe o singur dischet folosit de cineva drept suport pentru ceac de
cafea? i dac...?
Existau sute de probleme cu aceast reea i toate evideniau o singur soluie: nevoia, absolut necesitate,
de a schimba documentele electrice ntre calculatoare. Combinai cu dorina de a schimba, de a folosi n
comun discuri i imprimate scumpe, i avei o problem la care s meditai. Nevoia de a folosi n comun
date i periferice a stimula crearea primei reele locale de calculatoare, dar aa cum vei vedea, problema
central a fost nevoia de a folosi n comunicatie.
Comutatoarele de date
O modalitate de a folosi n comun periferice a fost folosirea unui comutator de date: un dispozitiv
ce permite doar unui utilizator la un moment dat s foloseasc dispozitivul, ca exemplu o imprimat.
Dac o alt persoan folosea imprimata cnd doreai tu s o foloseti, trebuia s atepi pn termina. Un
comutator de date poate fi comparat cu o coad la banc. Orice persoan (datele ce vor vi imprimate) care
se aeaz prima coad (comutatorul) ajunge prima la casier (imprimanta). Restul trebuia s atepte pn
ce aceasta termin.
Comutatorul de date ofer utilizatorului o conexiune pe portul serial sau paralel, pe baz creia
primul utilizator care cere primete dreptul de folosi imprimanta. Calculatorul care nu mai are nevoie de
periferic trebuie s trimit o secven de caractere prin care spune de fapt Am terminat.
44
Aceste dispozitive, dei erau bune pentru imprimant i plotere (ele nc mai sunt folosite cteva
companii nc le mai ofer ), nu permiteau folosirea n comun a discurilor. De asemenea, necesitau o linie
dedicat ntre calculator i comutator. Aceasta devenea dificil de realizat cnd calculatoarele erau
rspndite pe o suprafa mare, i imposibil dac erau mai multe calculatoare.
Aici servesc discuri
Prima ncercare de a realiza ceea ce astzi numim reea local (LAN) a fost tehnologie, acum
nvechit, numim disc server. Un disc server era un calculator, prin care, printr-o tehnic de comunicaie
oarecare, era legat de un grup de calculatoare numit clieni. El rula un sistem de operare special care era
proiectat astfel nct s poat permit accesul mai multor clieni n acelai timp la disc i la imprimat:
acest sistem se numete sistem de operare pentru reea (Network Operating System sau NOS).
Funcionarea reelei
Aplicaia client/server
Primele aplicaii de reea erau n majoritate programe integrate. De exemplu, dac ofereau o baz
de date multiutilizator ele aveau i partea frontal (front-end) de interaciune cu utilizatorului i motorul
bazei de date (partea de program care lucra cu fiierele bazei de date) pe acelai PC. Singura parte care se
putea afla n reea, pe server, era baza de date.
n aceast configuraie, calculatorul client realiza toat prelucrarea datelor (citire, cutare a
nregistrrilor dorite ntre datele citite etc.). Aplicaiile acestea pot fi descrise ca avnd doar client.
Serverul era o simpl pomp de date: trimitea utilizatorului date din fiierele aflate pe disc sau le
primea i le stoca pe disc.
n ultimii ani au aprut un numr mare de sisteme de baz de date sofisticate care pun n reea
motorul de acces la baza de date care se afl n parte frontal (front-end) utilizatorul. Acestea se numesc
sisteme client/server.
O dat cu mbuntirea performanelor datorit eliminrii suprancrcrii reelei cu transferuri
mari de date, mai exist i avantajul faptului c serverul poate deservi mai muli clieni n acelai timp.
ntregul proces de sincronizare al accesului la baza de date, care trebuia realizat de clieni, este acum
realizat de server, ceea ce face aplicaiile mai simple i ntregul sistem mai eficient.
Bazele de date nu sunt singurele aplicaii care pot fi realizate n sistem client/server. Alte aplicaii
client/server includ servere de pot electronic, sisteme de vizualizare pe calculator a imaginilor i
urmrire serviciilor de reea.
Avantajele sistemelor client/server sunt urmtoarele:
securitate mai bun, deoarece accesul la datele din baza de date server este indirect.
Utilizatorii nu pot vedea fiierele de date dect dac li se d acest drept n mod explicit.
Performanele pot fi mbuntite uor, deoarece o mai bun proiectare a serverului
poate duce la o mai bun coordonare a utilizatorilor care doresc servicii n acelai timp i,
de aici, performane mai bune. n cazul severelor de baze de date prin reea pentru a gsi ce
i intereseaz; e suficient ca ele s trimit cereri ctre server, iar serverul le va trimite doar
rezultatele pe care le doresc.
Crete raportul calitate/pre. Clienii trebuie doar s aib suficient putere de calcul pentru a rula
partea frontal (front-end). (Cnd sunt necesare performane mai mari, serverul poate fi nlocuit cu un
calculator personal mai performant i, respectiv, mai scump).
Dezavantajele sistemelor client/server:
Complexitatea: nu este simplu, de obicei, s configurezi i s administrezi sisteme
client/server.
Necesiti: pentru a avea muli utilizatori, serverul din sistemele client/server are nevoie de un
calculator scump. Aplicaiile de pe server au tendina s devie mai mari i mai complexe i au nevoie de
mai mult memorie RAM.
Pre: performanele serverului scad o dat cu creterea numrului de utilizatori. Pentru a reface
performanele, serverul de baz de date trebuie s ruleze pe o main dedicat acelui server. Deci, acolo
45
unde cndva era un server dedicat general, care funciona i ca server de baz de date, acum avem un
server dedicat general i un server de baze de date dedicat, ceea ce duce cel puin la dublarea costului.
Tehnologii de grup
Tehnologiile de grup (groupware) sunt un set de tehnologii care au scopul de a mbunti
productivitatea a doi sau mai muli utilizatori care coopereaz n realitate unor obiective comune. Ideea
este ca o dat ce reeaua unete utilizatorii, munca i comunicrile cu privire la ea pot fi automatizate
pentru mbuntirea fluxului muncii i a oportunitilor. Teoretic, un grup de oameni care muncesc
mpreun ntr-o activitate comun sau pentru obiective comune poate fi mult mai eficient dect un grup
de oameni care muncesc independent. Deoarece calculatoarele mbuntesc dialogul ntre membrii
grupului i urmresc progresele lor, detaliile nu vor mai fi omise, iar desfurarea poate fi foarte uor de
urmrit.
Aceste idei au fost aplicate la procese cum sunt planificate i administrate proiectelor. Planificarea
n reea permite unui grup dintr-o reea s-i fac orare pe reea. Cnd vor s-i coordoneze activitile, de
exemplu s stabileasc o ntlnire, orarul grupului poate fi examinat i poate fi gsit momentul cnd toi
membrii sunt disponibili. Folosind pota electronic, acetea pot fi rugai s va edin (sau n
organizaiile mai autoritate li se ordon).
Alte caracteristici ale aplicaiei de grup:
Sisteme de informare (oferite n sisteme de pot electronic cum ar fi cc: Mail).
Baze de date folosite n comun.
Sisteme de conducere a proiectelor.
Servicii de bibliotec (pentru administrarea documentaiilor aparinnd unui grup).
Sisteme de control al versiunii (asemntoare cu serviciul de bibliotec, dar cu faciliti
de control al arhivrii i gsirii diverselor versiuni de fiier; aceste sisteme sunt de obicei
folosite pentru dezvoltarea programelor).
Una dintre cele mai ludate aplicaii ale tehnologiilor de grup, Lotus Notes, este un sistem de baze
de date cu pot electronic. Rolul lui Notes este de a rspndi informaiile deinute n bazele de date ale
organizaiilor, la un numr oarecare de utilizatori. Sistemul permite duplicarea i sincronizarea mai multor
copii de baze de date.
O alt direcie principal a aplicaiilor de grup este posibilitatea urmririi fluxului muncii. Ideea
este c grupurile de utilizatori care sunt ntr-o reea pot beneficia de automatizarea activitilor de rutin.
Mare parte a sistemelor care se ocup de fluxul muncii se bazeaz pe formulare. Ele primesc date de la o
persoan, pe care apoi le transmit, dac e posibil cu date suplimentarea din alte surse, celorlali membri.
Ele au mecanisme pentru contabilizarea i urmrirea tranzaciilor i raportarea stadiului muncii.
Obiectivele vor fi mai rar uitate sau amnate, deoarece calculatoarele sunt mai de ncredere dect
oamenii. Fluxul muncii este concept att de important n reele, nct multe dintre principalele companii
productoare de produse de reea au investit n companii care dezvolt tehnologii de baz pentru suportul
fluxului muncii.
Problema cu aplicaiile de grup este c e greu ca oamenii s se obinuiasc cu ea! (Poi s duci un
cal la ap, dar nu poi s-l faci s bea.).
NIVELE ALE LIMBAJELOR DE PROGRAMARE
Nivelul unui limbaj este apreciat prin poziia pe care o ocup pe scara constituit de limbajul
recunoscut de microprocesor (limbaj main) i limbajul natural al programatorului (limba romn, limba
englez ) .
Un limbaj de nivel sczut este foarte apropiat de main, el manipuleaz cu elemente de nivel
hardware, fizic, cum ar fi : registru, microprocesor, locaie de memorie, port de intrare / ieire etc.
Un limbaj de nivel nalt sau foarte nalt manipuleaz cu concepte apropiate de limbajul natural,
concepte de nivel logic, cum ar fi: colecie de date, nume de operaie (sort, writeln, open), variabile,
constante ( asemntoare ca neles cu cele din matematic).
46
Cu ajutorul unui limbaj de nivel nalt programatorul se face mult mai uor neles de ctre
calculator . Uneori o singur limie de program scris cu un astfel de limbaj poate echivala cu sute de linii
de program scrise n limbaj main. Deci din punct de vedere al reducerii timpului de realizare a unui
program i al siguranei n funcionare (absena erorilor de programare) este de preferat un limbaj de nivel
ct mai ridicat (nalt sau foarte nalt). n schimb, pe msur ce limbajul are un nivel mai ridicat execuia
programului conceput cu ajutorul su va fi mai lent, dect a unui program ce realizeaz aceleai operaii
dar este scris n limbaj de asamblare.
O alt diferen esenial ntre cele dou tipuri de limbaje o reprezint portabilitatea, adic
posibilitatea transferrii programelor pe un alt tip de main dect cea pe care au fost construite. Din acest
punct de vedere limbajul de asamblare este neportabil deoarece el este specific microprocesorului.
Programele realizate pe un tip de main trebuie rescrise integral pentru noul tip de main , folosind un
nou set de instruciuni care deobicei difer foarte mult. Lucrurile stau altfel cu programele concepute cu
ajutorul unui limbaj de nivel nalt, deoarece acestea sunt detaate de main. ntre un astfel de program i
calculator se interpune compilatorul (sau interpretorul) care rezolv corect transformarea fiierului-surs
n fiier - executabil.
Limbaje procedurale neprocedurale
Cele dou tipuri de limbaje, procedurale i neprocedurale, se difereniaz prin nivelul de
organizare (structurare) a unui program. Limbajele neprocedurale sunt concepute pentru a gndi un
program la nivel de instruciune, pe cnd cele procedurale, oblig programatorul s conceap programe la
nivel de bloc. ntr-un limbaj procedural (numit i limbaj structurat) programele sunt scrise instruciune cu
instruciune, dar ele sunt organizate logic n blocuri (grupuri de instruciuni) ce realizeaz o aciune bine
determinat. n general un bloc are un punct de intrare i un punct de ieire nu mai multe.
Un limbaj procedural ofer posibilitatea utilizrii unui nivel ridicat de concepere a unui program i
duce la realizarea de programe coerente i protejate la erori. Prin contrast, limbajele neprocedurale nu
favorizeaz programatorul n a se desprinde de nivelul instruciune i duc deseori la programe greu de
controlat mai ales n cazul programelor de dimensiuni mari.
Limbajele neprocedurale sunt nc preferate de unii utilizatori datorit timpului foarte scurt ct
decurge nvarea i utlizarea lor.
Limbaje orientate
Din punctul de vedere al aplicabilitii unui limbaj, limbajele pot fi orientate pe o anumit
problem sau concepute pentru soluionarea oricrui tip de problem limbaje de uz general sau altfel
spus, neorientate pe o problem.
Limbajele orientate prezint un grad nalt de specificitate pe cnd un limbaj neorientat reprezint
un cadru general ce permite introducerea de ctre utilizator a conceptelor i prelucrrilor dorite.
Deci, diferena esenial dintre cele dou tipuri de limbaje o constitue nivelul conceptual definit.
Cele specializate posed deja integral suportul necesar i permit programatorului s se concentreze la
ansamblul problemei, pe cnd cele nespecializate las n sarcina programatorului manevrarea nivelelor
inferioare ale problemei.
Limbaje concurente
Un limbaj concurent permite definirea de procese (prelucrri) paralele, execuia sa fiind ramificat
la un anumit moment de timp. Prin contrast limbajele neconcurente (majoritatea limbajelor) au o
desfurare liniar, fiind activ un singur proces la un moment dat. Procesele concurente presupun n mod
obligatoriu un sistem multi-tasking ce poate gestiona mai multe sarcini la un moment dat.
Limbaje de nivel sczut
Aceast categorie de limbaje are un reprezentant autoritar i anume: limbajul de asamblare.
Diferenierile care se pot face pentru limbajele de nivel sczut sunt urmtoarele:
47
indiscutabil cu un pas naintea celorlali. Concurena din domeniul economic poate fi numit pe bun
dreptate o btlie informaional.
Un sistem de gestionare a bazelor de date (S.G.B.D.) de tip clasic opereaz cu urmtorii termeni
fundamentali:
cmp o locaie n care se poate memora o informaie bine determinat;
nregistrare mai multe cmpuri alctuiesc mpreun o nregistrare;
baza de date colecie de nregistrri.
Deci, datele sunt gestionate prin intermediul unei structuri, organizat ierarhic, la un nivel de
organizare logic.
Tendina modern n exploatarea bazelor de date const n deplasarea interesului ctre bazele de
date relaionale. Diferena esenial const n definirea unui nivel logic suplimentar ntre datele
gestionate. Acestea nu mai sunt privite ca simple fie izolate ntre ele ci pot fi analizate pe baza legturilor
(relaiilor) ce exist ntre ele.
Noiunile cu care opereaz un S.G.B.D. relaional sunt urmtoarele:
tabel structur fundamental de depozitare a datelor;
linie n tabel echivalentul unei nregistrri clasice;
coloan n tabel echivalentul unui cmp de tip clasic;
baz de date o colecie de tabele, conectate prin valorile anumitor coloane.
Aceast nou concepie permite definirea de structuri 1:n. O nregistrare poate conine n valori
pentru un cmp anumit nu una singur ca n cazul clasic. Structurile de tip 1:n pot fi rezolvate i cu
ajutorul unui S.G.B.D. clasic, dar ntreaga gestiune a operaiilor revine programatorului pe cnd un mediu
relaional furnizeaz din start servicii speciale.
Spre deosebire de S.G.B.D.-urile clasice, un mediu relaional presupune ca cerin minimal
posibilitatea manipulrii datelor prin intermediul conexiunilor logice stabilite. Pentru aceasta exist
definit (i impus ca standard unanim recunoscut) limbajul de interogare SQL (Structured Query Language
limbaj de cereri structurate). Prin intermediul su sunt permise urmtoarele operaii:
regsire date (conexate logic) ce ndeplinesc o anumit condiie;
definire ordine de returnare a datelor;
redefinire conectri logice ale datelor;
exploatare;
programare.
Avantajele unui S.G.B.D. clasic sunt:
simplitate n manevrare; deci efort de studiu redus;
pot funciona pe un sistem de calcul ce nu implic resurse speciale, ci doar spaiu de
stocare extern suficient pentru problema dat;
pre de cost redus fa de cele relaionale.
Avantajele unui S.G.B.D. relaional sunt:
nivel logic superior (corelaii, structuri 1:n ),
prelucrri (regsiri) de date cu un nalt nivel de complexitate;
nivel superior de portabilitate a aplicaiilor, datelor.
S.G.B.D. -uri clasice
dBASE III
Cel mai rspndit sistem de gestiune a bazelor de date este dBASE, n diversele lui versiuni. El
poate fi considerat un BASIC al bazelor de date. La momentul apariiei a constituit o adevrat
revoluie n domeniul S.G.B.D.-urilor.
Meritele sale principale care l-au impus ateniei utilizatorilor i programatorilor sunt :
foarte simplu de utilizat;
limbaj de nivel foarte nalt , simplu de nvat;
50
54
CAPITOLUL VII
LIMBAJUL VISUAL BASIC
7.1 Programarea aplicaiilor Windows
Pentru realizarea unei aplicaii pot fi avute n vedere dou tehnologii de programare i anume:
programare procedural
programare orientat spre obiecte i dirijat de evenimente.
n programarea procedural, o aplicaie este constituit din unul sau mai multe programe care se
vor executa ntr-o anumit ordine, fiecare program fiind constituit dintr-o secven de instrucuni scrise
ntr-un limbaj de programare.
Acesta era modul clasic de realizare a aplicaiilor i sistemelor informatice i are o serie de
dezavantaje printre care: productivitate sczut n realizarea programelor, efort mare pentru realizarea
programelor i mai ales a interfeelor etc.
Apariia tehnologiei orientate obiect, a mediilor visuale de programare i a sistemului de operare
Windows a condus la apariia i dezvoltarea unei noi tehnologii de programare a aplicaiilor windows i
anume programarea orientat pe obiecte i dirijat de evenimente, tehnologie ce va fi prezentat n cele ce
urmeaz n cadrul limbajului Visual Basic.
O aplicaie Windows afieaz unul sau mai multe ecrane care conin obiecte cu care va
interaciona utilizatorul pentru a controla evoluia programului. ntr-un mediu de programare vizual,
obiectele principale sunt formele i controalele desenate n forme (form = o fereastr) Aceste obiecte pot
fi create prin selecie i depunere folosind barele de instrumente ale mediului respectiv.
Spre exemplu, bara cu instrumente Visual Basic permite crearea unei varieti de obiecte printre
care: forme, butoane, casete cu list, casete derulante combinate, casete de validare, butoane radio
(butoane de opiune), etc. Fiecare din aceste obiecte are un comportament predefinit. Spre exemplu cnd
se execut click pe un buton acesta trece n poziia apsat i apoi revine n poziia normal. Pentru a
schimba comportamentul obiectului acestuia trebuie s-i ataai cod de program (instruciuni)
corespunztor, cod ce se va executa atunci cnd are loc un anumit eveniment (spre exemplu n cazul
butonului evenimentul este click).
Evenimentele se produc ca urmare a unei aciuni a utilizatorului (ex. evenimentul click
corespunde apsrii butonului stng al mouse-ului pe obiectul respectiv), sau n urma execuiei codului
programului, sau pot fi declanate de ctre sistem.
Majoritatea obiectelor vor rspunde unui anumit numr de evenimente generate de ctre utilizator
printre care click-uri, dublu click-uri, apsri de taste sau trageri i eliberri ale obiectului. Limbajul
Visual Basic pune la dispoziia utilizatorului un mediu de dezvoltare care permite crearea de programe
orientate spre obiecte i conduse de evenimente. Pentru lucrul cu obiecte conduse de evenimente se
parcurg urmtoarele etape:
se creeaz o nou form creia i se d un nume;
se deseneaz i se denumesc obiectele ce urmeaz a fi afiate n forma respectiv;
se ataeaz fiecrui obiect codul ce va fi executat ca rspuns la evenimente generate de
utilizator sau de sistem.
Va rezulta o interfa grafic cu care interacioneaz utilizatorul pentru a controla evoluia
programului. Rezumnd putem spune c n programarea orientat spre obiecte i dirijat de evenimente,
obiectele au un comportament predefinit care poate fi modificat de utilizator prin ataare de cod
corespunztor i aceste obiecte rspund la evenimente declanate fie ca urmare a aciunii utilizatorului
asupra obiectelor, fie ca urmare a execuiei codului ataat, fie declanate de sistem.
55
Prefix
Exemplu
Form
Buton de comand
Caset de text
Frm
cmd, btn
Txt
frmForma1
cmdButon, btnOK
txtCaseta1
56
Obiect
Prefix
Bare de derulare
- orizontal
- vertical
Meniu
Caset de validare
Caset cu lista
Cadru
Imagine
Buton de opiune (radio)
hsb
vsb
Mnu
Chk
Lst
Fra
Img
Opt
Exemplu
mnuMeniuPrinc
optBO1
Operatori
n formarea expresiilor de diverse tipuri, operatorii sunt cei utilizai aproape general n limbajele
de programare de nivel nalt. Pentru fixarea termenilor i notaiilor sunt totui prezentai, pe categorii,
nsoii, acolo unde este cazul de scurte explicaii.
Operatori aritmetici
Operator
Semnificaie
Observaii
Ridicarea la
putere
nmulirea
mprirea
mprirea
ntreag
Mod
Restul
mpririi
Adunarea
numeric sau
concatenarea
irurilor
Scderea sau
inversarea
semnului
Operatori de comparare
58
Relaiile care exist ntre diferite tipuri de entiti se pot evidenia prin comparaii avnd una
dintre formele urmtoare:
result = expression1 comparisonoperator expression2
result = object1 Is object2
result = string Like pattern
unde
result este o variabil numeric
expression este o expresie oarecare
comparisonoperator este un operator relaional
object este un nume de obiect
string este o expresie ir oarecare
pattern este o expresie String sau un domeniu de caractere.
Operatorii de comparare sunt cei uzuali: < (mai mic), <= (mai mic sau egal), > (mai mare), >=
(mai mare sau egal), = (egal), <> (diferit, neegal).
Rezultatul este True (dac este adevrat relaia), False (dac relaia este neadevrat), Null (dac
cel puin un operand este Null).
Operatorul Is produce True dac variabilele se refer la acelai obiect i False n caz contrar.
Operatorul Like compar dou iruri cu observaia c al doilea tremen este un ablon. Prin urmare
rezultatul este True dac primul ir operand este format dup ablon, False n caz contrar. Atunci cnd un
operand este Null, rezultatul este tot Null.
Comportarea operatorului Like depinde de instruciunea Option Compare, care poate fi:
Option Compare Binary, ordinea este cea a reprezentrii interne binare, determinat n
Windows de codul de pagin.
Option Compare Text, compararea este insenzitiv la capitalizarea textului, ordinea este
determinat de setrile locale ale sistemului.
Construcia ablonului poate cuprinde caractere wildcard, liste de caractere, domenii de caractere:
un caracter oarecare
oricte caractere (chiar nici unul)
# o cifr oarecare (09).
[charlist] oricare dintre caracterele enumerate n list, un domeniu de litere poate fi dat
prin utilizarea cratimei.
[!charlist] orice caracter care nu este n list
Observaie. Pentru a utiliza n ablon caracterele speciale cu valoare de wildcard se vor utiliza construcii
de tip list: [[], [?] etc. Paranteza dreapta va fi indicat singur: ].
Pentru alte observaii utile se va studia Help Like operator.
Operatori de concatenare
Pentru combinarea irurilor de caractere se pot utiliza operatorii & i +.
n sintaxa
expression1 & expression2
unde operanzii sunt expresii oarecare, rezultatul este:
de tip String, dac ambii operanzi sunt String
de tip Variant(String) n celelalte cazuri
Null, dac ambii operanzi sunt Null.
nainte de concatenare, operanzii care nu sunt iruri se convertesc la Variant(String). Expresiile
Null sau Empty sunt tratate ca iruri de lungime zero ("").
59
Operatori logici
Pentru operaiile logice sunt utilizai urmtorii operatori, uzuali n programare.
Operator
Semnificaie
Observaii
And
conjuncia logic Null cu False d False, Null cu True sau cu Null d Null.
Operatorul And realizeaz i operaia de conjuncie bit cu
bit pentru expresii numerice.
Eqv
echivalena logic Dac o expresie este Null, rezultatul este Null. Eqv
realizeaz i compararea bit cu bit a dou expresii
numerice, poziionnd cifrele binare ale rezultatului dup
regulile de calcul ale echivalenei logice: 0 Eqv 0 este 1
etc.
Imp
implicaia logic True Imp Null este Null, False Imp * este True, Null Imp
True este True, Null Imp False (sau Null) este Null.
Operatorul Imp realizeaz i compararea bit cu bit a dou
expresii numerice, poziionnd cifrele binare ale
rezultatului dup regulile de calcul ale implicaiei logice: 1
Imp 0 este 0, n rest rezultatul este 1.
Not
Or
Xor
negaia logic
Not Null este Null. Prin operatorul Not se poate inversa bit
cu bit valorile unei variabile, poziionndu-se
corespunztor un rezultat numeric.
disjuncia logic Null Or True este True, Null cu False (sau Null) este Null.
Operatorul Or realizeaz i o comparaie bit cu bit a dou
expresii numerice poziionnd biii corespunztori ai
rezultatului dup regulile lui Or logic.
disjuncia
exclusiv
Instruciuni de atribuire
Atribuirea se poate efectua prin instruciunea Let (pentru valori atribuite variabilelor i
proprietilor), Set (pentru atribuirea de obiecte la o variabil de tip obiect), Lset i Rset (pentru atribuiri
speciale de iruri sau tipuri definite de utilizator).
Instruciunea Let
Atribuie valoarea unei expresii la o variabil sau proprietate.
[Let] varname = expression
unde varname este nume de variabil sau de proprietate.
Este de remarcat forma posibil (i de fapt general utilizat) fr cuvntul Let.
Observaii. Valoarea expresiei trebuie s fie compatibil ca tip cu variabila (sau proprietatea): valori
numerice nu pot fi atribuite variabilelor de tip String i nici reciproc.
Variabilele Variant pot primi valori numerice sau String, reciproc nu este valabil dect dac
valoarea expresiei Variant poate fi interpretat compatibil cu tipul variabilei: orice Variant poate fi
atribuit unei variabile de tip String (cu excepia Null), doar Variant care poate fi interpretat nuric poate fi
atribuit unei variabile de tip numeric.
La atribuirea valorilor numerice pot avea loc conversii la tipul numeric al variabilei.
Atribuirea valorilor de tip utilizator poate fi efectuat doar dac ambii termeni au acelai tip
definit. Pentru alte situaii se va utiliza instruciunea Lset.
Nu se poate utiliza Let (cu sau fr cuvntul Let) pentru legarea de obiecte la variabile obiect. Se
va utiliza n aceast situaie instruciunea Set.
60
Instruciunea LSet
Copie, cu aliniere la stnga, un ir de caractere (valoarea expresiei din dreapta) ntr-o variabila de
tip String. Deoarece copierea este binar, poate fi utilizat pentru atribuiri ntre tipuri utilizator diferite
(rezultatul este impredictibil deoarece nu se face nici o verificare de tipuri/componente ale valorilor de tip
record). Sintaxa este
LSet stringvar = string
LSet varname1 = varname2
unde
stringvar, string reprezint variabila de tip String i expresia de acelai tip implicate ntr-o
atribuire de iruri.
varname1, varname2 sunt denumiri de variabile, de tipuri definite de utilizator (vezi
instruciunea Type) diferite. Zona de memorie alocat celei de a doua variabile este copiat
(aliniat la stnga) n zona de memorie a primei variabile.
Caracterele care rmn neocupate se completeaz cu spaii, iar dac zona de unde se copie este
mai mare, caracterele din dreapta se pierd (sunt trunchiate).
Instruciunea LSet
Copie, cu aliniere la dreapta, un ir de caractere (valoarea expresiei din dreapta) ntr-o variabila de
tip String. Sintaxa este
RSet stringvar = string
Caracterele rmase neocupate n variabil sunt completate cu spaii. Instruciunea RSet nu se
poate utiliza (analog lui LSet) pentru tipuri definite de utilizator.
Instruciuni executabile
Execuia unui program are loc, n lipsa oricrui control, instruciune cu instruciune, de la stnga
la dreapta i de sus n jos. Acest sens poate fi modificat, ntr-o oarecare msur, prin ordinea de
preceden a operaiilor n evaluarea expresiilor. Este evident c o asemenea structur simpl nu poate
cuprinde toate aspectele programrii i din acest motiv necesitatea structurilor de control a fluxului
execuiei. Unele instruciuni au fost pstrate doar din motive de compatibilitate cu versiunile iniiale ale
limbajului, n locul lor fiind preferate structuri mai evoluate sau similare altor limbaje de programare.
Instruciuni de transfer (GoSubReturn, GoTo, OnError, OnGoSub, OnGoTo)
Aceast categorie cuprinde instruciunile prin care controlul execuiei este transferat la o alt
instruciune din procedur. n general, utilizarea acestor comenzi nu produce programe foarte structurate
(n sensul programrii structurate) i prin urmare, pentru o mai mare claritate a codului, pot fi nlocuite cu
alte structuri de programare.
GoSubReturn
n cadrul unei proceduri un grup de instruciuni poate fi organizat ca o subrutin (similar unei
proceduri on-line, nenumite) identificat prin linia de nceput. Transferul controlului la acest grup de
instruciuni i revenirea la locul apelului se poate efectua prin GoSubReturn cu sintaxa
GoSub line
...
line
...
Return
unde line este o etichet de linie sau un numr de linie din aceeai procedur.
Pot exista mai multe instruciuni Return, prima executat produce saltul la instruciunea care
urmeaz celei mai recente instruciuni GoSub executate.
61
GoTo
Realizeaz tranferul controlului execuiei la o linie din aceeai procedur.
GoTo line
unde line este o etichet de linie sau un numr de linie din aceeai procedur.
On Error
Permite controlul erorilor prin transferul controlului la rutine de tratare.
Observaie. Este prezentat n seciunea dedicat controlului erorilor.
OnGoSub, OnGoTo
Permit o ramificare multipl, dup valoarea unei expresii. Se recomand, pentru claritatea codului,
utilizarea structurii Select Case n locul acestor structuri.
On expression GoSub destinationlist
On expression GoTo destinationlist
unde
expression este o expresie numeric avnd valoare ntreag (dup o eventual rotunjire)
ntre 0 i 255 inclusiv.
destinationlist este o list de etichete de linii sau numere de linii, separate prin virgule
(elementele pot fi de ambele categorii), din aceeai procedur cu instruciunea.
Dac valoarea expresiei este negativ sau mai mare dect 255 se produce o eroare.
Dac valoarea expresiei, fie ea k, este n domeniul rangurilor listei, atunci se transfer controlul la
linia identificat de al k-lea element al listei.
Dac valoarea expresiei este 0 sau mai mare dect numrul de elemente din list, transferul se
efectueaz la linia care urmeaz instruciunea On...GoSub sau On...GoTo.
Instruciuni de terminare sau oprire a programului (DoEvents, End, Exit, Stop)
Terminarea execuiei programului sau oprirea temporar (pauza) se pot realiza prin instruciunile
enumerate aici.
DoEvents
Dei nu este o instruciune VBA ci este o funcie, includerea ei este natural prin aceea c permite
cedarea controlului ctre sistemul de operare, care poate astfel s funcioneze n regim de multitasking.
Aciunea poate fi realizat i prin alte tehnici (de exemplu utilizarea unui Timer etc.). Sintaxa este
DoEvents( )
Funcia returneaz, n general, valoarea 0.
Controlul este redat programului dup ce sistemul de operare a terminat procesarea evenimentelor
din coada de evenimente, ca i procesarea tuturor caracterelor din coada SendKeys.
Observaie. Pentru alte observaii se va studia documentaia comenzii DoEvents.
End
Termin execuia unei proceduri (sub forma prezentat aici) sau indic sfritul codului unei
structuri de tip bloc (cum ar fi End Function, End If etc., prezentate la structurile respective).
Sintaxa, n ipostaza opririi execuiei, este:
End
Prin aceast instruciune, care poate fi plasat oriunde n program, execuia este terminat imediat,
fr a se mai executa eventualele instruciuni scrise pentru tratarea unor evenimente specifice sfritului
de program (Unload, Terminate etc.).
Fiierele deschise prin Open sunt nchise i toate variabilele sunt eliberate. Obiectele create din
modulele clas sunt distruse, iar referinele din alte aplicaii la asemenea obiecte sunt invalidate. Memoria
este eliberat.
62
Exit
Prin instruciunea Exit, sub una din multiplele ei forme, se ntrerupe o ramur de execuie (cum ar
fi o procedur, o structur iterativ etc.) pentru a se continua nivelul apelant. Sintaxa este
Exit Do
Exit For
Exit Function
Exit Property
Exit Sub
i efectele sunt prezentate la structurile respective. Nu trebuie confundat cu instruciunea End.
Stop
Efectul instruciunii este dependent de modul de execuiei a programului. Dac se execut varianta
compilat a programului (fiierul .exe) atunci instruciunea este similar instruciunii End (suspend
execuia i nchide fiierele deschise). Dac execuia este din mediul VBA, atunci se suspend execuia
programului, dar nu se nchid fiierele deschise i nu se terge valoarea variabilelor. Execuia poate fi
reluat din punctul de suspendare.
Stop
Instruciunea este similar introducerii unui punct de oprire (Breakpoint) n codul surs.
Structuri iterative (Do...Loop, For...Next, For Each...Next, While...Wend, With)
Prin intermediul construciilor de tip bloc prezentate n aceast seciune se poate repeta, n mod
controlat, un grup de instruciuni. n cazul unui numr nedefinit de repetiii, condiia de oprire poate fi
testat la nceputul sau la sfritul unui ciclu, prin alegerea structurii adecvate.
DoLoop
Se vor utiliza structuri DoLoop pentru a executa un grup de instruciuni de un numr de ori
nedefinit aprioric. Dac se cunoate numrul de cicluri, se va utiliza structura ForNext.
nainte de continuare se va testa o condiie (despre care se presupune c poate fi modificat n
instruciunile executate). Diferitele variante posibile pentru DoLoop difer dup momentul evalurii
condiiei i decizia luat.
Do [{While | Until} condition]
[statements]
[Exit Do]
[statements]
Loop
sau
Do
[statements]
[Exit Do]
[statements]
Loop [{While | Until} condition]
unde
condition este o expresie care valoare de adevr True sau False. O condiie care este Null
se consider False.
statements sunt instruciounile care se repet atta timp (while) sau pn cnd (until)
condiia devine True.
Dac decizia este de a nu continua ciclarea, atunci se va executa prima instruciune care urmeaz
ntregii structuri (deci de dup linia care ncepe cu Loop).
Se poate abandona ciclarea oriunde n corpul structurii prin utilizarea comenzii Exit Do (cu
aceast sintax). Dac apare o comand Exit Do se poate omite chiar i condiia din enun ntruct
execuia se va termina prin aceast decizie.
63
Structurile Do pot fi inserate (dar complet) unele n altele. O terminare (prin orice metod) a unei
bucle transfer controlul la nivelul Do imediat superior.
Execuia structurilor este explicat n tabelul urmtor
Do WhileLoop
Do UntilLoop
DoLoop While
DoLoop Until
ForNext
Atunci cnd se cunoate numrul de repetri ale unui bloc de instruciuni, se va folosi structura
ForNext. Structura utilizeaz o variabil contor, a crei valoare se modific la fiecare ciclu, oprirea
fiind atunci cnd se atinge o valoare specificat. Sintaxa este:
For counter = start To end [Step step]
[statements]
[Exit For]
[statements]
Next [counter]
unde
counter este variabila contor (numr repetrile), de tip numeric. Nu poate fi de tip
Boolean sau element de tablou.
start este valoarea iniial a contorului.
end este valoarea final a contorului.
step este cantitatea care se adun la contor la fiecare pas. n cazul n care nu se specific
este implicit 1. Poate fi i negativ.
statements sunt instruciunile care se repet. Dac nu se specific, atunci singura aciune
este cea de modificare a contorului de un numr specificat de ori.
Aciunea este dictat de pasul de incrementare i relaia dintre valoarea iniial i cea final.
Instruciunile din corpul structurii se execut dac
counter <= end pentru step >= 0 sau
counter >= end pentru step < 0.
Dup ce toate instruciunile s-au executat, valoarea step este adugat la valoarea contorului i
instruciunile se execut din nou dup acelai test ca i prima dat, sau bucla ForNext este terminat i
se execut prima instruciune de dup linia Next.
Specificarea numelui contorului n linia Next poate clarifica textul surs, mai ales n cazul cnd
exist structuri ForNext mbricate.
Corpul unei bucle ForNext poate include (complet) o alt structur ForNext. n asemenea
situaii, structurile mbricate trebuie s aib variabile contor diferite.
64
Instruciunile Exit For pot fi plasate oriunde n corpul unei bucle i provoac abandonarea ciclrii.
Controlul execuiei se transfer la prima instruciune de dup linia Next.
For EachNext
Similar structurii ForNext, structura For EachNext repet un grup de instruciuni pentru
fiecare element dintr-o colecie de obiecte sau dintr-un tablou (cu excepia celor de un tip utilizator). Este
util atunci cnd nu se cunoate numrul de elemente sau dac se modific, n timpul execuiei, coninutul
coleciei.
Sintaxa este:
For Each element In group
[statements]
[Exit For]
[statements]
Next [element]
unde
element este variabila utilizat pentru parcurgerea elementelor. Dac se parcurge o colecie
de obiecte, atunci element poate fi Variant, o variabil generic de tip Object, sau o
variabil obiect specific pentru biblioteca de obiecte referit. Pentru parcurgerea unui
tablou, element poate fi doar o variabil de tip Variant.
group este numele coleciei de obiecte sau al tabloului.
statements este grupul de istruciuni executate pentru fiecare element.
Execuia unei structuri For EachNext este:
Se definete element ca numind primul element din grup (dac nu exist nici un element, se
transfer controlul la prima instruciune de dup Next se prsete bucla fr executarea
instruciunilor).
Se execut instruciunile din corpul buclei For.
Se testeaz dac element este ultimul element din grup. Dac rspunsul este afirmatif, se
prsete bucla.
Se definete element ca numind urmtorul element din grup.
Se repet paii 2 pn la 4.
Instruciunile Exit For sunt explicate la ForNext.
Buclele ForEach...Next pot fi mbricate cu condiia ca elementele utilizate la iterare s fie diferite.
Observaie. Pentru tergerea tuturor obiectelor dintr-o colecie se va utiliza ForNext i nu For Each
Next. Se va utiliza ca numr de obiecte colecie.Count.
WhileWend
Execut un grup de instruciuni att timp ct este adevrat o condiie. Sintaxa
While condition
[statements]
Wend
Este recomandat s se utilizeze o structur DoLoop n locul acestei structuri.
With
Programarea orientat pe obiecte produce, datorit calificrilor succesive, construcii foarte
complexe atunci cnd se numesc proprietile unui obiect. n cazul modificrilor succesive ale mai multor
proprieti ale aceluiai obiect, repetarea zonei de calificare poate produce erori de scriere i conduce la
un text greu de citit. Codul este simplificat prin utilizarea structurii WithEnd With. O asemenea
structur execut o serie de instruciuni pentru un obiect sau pentru o variabil de tip utilizator. Sintaxa
este:
With object
[statements]
65
End With
unde
object este numele unui obiect sau a unui tip definit de utilizator
statements sunt instruciunile care se execut pentru entitatea precizat.
Permind omiterea recalificrilor din referinele la obiectul precizat, orice construcie de tipul
".nume" este interpretat n instruciunile structurii drept "object.nume".
ntr-un bloc With nu se poate schimba obiectul procesat.
La plasarea unui bloc With n interiorul altui bloc With, obiectul extern este mascat complet, deci
calificrile eventuale la acest obiect vor fi efectuate.
Nu se recomand saltul n i dintr-un bloc With.
Structuri de decizie (IfThenElse, Select Case)
Ramificarea firului execuiei dup rezultatul verificrii unei condiii este o necesitate frecvent n
orice implementare.
Pe lng structurile prezentate, se pot utiliza trei funcii care realizeaz alegeri n mod liniarizat
(pe o linie de cod): Choose(), Iif(), Switch().
IfThenElse
O asemenea structur, ntlnit de altfel n toate limbajele de programare, execut un grup de
instruciuni ca rspuns la ndeplinirea unei condiii (compus sau nu din mai multe condiii testate
secvenial). Sintaxa permite o mare varietate de forme:
If condition Then [statements] [Else elsestatements]
sau
If condition Then
[statements]
[ElseIf condition-n Then
[elseifstatements] ...
[Else
[elsestatements]]
End If
unde
condition are una din formele: expresie numeric sau ir care se poate evalua True sau
False (Null este interpretat False);
expresie de forma TypeOf objectname Is objecttype, evaluat True dac objectname este
de tipul obiect specificat n objecttype.
statements, elsestatements, elseifstatements sunt blocurile de instruciuni executate atunci
cnd condiiile corespunztoare sunt True.
La utilizarea primei forme, fr clauza Else, este posibil s se scrie mai multe instruciuni,
separate de ":", pe aceeai linie.
Verificarea condiiilor implic evaluarea tuturor subexpresiilor, chiar dac prin jocul operanzilor i
operatorilor rezultatul poate fi precizat mai nainte (de exemplu OR cu primul operand True).
Select Case
Instruciunea Select Case se poate utiliza n locul unor instruciuni ElseIf multiple (dintr-o
structur IfThenElseIf) atunci cnd se compar aceeai expresie cu mai multe valori, diferite ntre
ele. Instruciunea Select Case furnizeaz, prin urmare, un sistem de luare a deciziilor similar instruciunii
IfThenElseIf. Totui, Select Case produce un un cod mai eficient i mai inteligibil. Sintaxa este:
Select Case testexpression
[Case expressionlist-n
[statements-n]] ...
[Case Else
[elsestatements]]
66
End Select
unde
testexpression este o expresie numeric sau ir.
expressionlist-n este lista, separat prin virgule, a uneia sau mai multe expresii de forma:
expression.
expression To expression. Cuvntul To introduce un interval de valori, valoarea minim
fiind prima specificat.
Is comparisonoperator expression. Se va utiliza Is cu operatori de comparare (exceptnd
Is i Like) pentru a specifica un domeniu de valori.
statements-n reprezint una sau mai multe instruciuni care se vor executa dac
testexpression este egal cu un element din expressionlist-n.
elsestatements reprezint una sau mai multe instruciuni care se vor executa dac
testexpression nu este egal cu nici un element din listele liniilor Case.
Dac testexpression se potrivete cu un element dintr-o list Case, se vor executa instruciunile
care urmeaz aceast clauz Case pn la urmtoarea clauz Case, sau pn la End Select. Control
execuiei trece apoi la instruciunea care urmeaz liniei finale End Select. Rezult c dac testexpression
se regsete n mai multe liste, doar prima potrivire este considerat.
Clauza Case Else are semnificaia uzual "altfel, n rest, n caz contrar etc.", adic introduce
instruciunile care se execut atunci cnd expresia de test nu se potrivete nici unui element din listele
clauzelor Else. Dac aceasta este situaia i nu este specificat o clauz Case Else, atunci execuia
urmeaz cu prima instruciune de dup End Select.
Instruciunile Select Case pot fi scufundate unele n altele, structurile interioare fiind complete
(fiecare structur are End Select propriu, includerea este complet).
Apeluri de proceduri i programe
n aceast seciune se prezint doar funcia Shell(), deoarece despre proceduri i apelul lor s-a
discutat n capitolul 1.
Funcia Shell()
Execut un program executabil i returneaz un Variant(Double) reprezentnd ID-ul de task al
programului n caz de succes; n caz contrar returneaz zero. Sintaxa este
Shell(pathname[,windowstyle])
unde
pathname este Variant (String). Conine numele programului care se execut, argumentele
necesare i poate da calea complet (dac este nevoie).
windowstyle este Variant (Integer) i precizeaz stilul ferestrei n care se va executa
programul (implicit este minimizat, cu focus).
Valorile posibile pentru argumentul windowstyle sunt
Constanta numit Valoarea
Semnificaia
VbHide
0
Fereastra este ascuns iar focus-ul este pe fereastra ascuns.
VbNormalFocus
1
Fereastra are focus-ul i este dimensionat i poziionat normal.
VbMinimizedFocus
2
Fereastra este afiat ca o icoan (minimizat) dar are focus-ul.
VbMaximizedFocus
3
Fereastr maximizat, cu focus.
VbNormalNoFocus
4
Fereastra este normal (restaurat la mrimea i poziia cea mai recent)
dar nu are focus-ul. Fereastra activ curent i pstreaz focus-ul.
VbMinimizedNoFocus
6
Fereastr minimizat, fr focus. Fereastra activ curent i pstreaz
focus-ul.
67
Dac funcia Shell nu poate porni programul specificat se va semnala eroare. Programul pornit
prin Shell se execut asincron, deci nu exist certitudinea c acest program se termin nainte de execuia
instruciunilor care urmeaz liniei Shell.
68
CAPITOLUL VIII
REGULI IMPORTANTE PRIVIND ALEGEREA UNUI LIMBAJ DE PROGRAMARE
Prezentm n continuare mai multe reguli importante, majoritatea dintre ele prezente i explicate
n seciunile anterioare.
1. Definete complet problema.
Aceast indicaie, foarte important n activitatea de programare, pare fr sens pentru unii cititori.
Dar nu se poate rezolva o problem dac nu se cunoate aceast problem. Specificarea corect i
complet a problemei nu este o sarcin trivial, ci una foarte important i adeseori chiar dificil.
Programul trebuie s respecte aceast specificaie, s fie construit avnd tot timpul n fa aceast
specificaie, s i se demonstreze corectitudinea n raport cu aceast specificaie, s fie testat i validat
innd seama de aceast specificaie.
2. Gndete mai nti, programeaz pe urm.
ncepnd cu scrierea specificaiilor problemei, trebuie pus n prim plan gndirea. Este specificaia
problemei corect? ntre metodele de rezolvare posibile, care ar fi cea mai potrivit scopului urmrit? n
paralel cu proiectarea algoritmului demonstreaz corectitudinea lui. Verific corectitudinea fiecrui pas
nainte de a merge mai departe.
3. Nu folosi variabile neiniializate.
Este vorba de prezena unei variabile ntr-o expresie fr ca n prealabil aceast variabil s fi
primit valoare. Este o eroare foarte frecvent a programatorilor nceptori (dar nu numai a lor). Destule
compilatoare permit folosirea variabilelor neiniializate, neverificnd dac o variabil a fost iniializat
naintea folosirii ei. Alte compilatoare iniializeaz automat variabilele numerice cu valoarea zero. Cu
toate acestea nu e bine s ne bazm pe o asemenea iniializare ci s atribuim singuri valorile iniiale
corespunztoare variabilelor. Programul realizat trebuie s fie portabil, s nu se bazeze pe specificul unui
anumit compilator.
4. Verific valoarea variabilei imediat dup obinerea acesteia.
Dac o variabil ntreag trebuie s ia valori ntr-un subdomeniu c1..c2 verific respectarea acestei
proprieti. Orice nclcare a ei indic o eroare care trebuie nlturat. Valoarea variabilei poate fi
calculat sau introdus de utilizator. n primul caz, verificarea trebuie fcut dup calcul, n al doilea caz
se recomand ca verificarea s urmeze imediat dup citirea valorii respectivei variabile.
5. Cunoate i folosete metodele de programare.
Este vorba de programarea Top-Down, Rafinarea n pai succesivi, Divide et impera [Gries85]),
Bottom-up i mixt, programarea modular, programarea structurat i celelalte metode prezentate n
acest curs, sau alte metode ce vor fi asimilate ulterior.
Aceste metode ncurajeaz reutilizarea, reducnd costul realizrii programelor. De asemenea,
folosirea unor componente existente (deci testate) mrete gradul de fiabilitate a produselor soft realizate
i scurteaz perioada de realizare a acestora. Evident, dac o parte din SUBPROGRAMii necesari
programului sunt deja scrii i verificai, viteza de lucru va crete prin folosirea lor. Folosete deci
bibliotecile de componente reutilizabile existente i construiete singur astfel de biblioteci, care s
nglobeze experiena proprie.
69
O bun programare modular elimin legturile ntre dou module prin variabile globale. Se
recomand ca fiecare modul s realizeze o activitate bine definit i independent de alt modul.
Comunicarea ntre dou module trebuie s se realizeze numai prin mecanismul parametrilor formaliactuali.
6. Amn pe mai trziu detaliile nesemnificative.
Aceast regul stabilete prioritile de realizare a componentelor unui program; n primul rnd se
acord atenie aspectelor eseniale, ncepnd cu modulul principal. n fiecare faz d atenie lucrurilor
importante. De exemplu, este inutil s se piard timp cu scrierea unor pri de program pentru tiprirea
rezultatelor i a constata ulterior c rezultatele nu sunt cele dorite, sau nu sunt corecte.
Nu uita ns c pentru beneficiar "Detaliile nesemnificative sunt semnificative". Beneficiarii in
foarte mult la forma rezultatelor i, adeseori, judec programatorii dup aceast form. E pcat de munca
depus dac tiprirea rezultatelor las o impresie proast asupra beneficiarului.
7. Evit artificiile.
Prin folosirea artificiilor n programare, a prescurtrilor i simplificrilor se pierde adesea din
claritatea programului i, mult mai grav, uneori se ajunge chiar la introducerea unor erori. n plus se poate
pierde portabilitatea programului.
Exist ns situaii n care prin anumite artificii se ctig eficien n execuie sau se face
economie de memorie. Dac acest fapt este important atunci artificiile sunt binevenite, n caz contrar nu
se recomand folosirea lor.
8. Folosete constante simbolice.
Folosirea intensiv a constantelor simbolice este recomandat oriunde n textul surs trebuie scris
un numr (la declararea tablourilor, la precizarea limitelor de variaie a unor variabile, etc.). Prin
utilizarea acestor constante se mrete gradul de generalitate a textului scris, iar n situaia n care
valoarea unei constante trebuie schimbat, modificarea este mult mai uoar (doar la locul definiiei
constantei) i nu duce la erori. Ea implic numai definiia constantei, nu modificarea valorii concrete n
toate instruciunile programului.
9. Verific corectitudinea algoritmului i programului n fiecare etap a elaborrii lor.
Detectarea i eliminarea unei erori imediat dup comiterea ei duce la creterea vitezei de realizare
a produsului, evitndu-se activiti inutile de depanare. Se recomand demonstrarea corectitudinii fiecrui
algoritm folosit, ntruct erorile semnalate n timpul testrii sunt adeseori greu de descoperit i, cteodat,
imposibil de eliminat altfel dect prin rescrierea modulului sau programului respectiv. Urmeaz testarea
fiecrui subprogram imediat dup ce a fost scris (codificat). Acest lucru se potrivete codificrii bottomup i sugereaz o abordare sistematic a activitii de codificare. Dac pentru proiectare se pot folosi
oricare dintre metodele indicate, n codificare (i testarea aferent codificrii), abordarea de jos n sus este
esenial. Sugerm ca aceast testare s se fac independent de programul n care se va folosi
subprogramul testat. Este adevrat c activitatea de testare necesit un anumit timp, dar ea este util cel
puin din trei puncte de vedere:
scoate n eviden erorile provocate de proiectarea algoritmului sau codificarea
neadecvat a acestuia;
faciliteaz detectarea erorilor, deoarece dimensiunea problemei este mai mic; n fapt nu
se pierde timp cu scrierea unui program de test, ci se ctig timp, deoarece la fiecare nivel
de detaliere se vor folosi numai componente testate deja; ceea ce rmne de testat la
nivelul respectiv este gestiunea corect a apelurilor respectivelor componente;
70
71
72
CAPITOLUL IX
METODA BACKTRACKING
La dispoziia celor care rezolv probleme cu ajutorul calculatorului exist mai multe metode.
Dintre acestea cel mai des utilizate sunt:
metoda Greedy;
metoda Divide et impera;
metoda Branch and Bound;
metoda Backtracking;
Metoda backtracking se aplic problemelor n care soluia poate fi reprezentat sub forma unui
vector x = (x1, x2, x3, xk, xn) S, unde S este mulimea soluiilor problemei i S = S1 x S2 x x
Sn, i Si sunt mulimi finite avnd s elemente si xi si , ()i = 1..n.
Pentru fiecare problem se dau relaii ntre componentele vectorului x, care sunt numite condiii
interne; soluiile posibile care satisfac condiiile interne se numesc soluii rezultat. Metoda de generare a
tuturor soluiilor posibile si apoi de determinare a soluiilor rezultat prin verificarea ndeplinirii condiiilor
interne necesit foarte mult timp.
Metoda backtracking evit aceast generare i este mai eficient. Elementele vectorului x,
primesc pe rnd valori n ordinea cresctoare a indicilor, x[k] va primi o valoare numai daca au fost
atribuite valori elementelor x1.. x[k-1]. La atribuirea valorii lui x[k] se verifica ndeplinirea unor condiii
de continuare referitoare la x1x[k-1]. Daca aceste condiii nu sunt ndeplinite, la pasul k, acest lucru
nseamn ca orice valori i-am atribui lui x[k+1], x[k+1], .. x[n] nu se va ajunge la o soluie rezultat.
Metoda backtracking construiete un vector soluie n mod progresiv ncepnd cu prima
component a vectorului i mergnd spre ultima cu eventuale reveniri asupra atribuirilor anterioare.
Metoda se aplica astfel :
1) se alege prima valoare sin S1 si I se atribuie lui x1 ;
2) se presupun generate elementele x1x[k-1], cu valori din S1..S[k-1]; pentru generarea lui x[k] se
alege primul element din S[k] disponibil si pentru valoarea aleasa se testeaz ndeplinirea
condiiilor de continuare.
Pot aprea urmtoarele situaii :
a) x[k] ndeplinete condiiile de continuare. Daca s-a ajuns la soluia final (k = n) atunci se
afieaz soluia obinut. Daca nu s-a ajuns la soluia final se trece la generarea
elementului urmtor x [k-1];
b) x[k] nu ndeplinete condiiile de continuare. Se ncearc urmtoarea valoare disponibila
din S[k]. Daca nu se gsete nici o valoare n S[k] care s ndeplineasc condiiile de
continuare, se revine la elementul x[k-1] i se reia algoritmul pentru o nou valoare a
acestuia. Algoritmul se ncheie cnd au fost luate in considerare toate elementele lui S1.
Problemele rezolvate prin aceast metod necesit timp mare de execuie, de aceea este indicat sa
se foloseasc metoda numai daca nu avem alt algoritm de rezolvare.
Dac mulimile S1,S2,Sn au acelai numr k de elemente, timpul necesar de execuie al
algoritmului este k la n. Dac mulimile S1, S2.. Sn nu au acelai numr de elemente, atunci se noteaz cu
m minimul cardinalelor mulimilor S1Sn si cu M, maximul. Timpul de execuie este situat n
intervalul [m la n .. M la n]. Metoda backtracking are complexitatea exponenial, in cele mai multe
cazuri fiind ineficient. Ea insa nu poate fi nlocuit cu alte variante de rezolvare mai rapide n situaia n
care se cere determinarea tuturor soluiilor unei probleme.
Generarea permutrilor. Se citete un numr natural n. S se genereze toate permutrile
mulimii {1, 2, 3, ,n}.
Generarea permutrilor se va face innd cont c orice permutare va fi alctuit din elemente
distincte ale mulimii A. Din acest motiv, la generarea unei permutri, vom urmri ca numerele s fie
distincte.
73
1
1
2
1
1
2
1
2
2
1
3
2
1
3
1
1
3
1
2
3
1
3
3
1
1
2
1
1
2
2
1
2
3
1
2
2
2
3
2
1
3
2
End Sub
Sub valid1(ev As Boolean, st As stiva, k As Integer)
ev = True
For i = 1 To k - 1
If (st.ss(i) = st.ss(k)) Then
ev = False
End If
Next
End Sub
Sub tipar_r()
Dim i As Integer, b As String
b = " "
For i = 1 To n
b = b + Str$(st.ss(i)) + ","
Next
MsgBox b
End Sub
Sub succesor(am_suc As Boolean, st As stiva, k As Integer)
If st.ss(k) < n Then
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
End Sub
Stiva este acea form de organizare a datelor (structur de date) cu proprietatea c operaiile de
introducere i scoatere a datelor se fac n vrful ei.
Stivele se pot simula utiliznd vectori.
Fie ST(i) un vector. ST(1), ST(2), ..., ST(n) pot reine numai litere sau numai cifre. O variabil K
indic n permanent vrful stivei.
Exemplificm, n continuare, modul de lucru cu stiva:
A
B
A
n mod practic la scoaterea unei variabile din stiv, scade cu 1 valoarea variabilei ce indic vrful
stivei, iar atunci cnd scriem ceva n stiv, o eventual valoare rezidual se pierde:
Pe un anumit nivel se retine, de regul, o singur informaie (liter sau cifr), ns este posibil; aa
cum va rezulta din exemplele, prezentate n lucrare, s avem mai multe informaii, caz n care avem de a
face cu stive duble, triple, etc.
ntreaga teorie a recursivitii se bazeaz pe structura de tip stiv.
75
An
mulimile A1, A2 , ., An sunt mulimi finite, iar elementele lor se consider c se afl
La ntlnirea unei astfel de probleme, dac nu cunoatem aceast tehnic, suntem tentai s
generm toate elementele produsului cartezian A1,A2 ,An si fiecare element s fie testat dac este
soluie. Rezolvnd problema n acest mod, timpul de execuie este att de mare, nct poate fi considerat
infinit, algoritmul neavnd nici o valoare practic.
De exemplu, dac dorim s generm toate permutrile unei mulimi finite A, nu are rost s
generm produsul cartezian AxAx.....xA, pentru ca apoi, s testm, pentru fiecare element al acestuia,
dac este sau nu permutare (nu are rost s generm 1.1,1.......1, pentru ca apoi s constatm c nu am
obinut o permutare, cnd de la a doua cifr 1 ne puteam da seama c cifrele nu sunt distincte).
Tehnica Backtracking are la baz un principiu extrem de simplu:
se construiete soluia pas cu pas: x1, x2 ,xn
76
...
xk
x2
ST
x1
Observaie: Problemele rezolvate prin aceast metod necesit un timp ndelungat. Din acest motiv, este
bine s utilizm metoda numai atunci cnd nu avem la dispoziie un alt algoritm mai eficient. Cu toate c
exist probleme pentru care nu se pot elabora ali algoritmi mai eficieni, tehnica backtracking trebuie
aplicat numai n ultim instan.
Fiind dat o tabl de ah, de dimensiune n, x n, se cer toate soluiile de aranjare a n dame, astfel
nct s nu se afle dou dame pe aceeai linie, coloan sau diagonal (dame s nu se atace reciproc).
Exemplu: Presupunnd c dispunem de o tabl de dimensiune 4x4, o soluie ar fi urmtoarea:
77
D
D
D
D
Observm c o dam trebuie s fie plasat singur pe linie. Plasm prima dam pe linia 1, coloana 1.
D
Observm c a treia dam nu poate fi plasat n linia 3. ncercm atunci plasarea celei de-a doua dame n
coloana 4.
D
D
78
Acum este posibil s plasm a patra dam n coloana 3 si astfel am obinut o soluie a problemei.
D
D
D
D
Algoritmul continu n acest mod pn cnd trebuie scoas de pe tabl prima dam.
Pentru reprezentarea unei soluii putem folosi un vector cu n componente (avnd n vedere c pe
fiecare linie se gsete o singur dam).
Exemplu pentru soluia gsit avem vectorul ST ce poate fi asimilat unei stive.
Dou dame se gsesc pe aceeai diagonal dac si numai dac este ndeplinit condiia: |st(i)-st(j)|
=|i-j| ( diferena, n modul, ntre linii si coloane este aceeai).
n general ST(i)=k semnific faptul c pe linia i dama ocup poziia k.
3
ST(4)
ST(3)
ST(2)
ST(1)
st(1)= 1 i = 1
st(3)= 3 j = 3
|st(1) - st(3)| = |1 3| = 2
|i j| = |1 3| = 2
D
D
D
sau situaia
D
D
D
D
st(1) = 3 i = 1
st(3) = 1 j = 3
|st(i) - st(j)| = |3 1| = 2
79
|i j| = |1 3| = 2
ntruct doua dame nu se pot gsi n aceeai coloan, rezult c o soluie este sub form de
permutare. O prim idee ne conduce la generarea tuturor permutrilor si la extragerea soluiilor pentru
problema ca dou dame s nu fie plasate n aceeai diagonal. A proceda astfel, nseamn c lucrm
conform strategiei backtracking. Aceasta presupune ca imediat ce am gsit dou dame care se atac, s
relum cutarea.
Iat algoritmul, conform strategiei generate de backtracking:
n prima poziie a stivei se ncarc valoarea 1, cu semnificaia c n linia unu se aeaz
prima dam n coloan.
Linia 2 se ncearc aezarea damei n coloana 1, acest lucru nefiind posibil ntruct
avem doua dame pe aceeai coloan.
n linia 2 se ncearc aezarea damei n coloana 2 , ns acest lucru nu este posibil,
pentru c damele se gsesc pe aceiai diagonal (|st(1)-st(2)|=|1-2|);
Aezarea damei 2 n coloana 3 este posibil.
Nu se poate plasa dama 3 n coloana 1, ntruct n liniile 1-3 damele ocupa acelai
coloan.
i aceast ncercare eueaz ntruct damele de pe 2 i 3 sunt pe aceeai diagonal.
Damele de pe 2-3 se gsesc pe aceeai coloan.
Damele de pe 2-3 se gsesc pe aceeai diagonal.
Am cobort n stiv mutnd dama de pe linia 2 i coloana 3 n coloana 4.
Algoritmul se ncheie atunci cnd stiva este vid. Semnificaia procedurilor utilizate este
urmtoarea:
INIT - nivelul k al stivei este iniializat cu 0;
SUCCESOR - mrete cu 1 valoarea aflat pe nivelul k al stivei n situaia n care aceasta
este mai mic dect n i atribuie variabilei EV valoarea TRUE, n caz contrar, atribuie
variabilei EV valoarea FALSE;
VALID - valideaz valoarea pus pe nivelul k al stivei, verificnd dac nu avem dou
dame pe aceeai linie (st(k)=st(i)), sau dac nu avem dou dame pe aceeai diagonal
(st(k)-st(i)=|k-i|)caz n care variabilei EV i se atribuie FALSE; n caz contrar, variabilei EV
i se atribuie TRUE;
SOLUTIE - verific dac stiva a fost completat pn la nivelul n inclusiv;
TIPAR - tiprete o soluie.
Subprogramele prezentate in limbajul Visual Basic sunt descrise mai jos:
Global n As Integer, am_suc As Boolean, ev As Boolean
Type stiva
ss(100) As Integer
End Type
Global st As stiva
Sub init(k As Integer, st As stiva)
st.ss(k) = 0
End Sub
Sub succesor(am_suc As Boolean, st As stiva, k As Integer)
If st.ss(k) < n Then
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
80
End Sub
Sub valid(ev As Boolean, st As stiva, k As Integer)
ev = True
For i = 1 To k - 1
If (st.ss(i) = st.ss(k)) Or (Abs(st.ss(i) - st.ss(k)) = Abs(k i)) Then
ev = False
End If
Next
End Sub
Function solutie(k As Integer) As Integer
If k = n Then
solutie = True
Else
solutie = False
End If
End Function
Sub tipar()
Dim i As Integer, b As String
b = " "
For i = 1 To n
b = b + "(" + Str$(i) + "," + Str$(st.ss(i)) + "),"
Next
MsgBox b
End Sub
Sub back()
Dim k As Integer
k = 1
While k > 0
Do
succesor am_suc, st, k
If am_suc = True Then
valid ev, st, k
End If
Loop Until (Not am_suc) Or (am_suc And ev)
If am_suc Then
If solutie(k) Then
tipar
Else
k = k + 1
init k, st
End If
Else
k = k - 1
End If
Wend
End Sub
Sub Button2_Click()
n = InputBox("n=", ib_title)
back
End Sub
81
Produsul cartezian a n mulimi. Se dau mulimile de mai jos i se cere produsul cartezian al lor.
A1 = {1, 2, 3, , k1}
A2 = {1, 2, 3, , k2}
An = {1, 2, 3, , kn}
Exemplu:
A1 = {1, 2}
A2 = {1, 2, 3}
A3 = {1, 2, 3}
A1 A2 A3 = {(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 1), (1, 2, 2), (1, 2, 3), (1, 3, 1), (1, 3, 2), (1, 3, 3),
(2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 2, 1), (2, 2, 2), (2, 2, 3), (2, 3, 1), (2, 3, 2), (2, 3, 3)}.
Pentru rezolvare, se folosesc stiva ST i un vector A ce reine numerele k 1, k2, kn. Utilizm
metoda backtracking, uor modificat din urmtoarele motive:
a) Orice element aflat la nivelul k al stivei este valid, motiv pentru care procedura valid nu
face altceva dect s atribuie variabilei ev valoarea TRUE.
b) Limita superioar pe nivelul k al stivei este dat de A(k).
Modul de concepere a algoritmului rezult din cele ce urmeaz:
1
Observaii:
Algoritmul prezentat aici este de tip backtracking? ntrebarea are sens pentru c este absent mecanismul
de ntoarcere. Vom admite c i aceasta este backtracking, dar degenerat.
Private Sub CommandButton16_Click()
Dim a As vector
cit_n "n=", n
cit_date "a", n, a
tipar " multimile sunt : ", n, a
back_prod_cart
End Sub
Sub cit_n(mes As String, nnn As Integer)
Do
nnn = InputBox(mes, y)
Loop Until n > 0 And n < 100
End Sub
Sub cit_date(mes As String, n As Integer, a As vector)
For i = 1 To n
a.v(i) = InputBox(mes + "(" + Str$(i) + ")=", y)
82
Next
End Sub
Sub tipar(mes
sir = ""
For i = 1 To
sir = sir +
Next
MsgBox mes +
End Sub
Sub back_prod_cart()
Dim k As Integer
k = 1
init k, st
While k > 0
Do
succesor_prod am_suc, st, k
If am_suc = True Then
valid_prod ev, st, k
End If
Loop Until (Not am_suc) Or (am_suc And ev)
If am_suc Then
If solutie(k) Then
tipar_r
Else
k = k + 1
init k, st
End If
Else
k = k - 1
End If
Wend
End Sub
Sub valid_prod(ev As Boolean, st As stiva, k As Integer)
ev = True
End Sub
Function solutie(k As Integer) As Boolean
If k = n Then
solutie = True
Else
solutie = False
End If
End Function
Sub succesor_prod(am_suc As Boolean, st As stiva, k As Integer)
If st.ss(k) < a.v(k) Then
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
End Sub
Sub init(k As Integer, st As stiva)
st.ss(k) = 0
End Sub
83
End Sub
Sub succesor_c(am_suc As Boolean, st As stiva, k As Integer)
If st.ss(k) < n - p + k Then
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
End Sub
Sub valid_c(ev As Boolean, st As stiva, k As Integer)
Dim i As Integer
ev = True
For i = 1 To k - 1
If (st.ss(i) = st.ss(k)) Then
ev = False
End If
Next
If k > 1 Then
If st.ss(k) < st.ss(k - 1) Then
ev = False
End If
End If
End Sub
Function solutie1(k As Integer) As Boolean
If k = p Then
solutie1 = True
Else
solutie1 = False
End If
End Function
Sub tipar_col()
Dim i As Integer, b As String
b = " "
For i = 1 To n
b = b + "Tara = " + Str$(i) + "; culoarea " + Str$(st.ss(i)) + " "
Next
MsgBox b
End Sub
Exemplu: n figura alturat sunt simbolizate cele 6 orae, precum i drumurile existente ntre ele.
86
2
2
1
3
2
1
De la oraul 2 la oraul 3 se gsete drum; prin oraul 3 nu s-a mai trecut, deci
oraul 3 este acceptat.
Algoritmul continu n acest mod pn se ajunge din nou la nivelul 1, caz n care algoritmul se
ncheie.
Un succesor, ntre 2 i n, aflat pe nivelul k al stivei, este considerat valid dac sunt ndeplinite
urmtoarele condiii:
nu s-a mai trecut prin oraul simbolizat de succesor, deci acesta nu se regsete n stiv;
exist drum ntre oraul aflat la nivelul k-1 i cel aflat la nivelul k;
dac succesorul se gsete la nivelul n, s existe drum de la el la oraul 1.
Observaii:
1. Problemele rezolvate prin aceast metod necesit un timp ndelungat de execuie. Din
acest motiv este bine s utilizm metoda atunci numai atunci cnd nu mai avem la
dispoziie un alt algoritm mai eficient
87
End Sub
3
5
init k, st
End If
Else
k = k - 1
End If
Wend
End Sub
Sub succesor_col(am_suc As Boolean, st As stiva, k As Integer)
If st.ss(k) < 4 Then
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
End Sub
Sub valid_col(ev As Boolean, st As stiva, k As Integer)
ev = True
For i = 1 To k - 1
If (st.ss(i) = st.ss(k)) And (mat.m(i, k) = 1) Then
ev = False
End If
Next
End Sub
Sub tipar_col()
Dim i As Integer, b As String
b = " "
For i = 1 To n
b = b + "Tara = " + Str$(i) + "; culoarea " + Str$(st.ss(i)) + " "
Next
MsgBox b
End Sub
Sub tipar_rr()
Dim i As Integer, b As String
b = " "
For i = 1 To p
b = b + Str$(st.ss(i)) + ","
Next
MsgBox b
End Sub
90
CAPITOLUL X
METODA DIVIDE ET IMPERA
Metoda de programare DIVIDE ET IMPERA consta in impartirea problemei initiale de
dimensiuni [n] in doua sau mai multe probleme de dimensiuni reduse. In general se executa impartirea in
doua subprobleme de dimensiuni aproximativ egale si anume [n/2]. Impartirea in subprobleme are loc
pana cand dimensiunea acestora devine suficient de mica pentru a fi rezolvate in mod direct(cazul de
baza). Dupa rezolvarea celor doua subprobleme se executa faza de combinare a rezultatelor in vederea
rezolvarii intregii probleme.
Metoda DIVIDE ET IMPERA se poate aplica in rezolvarea unei probleme care indeplineste
urmatoarele conditii:
se poate descompune in (doua sau mai multe) suprobleme;
aceste suprobleme sunt independente una fata de alta (o subproblema nu se rezolva pe
baza alteia si nu se foloseste rezultate celeilalte);
aceste subprobleme sunt similare cu problema initiala;
la randul lor subproblemele se pot descompune (daca este necesar) in alte subprobleme
mai simple;
aceste subprobleme simple se pot solutiona imediat prin algoritmul simplificat.
Deoarece putine probleme indeplinesc conditiile de mai sus ,aplicarea metodei este destul de rara.
Dupa cum sugereaza si numele "desparte si stapaneste "etapele rezolvarii unei probleme (numita
problema initiala) in DIVIDE ET IMPERA sunt :
descompunerea problemei initiale in subprobleme independente, smilare problemei de
baza, de dimensiuni mai mici;
descompunerea treptata a subproblemelor in alte subprobleme din ce in ce mai simple,
pana cand se pot rezolva imediata ,prin algoritmul simplificat;
rezolvarea subproblemelor simple;
combinarea solutiilor gasite pentru construirea solutiilor subproblemelor de dimensiuni
din ce in ce mai mari;
combinarea ultimelor solutii determina obtinerea solutiei problemei initiale
Metoda DIVIDE ET IMPERA admite o implementare recursiva, deorece subproblemele sunt
similare problemei initiale, dar de dimensiuni mai mici.
Principiul fundamental al recursivitatii este autoapelarea unui subprogram cand acesta este activ;
ceea ce se intampla la un nivel, se intampla la orice nivel, avand grija sa asiguram conditia de terminare
ale apelurilor repetate. Asemanator se intampla si in cazul metodei DIVITE ET IMPERA; la un anumit
nivel sunt doua posibilitati:
s-a ajuns la o (sub)problema simpla ce admite o rezolvare imediata caz in care se
rezolva (sub)problema si se revine din apel (la subproblema anterioara, de dimensiuni mai
mari);
s-a ajuns la o (sub)problema care nu admite o rezolvare imediata, caz in care o
descompunem in doua sau mai multe subprobleme si pentru fiecare din ele se continua
apelurile recursive (ale procedurii sau functiei).
In etapa finala a metodei DIVIDE ET IMPERA se produce combinarea subproblemelor (rezolvate
deja) prin secventele de revenire din apelurile recursive.
Etapele metodei DIVIDE ET IMPERA (prezentate anterior) se pot reprezenta prin urmatorul
subprogram general (procedura sau functie )recursiv exprimat in limbaj natural:
Subprogram DIVIMP (PROB);
Daca PROBLEMA PROB este simpla
Atunci se rezolva si se obtine solutia SOL
Altfel pentru i=1,k executa DIVIMP(PROB) si se obtine SOL1;
91
94
CAPITOLUL XI
METODA GREEDY
11.1 Algoritmi greedy
Pusi in fata unei probleme pentru care trebuie sa elaboram un algoritm, de multe ori nu stim cum
sa incepem. Ca si in orice alta activitate, exista cateva principii generale care ne pot ajuta in aceasta
situatie. Ne propunem sa prezentam in urmatoarele capitole tehnicile fundamentale de elaborare a
algoritmilor. Cateva din aceste metode sunt atat de generale, incat le folosim frecvent, chiar daca numai
intuitiv, ca reguli elementare in gandire.
11.2 Tehnica greedy
Algoritmii greedy (greedy = lacom) sunt in general simpli si sunt folositi la probleme de
optimizare, cum ar fi: sa se gaseasca cea mai buna ordine de executare a unor lucrari pe calculator, sa se
gaseasca cel mai scurt drum intr-un graf etc. In cele mai multe situatii de acest fel avem:
multime de candidati (lucrari de executat, varfuri ale grafului etc)
o functie care verifica daca o anumita multime de candidati constituie o solutie posibila
o functie care verifica daca o multime de candidati este fezabila, adica daca este posibil
sa completam aceasta multime astfel incat sa obtinem o solutie posibila, nu neaparat
optima, a problemei
o functie de selectie care indica la orice moment care este cel mai promitator dintre
candidatii inca nefolositi
o functie obiectiv care da valoarea unei solutii (timpul necesar executarii tuturor
lucrarilor intr-o anumita ordine, lungimea drumului pe care l-am gasit etc); aceasta este
functia pe care urmarim sa o optimizam (minimizam/maximizam)
Pentru a rezolva problema noastra de optimizare, cautam o solutie posibila care sa optimizeze
valoarea functiei obiectiv. Un algoritm greedy construieste solutia pas cu pas. Initial, multimea
candidatilor selectati este vida. La fiecare pas, incercam sa adaugam acestei multimi cel mai promitator
candidat, conform functiei de selectie. Daca, dupa o astfel de adaugare, multimea de candidati selectati nu
mai este fezabila, eliminam ultimul candidat adaugat; acesta nu va mai fi niciodata considerat. Daca, dupa
adaugare, multimea de candidati selectati este fezabila, ultimul candidat adaugat va ramane de acum
incolo in ea. De fiecare data cand largim multimea candidatilor selectati, verificam daca aceasta multime
nu constituie o solutie posibila a problemei noastre. Daca algoritmul greedy functioneaza corect, prima
solutie gasita va fi totodata o solutie optima a problemei. Solutia optima nu este in mod necesar unica: se
poate ca functia obiectiv sa aiba aceeasi valoare optima pentru mai multe solutii posibile. Descrierea
formala a unui algoritm greedy general este:
function greedy(C)
S
95
Este de inteles acum de ce un astfel de algoritm se numeste lacom (am putea sa-i spunem si
nechibzuit). La fiecare pas, procedura alege cel mai bun candidat la momentul respectiv, fara sa-i pese
de viitor si fara sa se razgandeasca. Daca un candidat este inclus in solutie, el ramane acolo; daca un
candidat este exclus din solutie, el nu va mai fi niciodata reconsiderat. Asemenea unui intreprinzator
rudimentar care urmareste castigul imediat in dauna celui de perspectiva, un algoritm greedy actioneaza
simplist. Totusi, ca si in afaceri, o astfel de metoda poate da rezultate foarte bune tocmai datorita
simplitatii ei.
Functia select este de obicei derivata din functia obiectiv; uneori aceste doua functii sunt chiar
identice.
Un exemplu simplu de algoritm greedy este algoritmul folosit pentru rezolvarea urmatoarei
probleme. Sa presupunem ca dorim sa dam restul unui client, folosind un numar cat mai mic de monezi.
In acest caz, elementele problemei sunt:
candidatii: multimea initiala de monezi de 1, 5, si 25 unitati, in care presupunem ca din
fiecare tip de moneda avem o cantitate nelimitata
solutie posibila: valoarea totala a unei astfel de multimi de monezi selectate trebuie sa
fie exact valoarea pe care trebuie sa o dam ca rest
multime fezabila: valoarea totala a unei astfel de multimi de monezi selectate nu este
mai mare decat valoarea pe care trebuie sa o dam ca rest
functia de selectie: se alege cea mai mare moneda din multimea de candidati ramasa
functia obiectiv: numarul de monezi folosite in solutie; se doreste minimizarea acestui
numar
Se poate demonstra ca algoritmul greedy va gasi in acest caz mereu solutia optima (restul cu un
numar minim de monezi). Pe de alta parte, presupunand ca exista si monezi de 12 unitati sau ca unele din
tipurile de monezi lipsesc din multimea initiala de candidati, se pot gasi contraexemple pentru care
algoritmul nu gaseste solutia optima, sau nu gaseste nici o solutie cu toate ca exista solutie.
Evident, solutia optima se poate gasi incercand toate combinarile posibile de monezi. Acest mod
de lucru necesita insa foarte mult timp.
Un algoritm greedy nu duce deci intotdeauna la solutia optima, sau la o solutie. Este doar un
principiu general, urmand ca pentru fiecare caz in parte sa determinam daca obtinem sau nu solutia
optima.
11.3 Minimizarea timpului mediu de asteptare
O singura statie de servire (procesor, pompa de benzina etc) trebuie sa satisfaca cererile a n clienti.
Timpul de servire necesar fiecarui client este cunoscut in prealabil: pentru clientul i este necesar un timp
ti, 1 i n. Dorim sa minimizam timpul total de asteptare
(timpul de asteptare pentru clientul i)
ceea ce este acelasi lucru cu a minimiza timpul mediu de asteptare, care este T/n. De exemplu, daca avem
trei clienti cu t1 = 5, t2 = 10, t3 = 3, sunt posibile sase ordini de servire. In primul caz, clientul 1 este servit
primul, clientul 2 asteapta pana este servit clientul 1 si apoi este servit, clientul 3 asteapta pana sunt serviti
clientii 1, 2 si apoi este servit. Timpul total de asteptare a celor trei clienti este 38.
Ordinea
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
T
5+(5+10)+(5+10+3)
5+(5+3)+(5+3+10)
10+(10+5)+(10+5+3)
10+(10+3)+(10+3+5)
3+(3+5)+(3+5+10)
3+(3+10)+(3+10+5)
96
=
=
=
=
=
=
38
31
43
41
29
34
optim
Algoritmul greedy este foarte simplu: la fiecare pas se selecteaza clientul cu timpul minim de
servire din multimea de clienti ramasa. Vom demonstra ca acest algoritm este optim.
Fie I = (i1 i2 ... in) o permutare oarecare a intregilor {1, 2, ..., n}. Daca servirea are loc in ordinea I,
avem
Presupunem acum ca I este astfel incat putem gasi doi intregi a < b cu
Interschimbam pe ia cu ib in I; cu alte cuvinte, clientul care a fost servit al b-lea va fi servit acum al
a-lea si invers. Obtinem o noua ordine de servire J, care este de preferat deoarece
felul urmator: varful terminal i, 1 i 6, va corespunde sirului Si, iar varfurile neterminale se
numeroteaza de la 7 la 11 in ordinea obtinerii interclasarilor respective (Figura 11.2).
unde ai este adancimea varfului i. Se observa ca numarul total de deplasari de elemente pentru strategia
corespunzatoare lui A este chiar L(A). Solutia optima a problemei noastre este atunci arborele (strategia)
pentru care lungimea externa ponderata este minima.
Proprietatea 11.1 Prin metoda greedy se obtine intotdeauna interclasarea optima a n siruri ordonate, deci
strategia cu arborele de lungime externa ponderata minima.
Demonstratie: Demonstram prin inductie. Pentru n = 1, proprietatea este verificata. Presupunem ca
proprietatea este adevarata pentru n1 siruri. Fie A arborele strategiei greedy de interclasare a n siruri de
lungime q1 q2 ... qn. Fie B un arbore cu lungimea externa ponderata minima, corespunzator unei
strategii optime de interclasare a celor n siruri. In arborele A apare subarborele
reprezentand prima interclasare facuta conform strategiei greedy. In arborele B, fie un varf neterminal de
adancime maxima. Cei doi fii ai acestui varf sunt atunci doua varfuri terminale qj si qk. Fie B' arborele
obtinut din B schimband intre ele varfurile q1 si qj, respectiv q2 si qk. Evident, L(B') L(B). Deoarece B
are lungimea externa ponderata minima, rezulta ca L(B') = L(B). Eliminand din B' varfurile q1 si q2,
obtinem un arbore B" cu n1 varfuri terminale q1+q2, q3, ..., qn. Arborele B' are lungimea externa
ponderata minima si L(B') = L(B") + q1+q2. Rezulta ca si B" are lungimea externa ponderata minima.
Atunci, conform ipotezei inductiei, avem L(B") = L(A'), unde A' este arborele strategiei greedy de
interclasare a sirurilor de lungime q1+q2, q3, ..., qn. Cum A se obtine din A' atasand la varful q1+q2 fiii q1 si
q2, iar B' se obtine in acelasi mod din B", rezulta ca L(A) = L(B') = L(B). Proprietatea este deci adevarata
pentru orice n.
98
La scrierea algoritmului care genereaza arborele strategiei greedy de interclasare vom folosi un
min-heap. Fiecare element al min-heap-ului este o pereche (q, i) unde i este numarul unui varf din
arborele strategiei de interclasare, iar q este lungimea sirului pe care il reprezinta. Proprietatea de minheap se refera la valoarea lui q.
Algoritmul interopt va construi arborele strategiei greedy. Un varf i al arborelui va fi memorat in
trei locatii diferite continand:
LU[i] = lungimea sirului reprezentat de varf
ST[i] = numarul fiului stang
DR[i] = numarul fiului drept
procedure interopt(Q[1 .. n])
{construieste arborele strategiei greedy de interclasare
a sirurilor de lungimi Q[i] = qi, 1 i n}
H min-heap vid
for i 1 to n do
(Q[i], i) => H
{insereaza in min-heap}
LU[i] Q[i]; ST[i] 0; DR[i] 0
for i n+1 to 2n1 do
(s, j) <= H
{extrage radacina lui H}
(r, k) <= H
{extrage radacina lui H}
ST[i] j; DR[i] k; LU[i] s+r
(LU[i], i) => H
{insereaza in min-heap}
In cazul cel mai nefavorabil, operatiile de inserare in min-heap si de extragere din min-heap
necesita un timp in ordinul lui log n. Restul operatiilor necesita un timp constant. Timpul total pentru
interopt este deci in O(n log n).
Coduri Huffman
O alta aplicatie a strategiei greedy si a arborilor binari cu lungime externa ponderata minima este
obtinerea unei codificari cat mai compacte a unui text.
Un principiu general de codificare a unui sir de caractere este urmatorul: se masoara frecventa de
aparitie a diferitelor caractere dintr-un esantion de text si se atribuie cele mai scurte coduri, celor mai
frecvente caractere, si cele mai lungi coduri, celor mai putin frecvente caractere. Acest principiu sta, de
exemplu, la baza codului Morse. Pentru situatia in care codificarea este binara, exista o metoda eleganta
pentru a obtine codul respectiv. Aceasta metoda, descoperita de Huffman (1952) foloseste o strategie
greedy si se numeste codificarea Huffman. O vom descrie pe baza unui exemplu.
Fie un text compus din urmatoarele litere (in paranteze figureaza frecventele lor de aparitie):
S (10), I (29), P (4), O (9), T (5)
Conform metodei greedy, construim un arbore binar fuzionand cele doua litere cu frecventele cele
mai mici. Valoarea fiecarui varf este data de frecventa pe care o reprezinta.
In final, ajungem la arborele din Figura 11.3, in care fiecare varf terminal corespunde unei litere
din text.
Pentru a obtine codificarea binara a literei P, nu avem decat sa scriem secventa de 0-uri si 1-uri in
ordinea aparitiei lor pe drumul de la radacina catre varful corespunzator lui P: 1011. Procedam similar si
pentru restul literelor:
S (11), I (0), P (1011), O (100), T (1010)
Pentru un text format din n litere care apar cu frecventele f1, f2, ..., fn, un arbore de codificare este
un arbore binar cu varfurile terminale avand valorile f1, f2, ..., fn, prin care se obtine o codificare binara a
textului. Un arbore de codificare nu trebuie in mod necesar sa fie construit dupa metoda greedy a lui
Huffman, alegerea varfurilor care sunt fuzionate la fiecare pas putandu-se face dupa diverse criterii.
Lungimea externa ponderata a unui arbore de codificare este:
Graful partial <V, A> este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree). Un graf poate avea mai multi arbori partiali de cost minim si acest lucru se poate
verifica pe un exemplu.
Vom prezenta doi algoritmi greedy care determina arborele partial de cost minim al unui graf. In
terminologia metodei greedy, vom spune ca o multime de muchii este o solutie, daca constituie un arbore
partial al grafului G, si este fezabila, daca nu contine cicluri. O multime fezabila de muchii este
promitatoare, daca poate fi completata pentru a forma solutia optima. O muchie atinge o multime data de
varfuri, daca exact un capat al muchiei este in multime. Urmatoarea proprietate va fi folosita pentru a
demonstra corectitudinea celor doi algoritmi.
Proprietatea 11.2 Fie G = <V, M> un graf neorientat conex in care fiecare muchie are un cost nenegativ.
Fie W V o submultime stricta a varfurilor lui G si fie A M o multime promitatoare de muchii, astfel
incat nici o muchie din A nu atinge W. Fie m muchia de cost minim care atinge W. Atunci, A {m} este
promitatoare.
Demonstratie: Fie B un arbore partial de cost minim al lui G, astfel incat A B (adica, muchiile din A
sunt continute in arborele B). Un astfel de B trebuie sa existe, deoarece A este promitatoare. Daca m B,
nu mai ramane nimic de demonstrat. Presupunem ca m B. Adaugandu-l pe m la B, obtinem exact un
ciclu. In acest ciclu, deoarece m atinge W, trebuie sa mai existe cel putin o muchie m' care atinge si ea pe
W (altfel, ciclul nu se inchide). Eliminandu-l pe m', ciclul dispare si obtinem un nou arbore partial B' al lui
G. Costul lui m este mai mic sau egal cu costul lui m', deci costul total al lui B' este mai mic sau egal cu
costul total al lui B. De aceea, B' este si el un arbore partial de cost minim al lui G, care include pe m.
Observam ca A B' deoarece muchia m', care atinge W, nu poate fi in A. Deci, A {m} este
promitatoare.
Multimea initiala a candidatilor este M. Cei doi algoritmi greedy aleg muchiile una cate una intr-o
anumita ordine, aceasta ordine fiind specifica fiecarui algoritm.
Algoritmul lui Kruskal
Arborele partial de cost minim poate fi construit muchie, cu muchie, dupa urmatoarea metoda a lui
Kruskal (1956): se alege intai muchia de cost minim, iar apoi se adauga repetat muchia de cost minim
nealeasa anterior si care nu formeaza cu precedentele un ciclu. Alegem astfel #V1 muchii. Este usor de
dedus ca obtinem in final un arbore. Este insa acesta chiar arborele partial de cost minim cautat?
Inainte de a raspunde la intrebare, sa consideram, de exemplu, graful din Figura 11.4a. Ordonam
crescator (in functie de cost) muchiile grafului: {1, 2}, {2, 3}, {4, 5}, {6, 7}, {1, 4}, {2, 5}, {4, 7}, {3, 5},
{2, 4}, {3, 6}, {5, 7}, {5, 6} si apoi aplicam algoritmul. Structura componentelor conexe este ilustrata,
pentru fiecare pas, in Tabelul 11.1.
Pasul
Muchia considerata
initializare
1
2
3
4
5
6
7
{1, 2}
{2, 3}
{4, 5}
{6, 7}
{1, 4}
{2, 5}
{4, 7}
Tabelul 11.1 Algoritmul lui Kruskal aplicat grafului din Figura 11.4a.
Multimea A este initial vida si se completeaza pe parcurs cu muchii acceptate (care nu formeaza
un ciclu cu muchiile deja existente in A). In final, multimea A va contine muchiile {1, 2}, {2, 3}, {4, 5},
{6, 7}, {1, 4}, {4, 7}. La fiecare pas, graful partial <V, A> formeaza o padure de componente conexe,
obtinuta din padurea precedenta unind doua componente. Fiecare componenta conexa este la randul ei un
arbore partial de cost minim pentru varfurile pe care le conecteaza. Initial, fiecare varf formeaza o
componenta conexa. La sfarsit, vom avea o singura componenta conexa, care este arborele partial de cost
minim cautat (Figura 11.4b).
Ceea ce am observat in acest caz particular este valabil si pentru cazul general, din Proprietatea
11.2 rezultand:
Proprietatea 11.3 In algoritmul lui Kruskal, la fiecare pas, graful partial <V, A> formeaza o padure de
componente conexe, in care fiecare componenta conexa este la randul ei un arbore partial de cost minim
pentru varfurile pe care le conecteaza. In final, se obtine arborele partial de cost minim al grafului G.
Pentru a implementa algoritmul, trebuie sa putem manipula submultimile formate din varfurile
componentelor conexe. Folosim pentru aceasta o structura de multimi disjuncte si procedurile de tip find
si merge. In acest caz, este preferabil sa reprezentam graful ca o lista de muchii cu costul asociat lor, astfel
incat sa putem ordona aceasta lista in functie de cost. Iata algoritmul:
function Kruskal(G = <V, M>)
{initializare}
sorteaza M crescator in functie de cost
n #V
A
{va contine muchiile arborelui partial de cost minim}
initializeaza n multimi disjuncte continand
fiecare cate un element din V
{bucla greedy}
repeat
{u, v} muchia de cost minim care
inca nu a fost considerate
ucomp find(u)
vcomp find(v)
if ucomp vcomp then merge(ucomp, vcomp)
A A {{u, v}}
until #A = n-1
return A
102
Muchia considerata
{2, 1}
{3, 2}
{4, 1}
{5, 4}
{7, 4}
{6, 7}
U
{1}
{1, 2}
{1, 2, 3}
{1, 2, 3, 4}
{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5, 6}
{1, 2, 3, 4, 5, 6, 7}
Tabelul 11.2 Algoritmul lui Prim aplicat grafului din Figura 11.4a.
Proprietatea 11.4 In algoritmul lui Prim, la fiecare pas, <U, A> formeaza un arbore partial de cost minim
pentru subgraful <U, A> al lui G. In final, se obtine arborele partial de cost minim al grafului G.
Descrierea formala a algoritmului este data in continuare.
function Prim-formal(G = <V, M>)
{initializare}
A
{va contine muchiile arborelui partial de cost minim}
U {un varf oarecare din V}
{bucla greedy}
while U V do
103
AA
{{k, vecin[k]}}
mincost[k] 1
{adauga varful k la U}
for j 2 to n do
if C[k, j] < mincost[ j] then mincost[ j] C[k, j]
vecin[ j] k
return A
Bucla principala se executa de n1 ori si, la fiecare iteratie, buclele for din interior necesita un
timp in O(n). Algoritmul Prim necesita, deci, un timp in O(n2). Am vazut ca timpul pentru algoritmul lui
Kruskal este in O(m log n), unde m = #M. Pentru un graf dens (adica, cu foarte multe muchii), se deduce
ca m se apropie de n(n1)/2. In acest caz, algoritmul Kruskal necesita un timp in O(n2 log n) si algoritmul
Prim este probabil mai bun. Pentru un graf rar (adica, cu un numar foarte mic de muchii), m se apropie de
n si algoritmul Kruskal necesita un timp in O(n log n), fiind probabil mai eficient decat algoritmul Prim.
11.5 Cele mai scurte drumuri care pleaca din acelasi punct
Fie G = <V, M> un graf orientat, unde V este multimea varfurilor si M este multimea muchiilor.
Fiecare muchie are o lungime nenegativa. Unul din varfuri este desemnat ca varf sursa. Problema este sa
determinam lungimea celui mai scurt drum de la sursa catre fiecare varf din graf.
Vom folosi un algoritm greedy, datorat lui Dijkstra (1959). Notam cu C multimea varfurilor
disponibile (candidatii) si cu S multimea varfurilor deja selectate. In fiecare moment, S contine acele
varfuri a caror distanta minima de la sursa este deja cunoscuta, in timp ce multimea C contine toate
celelalte varfuri. La inceput, S contine doar varful sursa, iar in final S contine toate varfurile grafului. La
fiecare pas, adaugam in S acel varf din C a carui distanta de la sursa este cea mai mica.
Spunem ca un drum de la sursa catre un alt varf este special, daca toate varfurile intermediare de-a
lungul drumului apartin lui S. Algoritmul lui Dijkstra lucreaza in felul urmator. La fiecare pas al
104
algoritmului, un tablou D contine lungimea celui mai scurt drum special catre fiecare varf al grafului.
Dupa ce adaugam un nou varf v la S, cel mai scurt drum special catre v va fi, de asemenea, cel mai scurt
dintre toate drumurile catre v. Cand algoritmul se termina, toate varfurile din graf sunt in S, deci toate
drumurile de la sursa catre celelalte varfuri sunt speciale si valorile din D reprezinta solutia problemei.
Presupunem, pentru simplificare, ca varfurile sunt numerotate, V = {1, 2, ..., n}, varful 1 fiind
sursa, si ca matricea L da lungimea fiecarei muchii, cu L[i, j] = + , daca muchia (i, j) nu exista. Solutia
se va construi in tabloul D[2 .. n]. Algoritmul este:
function Dijkstra(L[1 .. n, 1 .. n])
{initializare}
C {2, 3, ..., n}
{S = V \C exista doar implicit}
for i 2 to n do D[i] L[1, i]
{bucla greedy}
repeat n2 times
v varful din C care minimizeaza D[v]
C C \ {v}
{si, implicit, S S {v}}
for fiecare w C do
D[w] min(D[w], D[v]+L[v, w])
return D
Pentru graful din Figura 11.5, pasii algoritmului sunt prezentati in Tabelul 11.3.
5
4
3
C
{2, 3, 4, 5}
{2, 3, 4}
{2, 3}
{2}
D
[50, 30, 100, 10]
[50, 30, 20, 10]
[40, 30, 20, 10]
[35, 30, 20, 10]
Tabelul 11.3 Algoritmul lui Dijkstra aplicat grafului din Figura 11.5.
Observam ca D nu se schimba daca mai efectuam o iteratie pentru a-l scoate si pe {2} din C. De
aceea, bucla greedy se repeta de doar n-2 ori.
Se poate demonstra urmatoarea proprietate:
Proprietatea 11.5. In algoritmul lui Dijkstra, daca un varf i
i) este in S, atunci D[i] da lungimea celui mai scurt drum de la sursa catre i;
ii) nu este in S, atunci D[i] da lungimea celui mai scurt drum special de la sursa catre i.
La terminarea algoritmului, toate varfurile grafului, cu exceptia unuia, sunt in S. Din proprietatea
precedenta, rezulta ca algoritmul lui Dijkstra functioneaza corect.
105
Daca dorim sa aflam nu numai lungimea celor mai scurte drumuri, dar si pe unde trec ele, este
suficient sa adaugam un tablou P[2 .. n], unde P[v] contine numarul nodului care il precede pe v in cel
mai scurt drum. Pentru a gasi drumul complet, nu avem decat sa urmarim, in tabloul P, varfurile prin care
trece acest drum, de la destinatie la sursa. Modificarile in algoritm sunt simple:
initializeaza P[i] cu 1, pentru 2 i n
continutul buclei for cea mai interioara se inlocuieste cu
if D[w] > D[v] + L[v, w] then D[w] D[v] + L[v, w]
P[w] v
bucla repeat se executa de n -1 ori
Sa presupunem ca aplicam algoritmul Dijkstra asupra unui graf cu n varfuri si m muchii.
Initializarea necesita un timp in O(n). Alegerea lui v din bucla repeat presupune parcurgerea tuturor
varfurilor continute in C la iteratia respectiva, deci a n -1, n -2, ..., 2 varfuri, ceea ce necesita in total un
timp in O(n2). Bucla for interioara efectueaza n-2, n-3, ..., 1 iteratii, totalul fiind tot in O(n2). Rezulta ca
algoritmul Dijkstra necesita un timp in O(n2).
Incercam sa imbunatatim acest algoritm. Vom reprezenta graful nu sub forma matricii de adiacenta
L, ci sub forma a n liste de adiacenta, continand pentru fiecare varf lungimea muchiilor care pleaca din el.
Bucla for interioara devine astfel mai rapida, deoarece putem sa consideram doar varfurile w adiacente lui
v. Aceasta nu poate duce la modificarea ordinului timpului total al algoritmului, daca nu reusim sa scadem
si ordinul timpului necesar pentru alegerea lui v din bucla repeat. De aceea, vom tine varfurile v din C
intr-un min-heap, in care fiecare element este de forma (v, D[v]), proprietatea de min-heap referindu-se la
valoarea lui D[v]. Numim algoritmul astfel obtinut Dijkstra-modificat. Sa il analizam in cele ce urmeaza.
Initializarea min-heap-ului necesita un timp in O(n). Instructiunea C C \ {v} consta in
extragerea radacinii min-heap-ului si necesita un timp in O(log n). Pentru cele n2 extrageri este nevoie
de un timp in O(n log n).
Pentru a testa daca D[w] > D[v]+L[v, w], bucla for interioara consta acum in inspectarea
fiecarui varf w din C adiacent lui v. Fiecare varf v din C este introdus in S exact o data si cu acest prilej
sunt testate exact muchiile adiacente lui; rezulta ca numarul total de astfel de testari este de cel mult m.
Daca testul este adevarat, trebuie sa il modificam pe D[w] si sa operam un percolate cu w in min-heap,
ceea ce necesita din nou un timp in O(log n). Timpul total pentru operatiile percolate este deci in
O(m log n).
In concluzie, algoritmul Dijkstra-modificat necesita un timp in O(max(n, m) log n). Daca graful
este conex, atunci m n si timpul este in O(m log n). Pentru un graf rar este preferabil sa folosim
algoritmul Dijkstra-modificat, iar pentru un graf dens algoritmul Dijkstra este mai eficient.
Este usor de observat ca, intr-un graf G neorientat conex, muchiile celor mai scurte drumuri de la
un varf i la celelalte varfuri formeaza un arbore partial al celor mai scurte drumuri pentru G. Desigur,
acest arbore depinde de alegerea radacinii i si el difera, in general, de arborele partial de cost minim al lui
G.
Problema gasirii celor mai scurte drumuri care pleaca din acelasi punct se poate pune si in cazul
unui graf neorientat.
11.6 Euristica greedy
Pentru anumite probleme, se poate accepta utilizarea unor algoritmi despre care nu se stie daca
furnizeaza solutia optima, dar care furnizeaza rezultate acceptabile, sunt mai usor de implementat si
mai eficienti decat algoritmii care dau solutia optima. Un astfel de algoritm se numeste euristic.
Una din ideile frecvent utilizate in elaborarea algoritmilor euristici consta in descompunerea
procesului de cautare a solutiei optime in mai multe subprocese succesive, fiecare din aceste subprocese
constand dintr-o optimizare. O astfel de strategie nu poate conduce intotdeauna la o solutie optima,
deoarece alegerea unei solutii optime la o anumita etapa poate impiedica atingerea in final a unei solutii
optime a intregii probleme; cu alte cuvinte, optimizarea locala nu implica, in general, optimizarea globala.
106
Regasim, de fapt, principiul care sta la baza metodei greedy. Un algoritm greedy, despre care nu se poate
demonstra ca furnizeaza solutia optima, este un algoritm euristic.
Vom da doua exemple de utilizare a algoritmilor greedy euristici.
11.6.1 Colorarea unui graf
Fie G = <V, M> un graf neorientat, ale carui varfuri trebuie colorate astfel incat oricare doua
varfuri adiacente sa fie colorate diferit. Problema este de a obtine o colorare cu un numar minim de culori.
Folosim urmatorul algoritm greedy: alegem o culoare si un varf arbitrar de pornire, apoi consideram
varfurile ramase, incercand sa le coloram, fara a schimba culoarea. Cand nici un varf nu mai poate fi
colorat, schimbam culoarea si varful de start, repetand procedeul.
Conform strategiei greedy, vom construi ciclul pas cu pas, adaugand la fiecare iteratie cea mai
scurta muchie disponibila cu urmatoarele proprietati:
nu formeaza un ciclu cu muchiile deja selectate (exceptand pentru ultima muchie aleasa,
care completeaza ciclul)
nu exista inca doua muchii deja selectate, astfel incat cele trei muchii sa fie incidente in
acelasi varf
La: 2
De la:
1
3 10 11 7 25
2
6 12 8 26
3
9 4 20
4
5 15
5
18
Tabelul 11.4 Matricea distantelor pentru problema comis-voiajorului.
De exemplu, pentru sase orase a caror matrice a distantelor este data in Tabelul 11.4, muchiile se
aleg in ordinea: {1, 2}, {3, 5}, {4, 5}, {2, 3}, {4, 6}, {1, 6} si se obtine ciclul (1, 2, 3, 5, 4, 6, 1) de
lungime 58. Algoritmul greedy nu a gasit ciclul optim, deoarece ciclul (1, 2, 3, 6, 4, 5, 1) are lungimea 56.
108
CAPITOLUL XII
STUDII DE CAZ APLICAII
109
1. S se determine toate numerele perechile de numere gemene pana la o anumita valoare n. Dou
numere sunt gemene dac sunt ambele prime i diferena dintre cel mai mare i cel mai mic este 2.
Private Sub CommandButton1_Click()
Dim rad As Integer, n As Integer, p As Integer, i As Integer, j
As Integer
cit_n "n = ", n
For i = 3 To n
p = 1
rad = Int(Sqr(i + 2))
For j = 2 To Int(rad)
If i Mod j = 0 Or (i + 2) Mod j = 0 Then
prim = 0
j = Int(rad)
End If
Next
If p Then
MsgBox "(" + Str$(i) + "," + Str$(i + 2) + ")" + Chr(13)
End If
Next
End Sub
cit_n "n=", n
back_partitii
End Sub
11. Cutarea binar utiliznd metoda Divide et Impera pentru sortarea unui ir de numere
Private Sub CommandButton2_Click()
Dim n As Integer, x As Integer, a As vector
cit_n "n = ", n
cit_date "a", n, a
tipar "sirul dat este : ", n, a
divimp 1, n, a
'MsgBox "Sirul a sortat este"
tipar "Sirul a sortat este", n, a
x = InputBox(" x = ", y)
st = 1
dr = n
l = True
While st <= dr And l = True
pp = (st + dr) / 2
If a.v(pp) = x Then
l = False
MsgBox "numarul x = " + Str$(x) + " se afla printre elementele
vectorului a"
End If
If a.v(pp) < x Then
st = pp + 1
Else
dr = p - 1
End If
Wend
If l = True Then
MsgBox "numarul x = " + Str$(x) + " nu se fala in sir "
End If
End Sub
i = i + 1
k = k + 1
Else
b.v(k) = a.v(j)
j = j + 1
k = k + 1
End If
Wend
If i <= m Then
For j = i To m
b.v(k) = a.v(j)
k = k + 1
Next
Else
For i = j To q
b.v(k) = a.v(i)
k = k + 1
Next
End If
k = 1
For i = p To q
a.v(i) = b.v(k)
k = k + 1
Next
End Sub
113
End Sub
End Sub
c.m(i, j) = 0
c1.m(i, j) = 0
Next
Next
For i = 1 To n
c.m(i, i) = 1
c1.m(i, i) = 1
Next
'Next
While k > 0
If k Mod 2 = 1 Then
prod_mat n, n, n, c1, a, c
End If
For i = 1 To n
For j = 1 To n
c1.m(i, j) = c.m(i, j)
'c1.m(i, j) = 0
Next
Next
prod_mat n, n, n, a, a, b
k = Int(k / 2)
For i = 1 To n
For j = 1 To n
a.m(i, j) = b.m(i, j)
'c1.m(i, j) = 0
Next
Next
Wend
For i = 1 To n
For j = 1 To n
c.m(i, j) = c1.m(i, j)
'c1.m(i, j) = 0
Next
Next
End Sub
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
End Sub
31. Subrutina succesor pentru problema produsului cartezian a n mulimi utiliznd metoda
backtracking
Sub succesor_prod(am_suc As Boolean, st As stiva, k As Integer)
If st.ss(k) < a.v(k) Then
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
End Sub
MsgBox b
End Sub
k = k - 1
End If
Wend
End Sub
b = " "
For i = 1 To p
b = b + Str$(st.ss(i)) + ","
Next
MsgBox b
End Sub
End Sub
Else
max = st.ss(1)
For i = 2 To k - 1
If max < st.ss(i) Then
max = st.ss(i)
End If
Next
End If
If st.ss(k) < max + 1 And st.ss(k) < k Then
am_suc = True
st.ss(k) = st.ss(k) + 1
Else
am_suc = False
End If
End Sub
53. Funcia pentru a verifica dac un numr natural n este prim sau nu
Function prim(n As Integer) As Boolean
b = True
For i = 2 To Int(Sqr(n))
If n Mod i = 0 Then
b = False
i = Int(Sqr(n))
End If
Next
prim = b
End Function
Sub buton1_Click()
Dim n As Integer, ninv As Integer, n1 As Integer, sir As String
Do
n = InputBox(" n = ", y)
Loop Until n > 0
n1 = n
ninv = 0
sir = ""
While n <> 0
sir = sir + LTrim(RTrim(Str$(n Mod 10)))
ninv = ninv * 10 + n Mod 10
n = Int(n / 10)
Wend
MsgBox " numarul initial este : " + Str$(n1) + " numarul inversat
este: " + sir
End Sub
55. Algoritmul lui Euclid pentru calcularea CMMDC a dou numere naturale pozitive
Private Sub Buton10_Click()
Dim a As Integer, b As Integer, c As Integer
Do
a = InputBox("a = ", y)
b = InputBox("b = ", y)
a1 = a
b1 = b
Loop Until a > 0 And b > 0 And a > b
c = euclid2(a, b)
If c = 1 Then
MsgBox " nr. sunt prime intre ele (" + Str$(a1) + "," + Str$(b1)
+ ")"
Else
MsgBox "Cmmdc (" + Str$(a1) + "," + Str$(b1) + ")=" + Str$
(euclid2(a, b))
End If
End Sub
a.v(i) = a.v(i + 1)
a.v(i + 1) = x
k = 1
End If
Next
Loop Until k = 0
End Sub
n1 = n
s = 0
While n <> 0
s = s + n Mod 10
n = Int(n / 10)
Wend
MsgBox " suma cifrelor numarului n = " + Str$(n1) + " este : " +
Str$(s)
End Sub
63. Determinarea numerelor prime mai mici sau egale cu n utiliznd metoda direct
Sub buton4_Click()
Dim n As Integer, s As Long, n1 As Integer, i As Integer
Do
n = InputBox(" n = ", y)
Loop Until n > 0
n1 = n
If n = 2 Then
MsgBox "numerele prime sunt : 2"
Else
sir = "2,"
i = 3
While i <= n
If prim(i) = True Then
sir = sir + Str$(i) + ","
End If
i = i + 2
Wend
End If
MsgBox "numere prime sunt : " + sir
End Sub
Sub buton5_Click()
Dim n As Integer, a As vector, sir As String
Do
n = InputBox(" n = ", y)
Loop Until n > 0
For i = 1 To n
a.v(i) = i
Next
For i = 2 To Int(Sqr(n))
If a.v(i) <> 0 Then
j = 2 * i
While j <= n
j = j + i
a.v(j) = 0
Wend
End If
Next
sir = ""
For i = 2 To n
If a.v(i) <> 0 Then
sir = sir + Str$(i) + ","
End If
Next
MsgBox "Numerele prime sunt : " + sir
End Sub
Sub buton7_Click()
Dim n As Integer, a As vector, sir As String, n1 As Integer
Do
n = InputBox(" n = ", y)
Loop Until n > 0
n1 = n
For n = 1 To n1
Max = Int(n / 2)
nr = 0
For i = 1 To Max
For j = i To Max
If i * i * i + j * j * j = n Then
If nr = 0 Then
i1 = i
j1 = j
Else
i2 = i
j2 = j
End If
nr = nr + 1
End If
Next
Next
If nr > 1 Then
MsgBox Str$(n) + "=" + Str$(i1) + "^" + Str$(j1) + "+" + Str$
(i2) + "^" + Str$(j2)
End If
Next
End Sub
a = b
b = r
Loop Until Not (r = 0 And r = 1)
If r = 1 Then
euclid = 1
Else
euclid = a
End If
End Function
Sub Button15_Click()
Dim x As Integer, y As Integer, z As Integer, t As String, bb As
vector
Dim xx As Integer
Do
x = InputBox("a=", ib_title)
y = InputBox("b=", ib_title)
Loop Until (x > 0) And (y > 0) And (x >= y)
baza1 x, y, bb, xx
t = ""
MsgBox "n = " + Str$(xx)
For z = xx To 1 Step -1
t = t + Str$(bb.v(z))
Next
MsgBox t
End Sub
75. Quicksort
Sub Button18_Click()
Dim n As Integer, a As vector
cit_n "n = ", n
cit_date "a", n, a
'MsgBox "Sirul a este"
tipar "Sirul a este", n, a
divimp 1, n, a
'MsgBox "Sirul a sortat este"
tipar "Sirul a sortat este", n, a
End Sub
132
k = k - 1
End If
Wend
End Sub
134
Bibliografie
[1.] Brassard, G., Bratley, P. Algorithmics - Theory and Practice, Prentice-Hall, Englewood Cliffs,
1988.
[2.] Cormen, T.H., Leiserson, C.E., Rivest, R.L. Introduction to Algorithms, The MIT Press,
Cambridge, Masshusetts, 1992 (eighth printing).
[3.] Ellis, M., Stroustrup, B. The Annotated C++ Reference Manual, Addison-Wesley, Reading,
1991.
[4.] Graham, R.L., Knuth, D.E., Patashnik, O. Concrete Mathematics, Addison-Wesley, Reading,
1989.
[5.] Horowitz, E., Sahni, S. Fundamentals of Computer Algorithms, Computer Science Press,
Rockville, 1978.
[6.] Knuth, D.E. Tratat de programarea calculatoarelor. Algoritmi fundamentali, Editura Tehnica,
Bucuresti, 1974.
[7.] Knuth, D.E. Tratat de programarea calculatoarelor. Sortare si cautare, Editura Tehnica,
Bucuresti, 1976.
[8.] Lippman, S. B. C++ Primer, Addison-Wesley, Reading, 1989.
[9.] Livovschi, L., Georgescu, H. Sinteza si analiza algoritmilor, Editura Stiintifica si Enciclopedica,
Bucuresti, 1986.
[10.] Morariu N, Limbaje de programare, curs ID,2003
[11.] Sedgewick, R. Algorithms, Addison-Wesley, Reading, 1988.
[12.] Sedgewick, R. Algorithms in C, Addison-Wesley, Reading, 1990.
[13.] Sethi, R. Programming Languages. Concepts and Constructs, Addison-Wesley, Reading, 1989.
[14.] Smith, J.H. Design and Analysis of Algorithms, PWS-KENT Publishing Company, Boston, 1989.
[15.] Standish, T.A. Data Structure Techniques, Addison-Wesley, Reading, 1979.
[16.] Stroustrup, B. The C++ Programming Language, Addison-Wesley, Reading, 1991.
[17.] Stroustrup, B. The Design and Evolution of C++, Addison-Wesley, Reading, 1994.
[18.] http://thor.info.uaic.ro/~dlucanu/
135