Top Banner
VALERIU LUPU ALGORITMI. TEHNICI ŞI LIMBAJE DE PROGRAMARE 3
187

Algoritmi

Jan 03, 2023

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Algoritmi

VALERIU LUPU

ALGORITMI. TEHNICI ŞI LIMBAJE DE PROGRAMARE

3

Page 2: Algoritmi

EDITURA UNIVERSITĂŢII “ŞTEFAN cel MARE” SUCEAVA2007

4

Page 3: Algoritmi

Prefaţă

Cartea isi propune in primul rand sa fie un curs si nu o "enciclopedie" dealgoritmi. Pornind de la structurile de date cele mai uzuale si de la analizaeficientei algoritmilor, cartea se concentreaza pe principiile fundamentale deelaborare a algoritmilor: greedy, divide et impera, programare dinamica,backtracking. Majoritatea algoritmilor selectati au o conotatie estetica. Efortulnecesar pentru intelegerea elementelor mai subtile este uneori considerabil. Ce esteinsa un algoritm "estetic"? Putem raspunde foarte simplu: un algoritm este esteticdaca exprima mult in cuvinte putine. Un algoritm estetic este oare in mod necesar sieficient? Cartea raspunde si acestor intrebari.

In al doilea rand, cartea prezinta mecanismele interne esentiale ale limbajuluiVisual 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 pursi simplu "transcrisi" din pseudo-cod in limbajul Visual Basic, ci sunt reganditi dinpunct de vedere al programarii orientate pe obiect. Speram ca, dupa citirea cartii,veti dezvolta aplicatii de programare in mod iterative cat si recursiv si vetielabora implementari ale altor structuri de date. Programele pot fi scrise si inlimbajul C#. Acest limbaj se caracterizeaza, in principal, prin introducerea claselorparametrice si a unui mecanism de tratare a exceptiilor foarte avansat, facilitatideosebit de importante pentru dezvoltarea de biblioteci C#.

Fara a face concesii rigorii matematice, prezentarea este intuitiva, cunumeroase exemple. Am evitat, pe cat posibil, situatia in care o carte de informaticaincepe - spre disperarea ne-matematicienilor - cu celebrul "Fie ... ", sau cu odefinitie. Am incercat, pe de alta parte, sa evitam situatia cand totul "esteevident", sau "se poate demonstra". Fiecare capitol este conceput fluid, ca o micapoveste, cu putine referinte si note. Multe rezultate mai tehnice sunt obtinute caexercitii. Algoritmii sunt prezentati intr-un limbaj pseudo-cod compact, fara detaliiinutile.

Presupunem ca cititorul nu are la baza cel putin un curs introductiv inprogramare, fiindu-i straini termeni precum algoritm, recursivitate, functie,procedura si pseudo-cod. Exista mai multe modalitati de parcurgere a cartii. Infunctie de interesul si pregatirea cititorului, acesta poate alege oricare dinpartile referitoare la elaborarea, analiza, sau implementarea algoritmilor. Cuexceptia partilor de analiza a eficientei algoritmilor (unde sunt necesare elementede matematici superioare), cartea poate fi parcursa si de catre un elev de liceu.Pentru parcurgerea sectiunilor de implementare, este recomandabila cunoasterealimbajului Visual Basic.

S-a dovedit utila si experienta autorului de peste douzeci de ani indezvoltarea produselor software. Le multumesc pentru aprecieri pro/contra asupralucrării membrilor catedrei de informatică de la Facultatea de Ştiinţe Economice şiAdministraţie Publică.

Autorul,Conf. univ. dr. Lupu Valeriu

Universitatea “Ştefan cel Mare” SuceavaFacultatea de Ştiinţe Economice şi Administraţie Publică

Catedra de Informatică

5

Page 4: Algoritmi

Cuprins

Capitolul I Descrierea

algoritmilor ....................................

..............................................

.......

.......

.6Capitolul

II

Subprograme .....................................

..................................................

..........

.......

......1

6Capitolul

III

Metode de proiectare a

algoritmilor ....................................

..........................

.......

......2

3Capitolul

IV

Analiza

algoritmilor ....................................

..................................................

.......

......2

7Capitolul V Clase de

algoritmi ........................................

..................................................

.

.......

......3

4

Capitolul

VI

Evoluţia limbajelor de

programare ......................................

..........................

.......

......4

1Capitolul

VII

Limbajul Visual

Basic ...........................................

........................................

.......

......5

5Capitolul

VIII

Reguli privind alegerea unui limbaj de programare

......................................

.......

......6

9Capitolul

IX

Metoda

backtracking ....................................

.......

......7

6

Page 5: Algoritmi

................................................. 3Capitolul X Metoda Divide et

impera ...........................................

....................................

.......

......9

1Capitolul

XI

Metoda

Greedy ...........................................

..................................................

.

.......

......9

5

Capitolul

XII

Studii de caz –

Aplicaţii .......................................

.........................................

.......

....109

Bibliografi

e

..................................................

..................................................

.....................

.......

....135

7

Page 6: Algoritmi

CAPITOLUL I

DESCRIEREA ALGORITMILOR

1.1 Algoritm, program, programare

Apariţia primelor calculatoare electronice a constituit un salturiaş în direcţia automatizării activităţii umane. Nu există astăzidomeniu de activitate în care calculatorul să nu îşi arateutilitatea[18].

Calculatoarele pot fi folosite pentru a rezolva probleme, numaidacă pentru rezolvarea acestora se concep programe corespunzătoare derezolvare. Termenul de program (programare) a suferit schimbări înscurta istorie a informaticii. Prin anii '60 problemele rezolvate cuajutorul calculatorului erau simple şi se găseau algoritmi nu preacomplicaţi pentru rezolvarea lor. Prin program se înţelegea rezultatulscrierii unui algoritm într-un limbaj de programare. Din cauzacreşterii complexităţii problemelor, astăzi pentru rezolvarea uneiprobleme adesea vom concepe un sistem de mai multe programe.

Dar ce este un algoritm? O definiţie matematică, riguroasă, estegreu de dat, chiar imposibilă fără a introduce şi alte noţiuni. Vomîncerca în continuare o descriere a ceea ce se înţelege prin algoritm.

Ne vom familiariza cu această noţiune prezentând mai multeexemple de algoritmi şi observând ce au ei în comun. Cel mai vechiexemplu este algoritmul lui Euclid, algoritm care determină cel maimare divizor comun a două numere naturale. Evident, vom prezenta maimulţi algoritmi, cei mai mulţi fiind legaţi de probleme accesibileabsolvenţilor de liceu.

Vom constata că un algoritm este un text finit, o secvenţă finităde propoziţii ale unui limbaj. Din cauză că este inventat special înacest scop, un astfel de limbaj este numit limbaj de descriere aalgoritmilor. Fiecare propoziţie a limbajului precizează o anumităregulă de calcul, aşa cum se va observa atunci când vom prezentalimbajul Pseudocod.

Oprindu-ne la semnificaţia algoritmului, la efectul execuţieilui, vom observa că fiecare algoritm defineşte o funcţie matematică.De asemenea, din toate secţiunile următoare va reieşi foarte clar căun algoritm este scris pentru rezolvarea unei probleme. Din mai multeexemple se va observa însă că, pentru rezolvarea aceleaşi probleme,există mai mulţi algoritmi.

Pentru fiecare problemă P există date presupuse cunoscute (dateiniţiale pentru algoritmul corespunzător, A) şi rezultate care se cera fi găsite (date finale). Evident, problema s-ar putea să nu aibă

8

Page 7: Algoritmi

sens pentru orice date iniţiale. Vom spune că datele pentru careproblema P are sens fac parte din domeniul D al algoritmului A.Rezultatele obţinute fac parte dintr-un domeniu R, astfel că executândalgoritmul A cu datele de intrare xD vom obţine rezultatele rR. Vomspune că A(x)=r şi astfel algoritmul A defineşte o funcţie

A : D ---> R .Algoritmii au următoarele caracteristici: generalitate,

finitudine şi unicitate.Prin generalitate se înţelege faptul că un algoritm este aplicabil

pentru orice date iniţiale xD. Deci un algoritm A nu rezolvă problemaP cu nişte date de intrare, ci o rezolvă în general, oricare ar fiaceste date. Astfel, algoritmul de rezolvare a unui sistem liniar de necuaţii cu n necunoscute prin metoda lui Gauss, rezolvă orice sistemliniar şi nu un singur sistem concret.

Prin finitudine se înţelege că textul algoritmului este finit,compus dintr-un număr finit de propoziţii. Mai mult, numărultransformărilor ce trebuie aplicate unei informaţii admisibile xDpentru a obţine rezultatul final corespunzător este finit.

Prin unicitate se înţelege că toate transformările prin care treceinformaţia iniţială pentru a obţine rezultatul rR sunt binedeterminate de regulile algoritmului. Aceasta înseamnă că fiecare pasdin execuţia algoritmului dă rezultate bine determinate şi precizeazăîn mod unic pasul următor. Altfel spus, ori de câte ori am executaalgoritmul, pornind de la aceeaşi informaţie admisibilă xD,transformările prin care se trece şi rezultatele obţinute suntaceleaşi.

În descrierea algoritmilor se folosesc mai multe limbaje dedescriere, dintre care cele mai des folosite sunt:

- limbajul schemelor logice; - limbajul Pseudocod.

În continuare vom folosi pentru descrierea algoritmilor limbajulPseudocod care va fi definit în cele ce urmează. În ultima vremeschemele logice sunt tot mai puţin folosite în descrierea algoritmilorşi nu sunt deloc potrivite în cazul problemelor complexe. Prezentămînsă şi schemele logice, care se mai folosesc în manualele de liceu,întrucât cu ajutorul lor vom preciza în continuare semanticapropoziţiilor Pseudocod.

1.2 Scheme logice

Schema logică este un mijloc de descriere a algoritmilor prinreprezentare grafică. Regulile de calcul ale algoritmului suntdescrise prin blocuri (figuri geometrice) reprezentând operaţiile

9

Page 8: Algoritmi

(paşii) algoritmului, iar ordinea lor de aplicare (succesiuneaoperaţiilor) este indicată prin săgeţi. Fiecărui tip de operaţie îieste consacrată o figură geometrică (un bloc tip) în interiorul căreiase va înscrie operaţia din pasul respectiv.

Prin execuţia unui algoritm descris printr-o schemă logică seînţelege efectuarea tuturor operaţiilor precizate prin blocurileschemei logice, în ordinea indicată de săgeţi.

În descrierea unui algoritm, deci şi într-o schemă logică,intervin variabile care marchează atât datele cunoscute iniţial, câtşi rezultatele dorite, precum şi alte rezultate intermediare necesareîn rezolvarea problemei. Întrucât variabila joacă un rol central înprogramare este bine să definim acest concept. Variabila defineşte omărime care îşi poate schimba valoarea în timp. Ea are un nume şi,eventual, o valoare. Este posibil ca variabila încă să nu fi primitvaloare, situaţie în care vom spune că ea este neiniţializată.Valorile pe care le poate lua variabila aparţin unei mulţimi D pe careo vom numi domeniul variabilei. În concluzie vom înţelege prinvariabilă tripletul

(nume, domeniul D, valoare)unde valoare aparţine mulţimii D {nedefinit}.

Blocurile delimitatoare Start şi Stop (Fig.1.2.1. a şi 1.2.1. b)vor marca începutul respectiv sfârşitul unui algoritm dat printr-oschemă logică. Descrierea unui algoritm prin schemă logică va începecu un singur bloc Start şi se va termina cu cel puţin un bloc Stop.

Blocurile de intrare/ieşire Citeşte şi Tipăreşte (Fig. 1.2.1. c şi d)indică introducerea unor Date de intrare respectiv extragerea unorRezultate finale. Ele permit precizarea datelor iniţiale cunoscute înproblemă şi tipărirea rezultatelor cerute de problemă. Blocul Citeşteiniţializează variabilele din lista de intrare cu valoricorespunzătoare, iar blocul Tipăreşte va preciza rezultatele obţinute(la execuţia pe calculator cere afişarea pe ecran a valorilorexpresiilor din lista de ieşire).

Blocurile de atribuire (calcul) se utilizează în descriereaoperaţiilor de atribuire (:=). Printr-o astfel de operaţie, uneivariabile var i se atribuie valoarea calculată a unei expresii expr(Fig.1.2.1. e).

10

Page 9: Algoritmi

Fig.1.2.1. Blocurile schemelor logice

Blocurile de decizie marchează punctele de ramificaţie alealgoritmului în etapa de decizie. Ramificarea poate fi dublă (blocullogic, Fig.1.2.1.f) sau triplă (blocul aritmetic, Fig. 1.2.1.g). Blocul dedecizie logic indică ramura pe care se va continua execuţia algoritmuluiîn funcţie de îndeplinirea (ramura Da) sau neîndeplinirea (ramura Nu)unei condiţii. Condiţia care se va înscrie în blocul de decizie logicva fi o expresie logică a cărei valoare poate fi una dintre valorile"adevărat" sau "fals". Blocul de decizie aritmetic va hotărî ramura decontinuare a algoritmului în funcţie de semnul valorii expresieiaritmetice înscrise în acest bloc, care poate fi negativă, nulă saupozitivă.

Blocurile de conectare marchează întreruperile săgeţilor delegătură dintre blocuri, dacă din diverse motive s-au efectuat astfelde întreruperi (Fig.1.2.1.h).

Pentru exemplificare vom da în continuare două scheme logice,corespunzătoare unor algoritmi pentru rezolvarea problemelor P1.2.1 şiP1.2.2.

P1.2.1. Să se rezolve ecuaţia de grad doi aX2+bX+c=0 (a,b,cR _i a0).Metoda de rezolvare a ecuaţiei de gradul doi este cunoscută. Ecuaţiapoate avea rădăcini reale, respectiv complexe, situaţie recunoscutădupă semnul discriminantului d = b2 - 4ac.

11

Page 10: Algoritmi

Fig.1.2.2. Algoritm pentru rezolvareaecuaţiei de gradul doi

Fig.1.2.3. Algoritmpentru calculul unei

sume.

Algoritmul de rezolvare a problemei va citi mai întâi dateleproblemei, marcate prin variabilele a, b şi c. Va calcula apoidiscriminantul d şi va continua în funcţie de valoarea lui d, aşa cumse 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 conţine imediat după bloculSTART un bloc de citire, care precizează datele cunoscute în problemă,apoi o parte care calculează suma cerută şi un bloc de tipărire asumei găsite, înaintea blocului STOP. Partea care calculează suma Scerută are un bloc pentru iniţializarea cu 0 a acestei sume, apoiblocuri pentru parcurgerea numerelor: x1, x2…xn şi adunarea celorpozitive la suma S. Pentru această parcurgere se foloseşte o variabilăcontor i, care este iniţializată cu 1 şi creşte mereu cu 1 pentru aatinge valoarea n, indicele ultimului număr dat.

Schemele logice dau o reprezentare grafică a algoritmilor cuajutorul unor blocuri de calcul. Execuţia urmează sensul indicat desăgeată, putând avea loc reveniri în orice punct din schema logică.

12

Page 11: Algoritmi

Din acest motiv se poate obţine o schemă logică încâlcită, greu deurmărit. Rezultă importanţa compunerii unor scheme logice structurate(D-scheme, după Djikstra), care să conţină numai anumite structuristandard de calcul şi în care drumurile de la START la STOP să fieuşor de urmărit.

1.3. Limbajul PSEUDOCOD

Limbajul Pseudocod este un limbaj inventat în scopul proiectăriialgoritmilor şi este format din propoziţii asemănătoare propoziţiilorlimbii române, care corespund structurilor de calcul folosite înconstruirea algoritmilor. Acesta va fi limbajul folosit de noi înproiectarea algoritmilor şi va fi definit în cele ce urmează. Ţinândseama că obţinerea unui algoritm pentru rezolvarea unei probleme nueste întotdeauna o sarcină simplă, că în acest scop sunt folositeanumite metode pe care le vom descrie în capitolele următoare, înetapele intermediare din obţinerea algoritmului vom folosi propoziţiicurente din limba română. Acestea sunt considerate elemente nefinisatedin algoritm, asupra cărora trebuie să se revină şi le vom numipropoziţii nestandard. Deci limbajul Pseudocod are două tipuri depropoziţii: propoziţii standard, care vor fi prezentate fiecare cusintaxa şi semnificaţia (semantica) ei şi propoziţii nestandard. Aşacum se va arăta mai târziu, propoziţiile nestandard sunt texte caredescriu părţi ale algoritmului încă incomplet elaborate, nefinisate,asupra cărora urmează să se revină.

Pe lângă aceste propoziţii standard şi nestandard, în textulalgoritmului vom mai introduce propoziţii explicative, numitecomentarii. Pentru a le distinge de celelalte propoziţii, comentariilevor fi închise între acolade. Rolul lor va fi explicat puţin maitârziu.

Propoziţiile standard ale limbajului Pseudocod folosite înaceastă lucrare, corespund structurilor de calcul prezentate în figura1.3.1 şi vor fi prezentate în continuare. Fiecare propoziţie standardîncepe cu un cuvânt cheie, aşa cum se va vedea în cele ce urmează.Pentru a deosebi aceste cuvinte de celelalte denumiri, construite deprogramator, în acest capitol vom scrie cuvintele cheie cu literemari. Menţionăm că şi propoziţiile simple se termină cu caracterul ';'în timp ce propoziţiile compuse, deci cele în interiorul cărora seaflă alte propoziţii, au un marcaj de sfârşit propriu. De asemenea,menţionăm că propoziţiile limbajului Pseudocod vor fi luate în seamăîn ordinea întâlnirii lor în text, asemenea oricărui text al limbiiromâne.

13

Page 12: Algoritmi

Prin execuţia unui algoritm descris în Pseudocod se înţelegeefectuarea operaţiilor precizate de propoziţiile algoritmului, înordinea citirii lor.

În figura 1.3.1, prin A, B s-au notat subscheme logice, adicăsecvenţe de oricâte structuri construite conform celor trei regulimenţionate în continuare.

Structura secvenţială (fig.1.3.1.a) este redată prin concatenareapropoziţiilor, simple sau compuse, ale limbajului Pseudocod, care vorfi executate în ordinea întâlnirii lor în text.

a)structur

asecvenţi

ală

b) structuraalternativă

c) structurarepetitivă

Figura 1.3.1. Structurile elementare de calcul

Propoziţiile simple din limbajul Pseudocod sunt CITEŞTE,TIPAREŞTE, FIE şi apelul de subprogram. Propoziţiile compuse corespundstructurilor alternative şi repetitive.

Structura alternativă (fig.1.3.1.b) este redată în Pseudocod prinpropoziţia DACĂ, prezentată în secţiunea 1.3.2, iar structurarepetitivă din fig.1.3.1.c este redată în Pseudocod prin propoziţiaCÂT TIMP, prezentată în secţiunea 1.3.3.

Bohm şi Jacopini au demonstrat că orice algoritm poate fi descrisfolosind numai aceste trei structuri de calcul.

Propoziţiile DATE şi REZULTATE sunt folosite în faza despecificare a problemelor, adică enunţarea riguroasă a acestora.Propoziţia DATE se foloseşte pentru precizarea datelor iniţiale, decia datelor considerate cunoscute în problemă (numite şi date deintrare) şi are sintaxa:

DATE listă;unde listă conţine toate numele variabilelor a căror valoare iniţialăeste cunoscută. În general, prin listă se înţelege o succesiune deelemente de acelaşi fel despărţite prin virgulă. Deci în propoziţiaDATE, în dreapta acestui cuvânt se vor scrie acele variabile caremarchează mărimile cunoscute în problemă.

14

Page 13: Algoritmi

Pentru precizarea rezultatelor dorite se foloseşte propoziţiastandard

REZULTATE listă;în construcţia "listă" ce urmează după cuvântul REZULTATE fiindtrecute numele variabilelor care marchează (conţin) rezultatele ceruteîn problemă.

Acum putem preciza mai exact ce înţelegem prin cunoaştereacompletă a problemei de rezolvat. Evident, o problemă este cunoscutăatunci când se ştie care sunt datele cunoscute în problemă şi cerezultate trebuiesc obţinute. Deci pentru cunoaşterea unei problemeeste necesară precizarea variabilelor care marchează date consideratecunoscute în problemă, care va fi reflectată printr-o propoziţie DATEşi cunoaşterea exactă a cerinţelor problemei, care se va reflecta prinpropoziţii REZULTATE. Variabilele prezente în aceste propoziţii auanumite semnificaţii, presupuse cunoscute. Cunoaşterea acestora,scrierea lor explicită, formează ceea ce vom numi în continuarespecificarea problemei. Specificarea unei probleme este o activitatefoarte importantă dar nu şi simplă.

De exemplu, pentru rezolvarea ecuaţiei de gradul al doilea,specificarea problemei, scrisă de un începător, poate fi:

DATE a,b,c; { Coeficienţii ecuaţiei }REZULTATE x1,x2; { Rădăcinile ecuaţiei }

Această specificaţie este însă incompletă dacă ecuaţia nu are rădăcinireale. În cazul în care rădăcinile sunt complexe putem nota prin x1, x2partea reală respectiv partea imaginară a rădăcinilor. Sau pur şisimplu, nu ne interesează valoarea rădăcinilor în acest caz, ci doarfaptul că ecuaţia nu are rădăcini reale. Cu alte cuvinte avem nevoiede un mesaj care să ne indice această situaţie (vezi schema logică1.2.2), sau de un indicator, fie el ind. Acest indicator va luavaloarea 1 dacă rădăcinile sunt reale şi valoarea 0 în caz contrar.Deci specificaţia corectă a problemei va fi

DATE a,b,c; { Coeficienţii ecuaţiei }REZULTATE ind, {Un indicator: 1=rădăcini reale, 0=complexe}x1,x2; { Rădăcinile ecuaţiei, în cazul ind=1,}

{respectiv partea reală şi cea }{imaginară în cazul ind=0}

Evident că specificarea problemei este o etapă importantă pentrugăsirea unei metode de rezolvare şi apoi în proiectarea algoritmuluicorespunzător. Nu se poate rezolva o problemă dacă aceasta nu estebine cunoscută, adică nu avem scrisă specificarea problemei. Cunoaştecomplet problema este prima regulă ce trebuie respectată pentru aobţine cât mai repede un algoritm corect pentru rezolvarea ei.

15

Page 14: Algoritmi

1.3.1 Algoritmi liniari

Propoziţiile CITEŞTE şi TIPĂREŞTE sunt folosite pentruiniţializarea variabilelor de intrare cu datele cunoscute în problemă,respectiv pentru tipărirea (aflarea) rezultatelor obţinute. În etapade programare propriu-zisă acestor propoziţii le corespund într-unlimbaj de programare instrucţiuni de intrare-ieşire.

Propoziţia CITEŞTE se foloseşte pentru precizarea dateloriniţiale, deci a datelor considerate cunoscute în problemă (numite şidate de intrare) şi are sintaxa:

CITEŞTE listă ;unde listă conţine toate numele variabilelor a căror valoare iniţialăeste cunoscută.

Deci în propoziţia CITEŞTE, în dreapta acestui cuvânt se vorscrie acele variabile care apar în propoziţia DATE în specificareaproblemei. Se subînţelege că aceste variabile sunt iniţializate cuvalorile cunoscute corespunzătoare.

Pentru aflarea rezultatelor dorite, pe care calculatorul o vaface prin tipărirea lor pe hârtie sau afişarea pe ecran, se foloseştepropoziţia standard

TIPĂREŞTE listă ;în construcţia listă ce urmează după cuvântul TIPĂREŞTE fiind trecutenumele variabilelor a căror valori dorim să le aflăm. Ele sunt deobicei rezultatele cerute în problemă, specificate şi în propoziţiaREZULTATE.

Blocului de atribuire dintr-o schemă logică îi corespunde înPseudocod propoziţia standard [FIE] var := expresie ;Această propoziţie este folosită pentru a indica un calcul algebric,al expresiei care urmează după simbolul de atribuire ":=" şi deatribuire a rezultatului obţinut variabilei var. Expresia din dreaptasemnului de atribuire poate fi orice expresie algebrică simplă,cunoscută din manualele de matematică din liceu şi construită cu celepatru operaţii: adunare, scădere, înmulţire şi împărţire (notate princaracterele +, -, *, respectiv /).

Prin scrierea cuvântului FIE între paranteze drepte se indicăposibilitatea omiterii acestui cuvânt din propoziţie. El s-a folositcu gândul ca fiecare propoziţie să înceapă cu un cuvânt al limbiiromâne care să reprezinte numele propoziţiei. De cele mai multe orivom omite acest cuvânt. Atunci când vom scrie succesiv mai multepropoziţii de atribuire vom folosi cuvântul FIE numai în primapropoziţie, omiţându-l în celelalte.

Din cele scrise mai sus rezultă că o variabilă poate fiiniţializată atât prin atribuire (deci dacă este variabila din stânga

16

Page 15: Algoritmi

semnului de atribuire :=) cât şi prin citire (când face parte dinlista propoziţiei CITEŞTE). O greşeală frecventă pe care o facîncepătorii este folosirea variabilelor neiniţializate. Evident că oexpresie în care apar variabile care nu au valori nu poate ficalculată, ea nu este definită. Deci nu folosiţi variabileneiniţializate.

Pentru a marca începutul descrierii unui algoritm vom folosipropoziţia:

ALGORITMUL nume ESTE: fără a avea o altă semnificaţie. De asemenea, prin cuvântulSFALGORITM vom marca sfârşitul unui algoritm.

Algoritmii care pot fi descrişi folosind numai propoziţiileprezentate mai sus se numesc algoritmi liniari.

Ca exemplu de algoritm liniar prezentăm un algoritm ce determinăviteza v cu care a mers un autovehicul ce a parcurs distanţa D întimpul T.

ALGORITMUL VITEZA ESTE: { A1: Calculează viteza }{ D = Distanţa (spaţiul) }{ T = Timpul; V = Viteza }

CITEŞTE D,T; { v:= spaţiu/timp } FIE V:=D/T; TIPĂREŞTE VSFALGORITM

1.3.2 Algoritmi cu ramificaţii

Foarte mulţi algoritmi execută anumite calcule în funcţie desatisfacerea unor condiţii. Aceste calcule sunt redate de structuraalternativă prezentată în figura 1.3.1.b, căreia îi corespunde propoziţiaPseudocod

DACĂ cond ATUNCI A ALTFEL B SFDACĂsau varianta redusă a ei, DACĂ cond ATUNCI A SFDACĂfolosită în cazul în care grupul de propoziţii B este vid.

Aceste propoziţii redau în Pseudocod structura alternativă decalcul. Ele cer mai întâi verificarea condiţiei scrise după cuvântulDACĂ. În caz că această condiţie este adevărată se va executa grupulde propoziţii A. În cazul în care această condiţie este falsă se vaexecuta grupul de propoziţii B, dacă este prezentă ramura ALTFEL.Indiferent care dintre secvenţele A sau B a fost executată, se vacontinua cu propoziţia următoare propoziţiei DACĂ.

O generalizare a structurii alternative realizată de propoziţiaDACĂ este structura selectivă:

17

Page 16: Algoritmi

SELECTEAZĂ i DINTRE v1: A1; v2: A2; . . . vn: An SFSELECTEAZĂ

structură echivalentă cu următorul text Pseudocod: DACĂ i=v1 ATUNCI A1 ALTFEL DACĂ i=v2 ATUNCI A2 ALTFEL . . . DACĂ i=vn ATUNCI An SFDACĂ . . . SFDACĂSFDACĂ

Cu propoziţiile prezentate până aici putem deja descrie destuialgoritmi. Aceştia se numesc algoritmi cu ramificaţii.

Ca exemplu vom scrie un algoritm pentru rezolvarea ecuaţiei degradul al doilea. Am scris mai sus specificaţia acestei probleme şi amprecizat semnificaţia variabilelor respective. Pe lângă acestevariabile, pentru rezolvarea problemei mai avem nevoie de douăvariabile auxiliare:

delta - pentru a reţine discriminantul ecuaţiei; r - pentru a reţine valoarea radicalului folosit în exprimarea

rădăcinilor. Ajungem uşor la algoritmul dat în continuare.

ALGORITMUL ECGRDOI ESTE: { Algoritmul 2: Rezolvarea }{ ecuaţiei de gradul doi }

CITEŞTE a,b,c; { a,b,c = Coeficienţii ecuaţiei } FIE delta:=b*b-4*a*c; DACĂ delta<0 ATUNCI ind:=0; { rădăcini complexe } r:=radical din (-delta); x1:=-b/(a+a); x2:=r/(a+a); ALTFEL ind:=1; { rădăcini reale } r:=radical din delta; x1:=(-b-r)/(a+a); x2:=(-b+r)/(a+a); SFDACĂ TIPĂREŞTE ind, x1,x2;SFALGORITM

18

Page 17: Algoritmi

1.3.3 Algoritmi ciclici

În rezolvarea multor probleme trebuie să efectuăm aceleaşicalcule de mai multe ori, sau să repetăm calcule asemănătoare. Deexemplu, pentru a calcula suma a două matrice va trebui să adunăm unelement al primei matrice cu elementul de pe aceeaşi poziţie din adoua matrice, această adunare repetându-se pentru fiecare poziţie.Alte calcule trebuiesc repetate în funcţie de satisfacerea unorcondiţii. În acest scop în limbajul Pseudocod există trei propoziţiistandard: CÂTTIMP, REPETĂ şi PENTRU.

Propoziţia CÂTTIMP are sintaxa CÂTTIMP cond EXECUTĂ ASFCÂT i cere execuţia repetată a grupului de propoziţii A, în funcţie decondiţia "cond". Mai exact, se evaluează condiţia "cond"; dacă aceastaeste adevărată se execută grupul A şi se revine la evaluareacondiţiei. Dacă ea este falsă execuţia propoziţiei se termină şi secontinuă cu propoziţia care urmează după SFCÂT. Dacă de prima datăcondiţia este falsă grupul A nu se va executa niciodată, altfel se varepeta execuţia grupului de propoziţii A până când condiţia va devenifalsă. Din cauză că înainte de execuţia grupului A are loc verificareacondiţiei, această structură se mai numeşte structură repetitivă condiţionatăanterior. Ea reprezintă structura repetitivă prezentată în figura1.3.1.c.

Ca exemplu de algoritm în care se foloseşte această propoziţiedăm algoritmul lui Euclid pentru calculul celui mai mare divizor comuna două numere.

ALGORITMUL Euclid ESTE: {A3: Cel mai mare divizor comun} CITEŞTE n1,n2; {Cele două numere a căror divizor se cere} FIE d:=n1; i:=n2; CÂTTIMP i0 EXECUTĂ r:=d modulo i; d:=i; i:=r SFCÂT TIPĂREŞTE d; { d= cel mai mare divizor comun al }SFALGORITM { numerelor n1 şi n2 }

În descrierea multor algoritmi se întâlneşte structura repetitivăcondiţionată posterior:

REPETĂ A PÂNĂ CÂND cond SFREPstructură echivalentă cu: CÂTTIMP not(cond) EXECUTĂ A SFCÂT

Deci ea cere execuţia necondiţionată a lui A şi apoi verificareacondiţiei "cond". Va avea loc repetarea execuţiei lui A până cândcondiţia devine adevărată. Deoarece condiţia se verifică după primaexecuţie a grupului A această structură este numită structura repetitivăcondiţionată posterior, prima execuţie a blocului A fiind necondiţionată.

19

Page 18: Algoritmi

O altă propoziţie care cere execuţia repetată a unei secvenţe Aeste propoziţia

PENTRU c:=li ;lf [;p] EXECUTĂ A SFPENTRUEa defineşte structura repetitivă predefinită, cu un număr determinat deexecuţii ale grupului de propoziţii A şi este echivalentă cu secvenţa

c:=li ; final:=lf ;REPETĂ

Ac:=c+p

PÂNĂCÂND (c>final şi p>0) sau (c<final şi p<0) SFREP

Se observă că, în sintaxa propoziţiei PENTRU, pasul p este închisîntre paranteze drepte. Prin aceasta indicăm faptul că el esteopţional, putând să lipsească. În cazul în care nu este prezent,valoarea lui implicită este 1.

Semnificaţia propoziţiei PENTRU este clară. Ea cere repetareagrupului de propoziţii A pentru toate valorile contorului c cuprinseîntre valorile expresiilor li şi lf (calculate o singură dată înainte deînceperea ciclului), cu pasul p. Se subînţelege că nu trebuie sămodificăm valorile contorului în nici o propoziţie din grupul A. Demulte ori aceste expresii sunt variabile simple, iar unii programatorimodifică în A valorile acestor variabile, încălcând semnificaţiapropoziţiei PENTRU. Deci, nu recalcula limitele şi nu modificavariabila de ciclare (contorul) în interiorul unei structurirepetitive PENTRU).

Să observăm, de asemenea, că prima execuţie a grupului A esteobligatorie, abia după modificarea contorului verificându-se condiţiade continuare a execuţiei lui A.

Ca exemplu, să descriem un algoritm care găseşte minimul şimaximul componentelor unui vector de numere reale. Vom nota prin Xacest vector, deci X = (x1, x2, ... , xn) .

Specificaţia problemei este următoarea: DATE n,(xi ,i=1,n); REZULTATE valmin,valmax;

iar semnificaţia acestor variabile se înţelege din cele scrise maisus. Pentru rezolvarea problemei vom examina pe rând cele ncomponente. Pentru a parcurge cele n componente avem nevoie de uncontor care să precizeze poziţia la care am ajuns. Fie i acest contor.Uşor se ajunge la următorul algoritm:

ALGORITMUL MAXMIN ESTE { Algoritmul 5: Calculul } { valorii minime şi maxime } CITEŞTE n,(xi,i=1,n); FIE valmin:=x1; valmax:=x1;

20

Page 19: Algoritmi

PENTRU i:=2,n EXECUTĂ DACĂ xi<valmin ATUNCI valmin:=xi SFDACĂ DACĂ xi>valmax ATUNCI valmax:=xi SFDACĂ SFPENTRU TIPĂREŞTE valmin,valmax; SFALGORITM

Un rol important în claritatea textului unui algoritm îl audenumirile alese pentru variabile. Ele trebuie să reflectesemnificaţia variabilelor respective. Deci alege denumiri sugestivepentru variabile, care să reflecte semnificaţia lor.

În exemplul de mai sus denumirile valmin şi valmax spun cititoruluice s-a notat prin aceste variabile.

1.4 Calculul efectuat de un algoritm

Fie X1, X2, ..., Xn, variabilele ce apar în algoritmul A. În oricemoment al execuţiei algoritmului, fiecare variabilă are o anumităvaloare, sau este încă neiniţializată.

Vom numi stare a algoritmului A cu variabilele menţionatevectorul

s = ( s1,s2,...,sn )format din valorile curente ale celor n variabile ale algoritmului.

Este posibil ca variabila Xj să fie încă neiniţializată, deci sănu aibă valoare curentă, caz în care sj este nedefinită, lucru notat încontinuare prin semnul întrebării '?'.

Prin executarea unei anumite instrucţiuni unele variabile îşischimbă valoarea, deci algoritmul îşi schimbă starea.

Se numeşte calcul efectuat de algoritmul A o secvenţă de stări s0, s1, s2, ..., sm

unde s0 este starea iniţială cu toate variabilele neiniţializate, iar sm

este starea în care se ajunge după execuţia ultimei propoziţii dinalgoritm.

1.5 Rafinare în paşi succesivi

Adeseori algoritmul de rezolvare a unei probleme este rezultatulunui proces complex, în care se iau mai multe decizii şi se precizeazătot ceea ce iniţial era neclar. Observaţia este adevărată mai ales încazul problemelor complicate, dar şi pentru probleme mai simple înprocesul de învăţământ. Este vorba de un proces de detaliere pas cupas a specificaţiei problemei, proces denumit şi proiectaredescendentă, sau rafinare în paşi succesivi. Algoritmul apare în maimulte versiuni succesive, fiecare versiune fiind o detaliere a

21

Page 20: Algoritmi

versiunii precedente. În versiunile iniţiale apar propoziţiinestandard, clare pentru cititor, dar neprecizate prin propoziţiistandard. Urmează ca în versiunile următoare să se revină asupra lor.Algoritmul apare astfel în versiuni succesive, tot mai complet de la oversiune la alta.

Apare aici o altă regulă importantă în proiectarea algoritmului:amână pe mai târziu detaliile nesemnificative; concentrează-ţi atenţiala deciziile importante ale momentului.

22

Page 21: Algoritmi

CAPITOLUL II

SUBPROGRAME

Conceptul de SUBPROGRAM

Orice problemă poate apare ca o subproblemă S a unei probleme maicomplexe C. Algoritmul de rezolvare a problemei S devine în acest cazun SUBPROGRAM pentru algoritmul de rezolvare a problemei C.

Pentru a defini un SUBPROGRAM vom folosi propoziţia standard SUBPROGRAMUL nume (lpf) ESTE:

unde nume este numele SUBPROGRAMului definit, iar lpf este listaparametrilor formali. Aceştia sunt formaţi din variabilele caremarchează datele de intrare (cele presupuse cunoscute) şi variabilelecare marchează datele de ieşire (rezultatele obţinute de SUBPROGRAM).

Această propoziţie este urmată de textul efectiv alSUBPROGRAMului, text care precizează calculele necesare rezolvăriisubproblemei corespunzătoare. Descrierea se va încheia cu cuvântulSFSUBPROGRAM sau SF-nume.

Dăm ca exemplu un SUBPROGRAM cu numele MAXIM, care găseştemaximul dintre componentele vectorului X = (x1,x2, ..., xn).

Datele cunoscute pentru acest SUBPROGRAM sunt vectorul X şinumărul n al componentelor vectorului X. Ca rezultat vom obţine maximulcerut, pe care-l vom nota cu max. Deci lista parametrilor formaliconţine trei variabile, n, X şi max. SUBPROGRAMul este dat încontinuare.

SUBPROGRAMUL maxim(n,X,max) ESTE: FIE max:=x1; PENTRU i:=2;n EXECUTĂ DACĂ xi>max ATUNCI max:=xi SFDACĂ SFPENTRUSF-maxim

În cadrul multor algoritmi este necesar calculul valorilor uneifuncţii în diferite puncte. Este necesar să definim funcţia printr-unSUBPROGRAM de tip funcţie.

Pentru definirea unui SUBPROGRAM de tip funcţie se foloseşte unantet care precizează numele funcţiei şi variabilele de care depindeea. SUBPROGRAMul are forma:

FUNCŢIA nume(lpf) ESTE: {Antetul funcţiei} text {corpul funcţiei}SF-nume {marca de sfârşit}

23

Page 22: Algoritmi

În corpul funcţiei trebuie să existe cel puţin o atribuire încare numele funcţiei apare în partea stângă, deci prin care funcţiaprimeşte o valoare.

Dăm ca exemplu o funcţie numar : R --> {2,3,4,5}, definitămatematic astfel:

În Pseudocod descrierea este următoarea:FUNCŢIA 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

Am văzut că definiţia unei funcţii constă dintr-un antet şidintr-un bloc care va defini acţiunile prin care se calculeazăvaloarea funcţiei. În antet se precizează numele funcţiei şi listaparametrilor formali.

În concluzie, există două categorii de SUBPROGRAMi: de tipfuncţie şi SUBPROGRAMi propriu-zişi, cărora li se mai spune şiproceduri. Importanţa lor va fi subliniată prin toate exemplele careurmează în acest curs. În încheiere menţionăm că subprogramele de tipfuncţie se folosesc în scopul definirii funcţiilor, aşa cum suntcunoscute ele din matematică, în timp ce SUBPROGRAMii de tip procedurăse referă la rezolvarea unor probleme ce apar ca subprobleme, fiindalgoritmi de sine stătători.

Apelul unui SUBPROGRAM

Am văzut că un SUBPROGRAM este dedicat rezolvării uneisubprobleme S a unei probleme mai complexe C. Algoritmul corespunzătorproblemei C va folosi toate operaţiile necesare rezolvării problemei S,deci va folosi ca parte întregul SUBPROGRAM conceput pentru rezolvareasubproblemei S. Spunem că el va apela acest SUBPROGRAM.

În Pseudocod apelul unei funcţii se face scriind într-o expresienumele funcţiei urmat de lista parametrilor actuali. Trebuie să existeo corespondenţă biunivocă între parametrii actuali şi cei formalifolosiţi în definiţia funcţiei. Deşi denumirile variabilelor din celedouă liste pot să difere, rolul variabilelor care se corespund esteacelaşi. Mai exact, parametrul formal şi parametrul actualcorespunzător trebuie să se refere la aceeaşi entitate, trebuie să

24

Page 23: Algoritmi

aibă aceeaşi semnificaţie, să reprezinte aceeaşi structură de date.Putem considera că în timpul execuţiei algoritmului cei doi parametridevin identici.

Folosirea unui SUBPROGRAM în cadrul unui algoritm se face apelândacest SUBPROGRAM prin propoziţia standard CHEAMĂ nume (lpa); unde nume este numele SUBPROGRAMului apelat iar lpa este listaparametrilor actuali. Această listă conţine toate datele de intrare(cele cunoscute în subproblema corespunzătoare) şi toate rezultateleobţinute în SUBPROGRAM. Şi în acest caz între lista parametrilorformali din definiţia SUBPROGRAMului şi lista parametrilor actuali dinpropoziţia de apel trebuie să existe o corespondenţă biunivocă, ca şiîn cazul funcţiilor. Ca o primă verificare a respectării acesteicorespondenţe, subliniem că numărul parametrilor actuali trebuie săcoincidă cu numărul parametrilor formali.

Ca exemplu de apelare a funcţiilor, dăm în continuare un programpentru a calcula a câta zi din anul curent este ziua curentă(zi,luna,an). El foloseşte un subprogram de tip funcţie pentru a obţinenumărul zilelor lunii cu numărul de ordine i şi un altul pentru averifica dacă un an este bisect sau nu. Aceste două funcţii sunt:

- NRZILE(i) furnizează numărul zilelor existente în luna i aunui an nebisect;- BISECT(an) adevărată dacă anul dintre paranteze este bisect.

Algoritmul este următorul:ALGORITMUL NUMĂRĂZILE ESTE: CITEŞTE 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Ă TIPĂREŞTE nr;SFALGORITM

Să observăm că în proiectarea acestui algoritm nu este necesar săcunoaştem textul SUBPROGRAMilor folosiţi, ci doar specificarea acestorSUBPROGRAMi, numele lor şi lista parametrilor formali. La acest nivelaccentul trebuie să cadă pe proiectarea algoritmului care apelează,urmând să se revină ulterior la proiectarea SUBPROGRAMilor apelaţi,conform specificaţiei acestora. În cazul de faţă este necesarădescrierea funcţiilor NRZILE(i) şi BISECT(an). Lăsăm această descriere catemă pentru cititor.

25

Page 24: Algoritmi

Ca exemplu de apelare a unei proceduri vom scrie mai jos oprocedură care efectuează suma a două polinoame.Un polinom P(X) este dat prin gradul său, m, şi prin vectorulcoeficienţilor P = (p0, p1, ..., pm) (prin pi s-a notat coeficientul lui Xi).

Procedura SUMAPOL(m,P,n,Q,r,S) trebuie să efectueze suma S(X) =P(X)+Q(X),unde P este un polinom de gradul m, iar Q este un polinom de gradul n,date. Suma lor, S, va fi un polinom de gradul r calculat în SUBPROGRAM.Pentru efectuarea ei este utilă o altă procedură care adună la suma S(X)un alt polinom, T(X), de grad mai mic sau egal decât gradul polinomuluiS(X). O astfel de procedură se dă în continuare.

SUBPROGRAMUL SUMAPOL1(n,T,r,S) ESTE: {n r} {S(X):=S(X)+T(X)} PENTRU i:=0;n EXECUTĂ si := si+ti

SFPENTRUSF-SUMAPOL1

SUBPROGRAMul SUMAPOL apelează acest SUBPROGRAM, aşa cum se poatevedea în continuare.

SUBPROGRAMUL SUMAPOL(m,P,n,Q,r,S) ESTE: {S(X):=P(X)+Q(X)} DACĂ m<n ATUNCI r:=n; S:=Q; CHEAMĂ SUMAPOL1(m,P,r,S) ALTFEL r:=m; S:=P; CHEAMĂ SUMAPOL1(n,Q,r,S) SFDACĂSF-SUMAPOL

Să observăm că în textul acestui SUBPROGRAM am extinssemnificaţia propoziţiei de atribuire, permiţând atribuirea S:=Q. Acestlucru este normal întrucât S notează un polinom, iar Q este un polinomcunoscut; prin atribuire S primeşte o valoare iniţială, cea dată depolinomul Q.

Subliniem că atribuirea v := uva fi corectă în cazul în care variabilele u şi v reprezintă aceleaşiobiecte matematice, deci au aceeaşi semnificaţie.

Alte exemple

Ca un al doilea exemplu de definire şi folosire a SUBPROGRAMilor,să considerăm următoarea problemă:

Se dau trei mulţimi de numere: A = { a1, a2, ... , am } B = { b1, b2, ... , bn }

26

Page 25: Algoritmi

C = { c1, c2, ... , cp } Se cere să se tipărească în ordine crescătoare elementele fiecărei mulţimi, precum şi a

mulţimilor A U B, B U C, C U A. În rezolvarea acestei probleme se întâlnesc următoarele

subprobleme: S1: Să se citească elementele unei mulţimi; S2: Să se efectueze reuniunea a două mulţimi; S3: Să se tipărească elementele unei mulţimi; S4: Să se ordoneze crescător elementele unei mulţimi.

Presupunând că pentru rezolvarea acestor subprobleme am conceputSUBPROGRAMii:

CITMUL(m,A); REUNIUNE(m,A,n,B,k,R);TIPMUL(m,A); ORDON(m,A);

care sunt specificaţi mai jos (la locul definirii lor) princomentarii, algoritmul de rezolvare a problemei de mai sus este dat încontinuare. Întrucât operaţiile respective se folosesc de mai multeori (de 3 ori), am definit un SUBPROGRAM TIPORDON(m,A) care ordoneazămai întâi elementele mulţimii A şi apoi le tipăreşte.

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 apelaţi mai sus sunt definiţi în continuare.

SUBPROGRAMUL CITMUL(n,M) ESTE: {Citeşte n şi M} CITEŞTE n; {n=nr. elementelor mulţimii} CITEŞTE (mi,i=1,n); {M=mulţimea cu elementele m1,m2,...,mn}SF-CITMUL SUBPROGRAMUL ORDON(n,M) ESTE: {Ordonează crescător cele n}

27

Page 26: Algoritmi

REPETĂ {elemente ale mulţimii M} FIE ind:=0; {Cazul M este ordonată} PENTRU i:=1;n-1 EXECUTĂ DACĂ mi>mi+1 ATUNCI {schimbă ordinea celor} FIE t := mi; {două elemente} mi:=mi+1; mi+1:=t; ind:=1; {Cazul M nu era ordonată} SFDACĂ SFPENTRU PÂNĂCÂND ind=0 SFREPSF-ORDON

SUBPROGRAMUL REUNIUNE(m,A,n,B,k,R) ESTE: { R := A U B }{ k = numărul elementelor mulţimii R }

FIE k:=m; R := A; PENTRU j:=1,n EXECUTĂ FIE ind:=0; {Ipoteza bj nu e in A} PENTRU i:=1;m EXECUTĂ DACĂ bj=ai ATUNCI ind:=1 {bj este in A} SFDACĂ SFPENTRU DACĂ ind=0 ATUNCI k:=k+1; rk:=bj SFDACĂ SFPENTRU SF-REUNIUNE SUBPROGRAMUL TIPMUL(n,M) ESTE: { Tipăreşte cele n elemente } PENTRU i:=1;n EXECUTĂ { ale mulţimii M } TIPĂREŞTE mi

SFPENTRUSF-TIPMUL SUBPROGRAMUL TIPORDON(n,M) ESTE: { Ordonează şi tipăreşte } CHEAMĂ ORDON(n,M); { elementele mulţimii M } CHEAMĂ TIPMUL(n,M); SF-TIPORDON

Tot ca exemplu de folosire a SUBPROGRAMilor, vom scrie unalgoritm pentru rezolvarea următoarei probleme: dirigintele unei clase de elevi doreşte să obţină un clasament al elevilor în funcţie de mediagenerală. În plus, pentru fiecare disciplină în parte doreşte lista primilor şase elevi.

În rezolvarea acestei probleme este necesară găsirea ordinii încare trebuiesc tipăriţi elevii în funcţie de un anumit rezultat: notala disciplina "j", sau media generală. Am identificat prin urmare douăsubprobleme independente, referitoare la:

28

Page 27: Algoritmi

(1) aflarea ordinii în care trebuie tipărite n numere pentrua le obţine ordonate; (2) tipărirea elevilor clasei într-o anumită ordine.

Prima subproblemă se poate specifica astfel: Dându-se numerele x1, x2, ... , xn, găsiţi ordinea o1, o2, ..., on, în care aceste numere devinordonate descrescător, adică

x[o1] x[o2] ... x[on] .Pentru rezolvarea ei vom da un SUBPROGRAM ORDINE în care intervin

trei parametri formali:- n, numărul valorilor existente; - X, vectorul acestor valori; - O, vectorul indicilor care dau ordinea dorită.

Primii doi parametri marchează datele presupuse cunoscute, iar altreilea, rezultatele calculate de SUBPROGRAM.

SUBPROGRAMUL ORDINE(n,X,O) ESTE:{n, numărul valorilor existente}

{X, vectorul acestor valori}{O, vectorul indicilor care dau ordinea dorită}

PENTRU i:=1; n EXECUTĂ oi :=i SFPENTRU REPETĂ ind:=0; PENTRU i:=1;n-1 EXECUTĂ DACĂ x[oi] < x[oi+1] ATUNCI FIE ind:=1; t:=oi+1 ; oi+1 :=oi; oi :=t; SFDACĂ SFPENTRU PANÂCÂND ind=0 SFREPSF-ORDINE

A doua subproblemă se poate specifica astfel: Dându-se ordinea o1,o2, ..., on, a elevilor clasei, numele şi mediile acestora, să se

tipărească 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Ă Tipăreşte datele elevului de rang oi. SFPENTRU SF-TIPAR

Variabilele folosite pentru problema dată sunt următoarele: - n reprezintă numărul elevilor clasei; - m este numărul disciplinelor la care elevii primesc note; - NUME este vectorul care reţine numele elevilor: NUMEi estenumele elevului cu numărul de ordine i;

29

Page 28: Algoritmi

- NOTE este matricea notelor elevilor, având n linii şi mcoloane;

NOTEi,j este nota elevului cu numele NUMEi la disciplinacu numărul 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} CITEŞTE m, {numărul 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); SFPENTRUSF-ALGORITM

Apel recursiv

În exemplele date se observă că apelul unui subprogram se facedupă ce el a fost definit. Este însă posibil ca un SUBPROGRAM să seapeleze pe el însuşi. Într-un astfel de caz spunem că apelul esterecursiv, iar SUBPROGRAMul respectiv este definit recursiv.

Ca exemplu, definim în continuare o funcţie care calculeazărecursiv valoarea n!. Se va folosi formula n! = n.(n-1)! în cazul n>0 şifaptul că 0!=1. Recursivitatea constă în faptul că în definiţiafuncţiei Factorial de n se foloseşte aceeaşi funcţie Factorial dar deargument n-1. Deci funcţia Factorial se apelează pe ea însăşi. Esteimportant ca numărul apelurilor să fie finit, deci ca procedeul decalcul descris să se termine.

FUNCTIA Factorial(n) ESTE: DACĂ n=0 ATUNCI Factorial:=1 ALTFEL Factorial:= n*Factorial(n-1)

30

Page 29: Algoritmi

SFDACĂSF-Factorial;

Tot ca exemplu de apel recursiv putem descrie o funcţie cecalculează maximul a n numere x1,x2,...,xn. Ea se bazează pe funcţia MAXIM2care calculează maximul a două numere, descrisă în continuare.

FUNCŢIA MAXIM2(a,b) ESTE: DACĂ a<b ATUNCI MAXIM2:=b ALTFEL MAXIM2:=a SFDACĂSF-MAXIM2

Funcţia MAXIM, care calculează maximul celor n numere esteurmătoarea:

FUNCŢIA 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

31

Page 30: Algoritmi

CAPITOLUL III

METODE DE PROIECTARE A ALGORITMILOR

3.1 Elaborarea algoritmilor

Prin elaborarea (proiectarea) unui algoritm înţelegem întreagaactivitate depusă de la enunţarea problemei până la realizareaalgoritmului corespunzător rezolvării acestei probleme.

În elaborarea unui algoritm deosebim următoarele activităţiimportante:

– specificarea problemei;– descrierea metodei alese pentru rezolvarea problemei;– proiectarea propriu-zisă. Ea constă în descompunereaproblemei în subprobleme, obţinerea algoritmului principal şia tuturor SUBPROGRAMilor apelaţi, conform metodelorprezentate în secţiunile următoare. Ea se termină cudescrierea algoritmului principal şi a SUBPROGRAMilormenţionaţi, dar şi cu precizarea denumirilor şisemnificaţiilor variabilelor folosite;– verificarea algoritmului obţinut.

3.2 Proiectarea ascendentă şi proiectarea descendentă

Există două metode generale de proiectare a algoritmilor, a cărordenumire provine din modul de abordare a rezolvării problemelor:metoda descendentă şi metoda ascendentă. Proiectarea descendentă (top-down) porneşte de la problema de rezolvat, pe care o descompune înpărţi rezolvabile separat. De obicei aceste părţi sunt subproblemeindependente, care la rândul lor pot fi descompuse în subprobleme. Laprima descompunere accentul trebuie pus pe algoritmul (modulul)principal nu asupra subproblemelor. La acest nivel nu ne intereseazăamănunte legate de rezolvarea subproblemelor, presupunem că le ştimrezolva, eventual că avem deja scrişi SUBPROGRAMi pentru rezolvarealor. Urmează să considerăm pe rând fiecare subproblemă în parte şi săproiectăm (în acelaşi mod) un SUBPROGRAM pentru rezolvarea ei. Înfinal, se va descrie SUBPROGRAMul de rezolvare al fiecăreisubprobleme, dar şi interacţiunile dintre aceşti SUBPROGRAMi şiordinea în care ei sunt folosiţi.

Noţiunea de modul va fi definită în secţiunea următoare.Deocamdată înţelegem prin modul orice SUBPROGRAM sau algoritmulprincipal. Legătura dintre module se prezintă cel mai bine sub formaunei diagrame numită arbore de programare. Fiecărui modul îicorespunde în arborele de programare un nod, ai cărui descendenţi sunt

32

Page 31: Algoritmi

toate modulele apelate direct. Nodul corespunzător algoritmuluiprincipal este chiar nodul rădăcină.

În multe cărţi metoda top-down este întâlnită şi sub denumireastepwise-refinement, adică rafinare în paşi succesivi. Este vorba deun proces de detaliere pas cu pas a specificaţiei, denumit proiectaredescendentă. Algoritmul apare în diferite versiuni succesive, fiecarefiind o detaliere a versiunii precedente.Scopul urmărit este acelaşi: concentrarea atenţiei asupra părţilorimportante ale momentului şi amânarea detaliilor pentru mai târziu.Dacă ar fi necesar să le deosebim am spune că metoda top-down sereferă la nivelul macro iar metoda rafinării succesive la nivel micro.La nivel macro se doreşte descompunerea unei probleme complexe însubprobleme. La nivel micro se doreşte obţinerea unui modul înversiune finală. Într-o versiune intermediară pot fi prezente numaipărţile importante ale acestuia, urmând să se revină asupra detaliilorîn versiunile următoare (aşa cum s-a arătat în secţiunea 1.5), după ceaspectele importante au fost rezolvate.

Avantajele proiectării top-down (cunoscută şi sub denumirea "Divideet impera") sunt multiple. Avantajul principal constă în faptul că eapermite programatorului să reducă complexitatea problemei,subproblemele în care a fost descompusă fiind mai simple, şi să amânedetaliile pentru mai târziu. În momentul în care descompunem problemaîn subprobleme nu ne gândim cum se vor rezolva subproblemele ci caresunt ele şi conexiunile dintre ele.

Proiectarea descendentă permite lucrul în echipe mari. Prindescompunerea problemei în mai multe subprobleme, fiecare subproblemăpoate fi dată spre rezolvare unei subechipe. Fiecare subechipă nucunoaşte decât subproblema pe care trebuie să o rezolve.

Metoda "Divide et Impera" poate fi folosită nu numai laîmpărţirea problemei în subprobleme ci şi la împărţirea datelor îngrupe mai mici de date. Un astfel de procedeu este folosit deSUBPROGRAMul Quicksort.

Metoda ascendentă (bottom-up) porneşte de la propoziţiilelimbajului şi de la SUBPROGRAMi existenţi, pe care îi asamblează înalţi SUBPROGRAMi pentru a ajunge în final la algoritmul dorit. Cu altecuvinte, în cazul metodei ascendente va fi scris mai întâiSUBPROGRAMul apelat şi apoi cel care apelează. Ca rezultat alproiectării ascendente se ajunge la o mulţime de SUBPROGRAMi care seapelează între ei. Este important să se cunoască care SUBPROGRAMapelează pe care, lucru redat printr-o diagramă de structură, ca şi încazul programării descendente.

33

Page 32: Algoritmi

Această metodă are marele dezavantaj că erorile de integrare vorfi detectate târziu, abia în faza de integrare. Se poate ajunge abiaacum la concluzia că unii SUBPROGRAMi, deşi corecţi, nu sunt utili.

De cele mai multe ori nu se practică o proiectare ascendentă saudescendentă pură ci o combinare a lor, o proiectare mixtă.

3.3 Proiectarea modulară

Prin proiectare (programare) modulară înţelegem metoda de proiectare(programare) a unui algoritm pentru rezolvarea unei probleme prinfolosirea modulelor.

Dar ce este un modul? Modulul este considerat o unitatestructurală de sine stătătoare, fie program, fie subprogram, fie ounitate de program. Un modul poate conţine sau poate fi conţinut într-alt modul. Un modul poate fi format din mai multe submodule. Astfel,în Pseudocod fiecare SUBPROGRAM şi algoritmul principal suntconsiderate module. În limbajele de programare cu structură de blocUNIT-urile pot fi considerate module. La compilarea separată un grupde subprograme compilate deodată constituie un modul, dar acest modulpoate fi considerat ca o mulţime de submodule din care este compus.

Este însă important ca fiecare modul să-şi aibă rolul său bineprecizat, să realizeze o funcţie în cadrul întregului program. Elapare în mod natural în descompunerea top-down.

Indiferent că privim modulul ca un singur SUBPROGRAM, un grup deSUBPROGRAMi, sau un algoritm de sine stătător ce apelează alţiSUBPROGRAMi, considerăm modulele relativ independente, dar cuposibilităţi de comunicare între ele. Astfel, un modul nu trebuie săfie influenţat de maniera în care se lucrează în interiorul altuimodul. Orice modificare ulterioară în structura unui program, dacăfuncţia pe care o realizează un modul M încă este necesară, acestmodul trebuie să fie util şi folosit în continuare fără modificări.

Rezultă că programarea modulară se bazează pe descompunereaproblemei în subprobleme şi proiectarea şi programarea separată aSUBPROGRAMilor corespunzători. De altfel, considerăm că într-oprogramare serioasă nu se poate ajunge la implementare fără a avea înprealabil algoritmii descrişi într-un limbaj de descriere (la noiPseudocod). Deci programarea modulară se referă în primul rând laproiectarea modulară a algoritmilor şi apoi la traducerea lor înlimbajul de programare ales, ţinând seama de specificul acestuilimbaj. Programarea modulară este strâns legată de programareaascendentă şi de programarea descendentă, ambele presupunând folosireaSUBPROGRAMilor pentru toate subproblemele întâlnite.

34

Page 33: Algoritmi

Avantajele programării modulare sunt multiple. Menţionăm în celece urmează câteva dintre ele. Descompunerea unei probleme complexe însubprobleme este un mijloc convenabil şi eficient de a reducecomplexitatea (Principiul Divide et impera acţionează şi înprogramare). Este evident că probabilitatea apariţiei erorilor înconceperea unui program creşte cu mărimea programului, lucru confirmatşi de experienţa practică. De asemenea, rezolvând o problemă maisimplă, testarea unui modul se poate face mult mai uşor decât testareaîntregului algoritm.

Apoi, faptul că trebuiesc proiectate mai multe subprograme pentrusubproblemele întâlnite, permite munca mai multor programatori. S-aajuns astfel la munca în echipă, modalitate prin care se ajunge lascurtarea termenului de realizare a produsului program.

Modulele se pot refolosi ori de câte ori avem nevoie de ele.Astfel, s-a ajuns la compilarea separată a subprogramelor şi lapăstrarea subprogramelor obţinute în biblioteci de subprograme, deunde ele se pot refolosi la nevoie. Sunt cunoscute astăzi multe astfelde biblioteci de subprograme. Reutilizabilitatea acestor subprograme este oproprietate foarte importantă în activitatea de programare. Ea duce lamărirea productivităţii în programare, dar şi la creşterea siguranţeiîn realizarea unui produs corect.

Uneori, în timpul proiectării algoritmului sau a implementăriilui, se ajunge la concluzia că proiectarea a fost incompletă sau căunele module sunt ineficiente. şi în această situaţie programareamodulară este avantajoasă, ea permiţând înlocuirea modulului în cauzăcu altul mai performant.

Una din activităţile importante în realizarea unui program esteverificarea corectitudinii acestuia. Experienţa a arătat că modulelese pot verifica cu atât mai uşor cu cât sunt mai mici. Abilitateaomului de a înţelege şi analiza corectitudinea unui SUBPROGRAM estemult mai mare pentru texte scurte. În unele cărţi chiar se recomandă anu se folosi SUBPROGRAMi mai mari decât 50 de propoziţii. Sigur că oastfel de limită nu există, dar se recomandă descompunerea unuiSUBPROGRAM în alţi SUBPROGRAMi oricând acest lucru este posibil în modnatural, deci aceşti noi SUBPROGRAMi rezolvă subprobleme de sinestătătoare, sau realizează funcţii bine definite.

3.4 Programarea structurată

Programarea structurată este un stil de programare apărut în urmaexperienţei primilor ani de activitate. Ea cere respectarea uneidiscipline de programare şi folosirea riguroasă a câtorva structuri de

35

Page 34: Algoritmi

calcul. Ca rezultat se va ajunge la un algoritm uşor de urmărit, clarşi corect.

Termenul programare, folosit în titlul acestei secţiuni şiconsacrat în literatura de specialitate, este folosit aici în senslarg şi nu este identic cu cel de programare propriu-zisă. Este vorbade întreaga activitate depusă pentru obţinerea unui program, deci atâtproiectarea algoritmului cât şi traducerea acestuia în limbajul deprogramare ales.

Bohm şi Jacopini au demonstrat că orice algoritm poate fi compusdin numai trei structuri de calcul:

– structura secvenţială;– structura alternativă;– structura repetitivă.

Fiecare din aceste structuri, ca parte dintr-o schemă logică, areo singură intrare şi o singură ieşire şi sunt prezentate în figura1.3.1.

Knuth consideră programarea structurată ca fiind un mijloc de aface produsele program mai uşor de citit. De asemenea, programareastructurată este definită ca fiind programarea în care abordarea estetop-down, organizarea muncii este făcută pe principiul echipeiprogramatorului şef, iar în proiectarea algoritmilor se folosesc celetrei structuri de calcul definite de Bohm-Jacopini.

Alţi autori consideră programarea structurată nu ca o simplămetodă de programare ci ansamblul tuturor metodelor de programarecunoscute. Dar programarea modulară, programarea top-down, sau bottom-up (ascendentă sau descendentă) au apărut înaintea programăriistructurate. Important este faptul că programarea structuratăpresupune o disciplină în activitatea de programare.

Considerăm că programarea structurată se poate întâlni:– la nivel micro, privind elaborarea unui SUBPROGRAM;– la nivel macro, privind dezvoltarea întregului produsinformatic (algoritm).

La nivel micro programarea structurată este cea în care autoruleste atent la structura fiecărui modul în parte, cerând claritate şiordine în scriere şi respectarea structurilor de calcul definite maisus.

La nivel macro programarea structurată presupune practicareaproiectării top-down, a programării modulare şi a celorlalte metode deprogramare, cerând ordine în întreaga activitate şi existenţa uneistructuri clare a întregii aplicaţii, precizată prin diagrama destructură a aplicaţiei.

În acest scop am definit limbajul Pseudocod, care are structurilede calcul menţionate. Schemele logice obţinute dintr-o descriere în

36

Page 35: Algoritmi

Pseudocod a unui algoritm, conform semanticii propoziţiilor Pseudocod,se numesc D-scheme (de la Dijkstra) sau scheme logice structurate.

37

Page 36: Algoritmi

CAPITOLUL IVANALIZA ALGORITMILOR

O anumită problemă poate fi rezolvată cu ajutorul calculatoruluinumai dacă se găseşte un algoritm pentru rezolvarea ei, care este datcalculatorului sub forma unui program. Întrucât toate problemelepractice pe care le întâlnim se pot rezolva cu ajutorulcalculatorului, s-ar putea crede că orice problemă este rezolvabilă.Această afirmaţie este însă falsă. Se ştie că nu există un programcare să rezolve "problema terminării programelor":"Scrieţi un program care să decidă dacă un algoritm oarecare, dat, intră sau nu într-un cicluinfinit".

Chiar şi problemele pentru care există algoritmi corespunzătorinu sunt neapărat rezolvabile cu calculatorul. Este posibil ca timpulnecesar execuţiei acestor algoritmi, sau cantitatea de memorienecesară, să nu permită folosirea lor în practică.

Evident, dacă spaţiul de memorie necesar programului este maimare decât cantitatea de memorie disponibilă, programul nu poate fiexecutat. De asemenea, dacă numărul calculelor ce trebuie efectuateste foarte mare, programul poate fi inutil. Iar asta se poateîntâmpla destul de uşor în cazul problemelor ce trebuiesc rezolvate întimp real (adică soluţia trebuie obţinută înaintea unui timp critic).

Iată câteva motive pentru care este necesar să analizămalgoritmii pe care-i concepem pentru rezolvarea unei probleme.

Ne interesează să analizăm un program din mai multe puncte devedere:

1) Corectitudine; 2) Eficienţă; 3) Posibilitate de îmbunătăţire; 4) Alte calităţi pe care le are.

4.1 Corectitudinea programelor

Un program este corect dacă el satisface specificaţiileproblemei. Nu ne interesează câtă memorie foloseşte acest program, dincâte instrucţiuni este compus, sau cât timp de execuţie necesită. Cualte cuvinte, un program este corect dacă pentru acele date de intrarecare satisfac specificaţiile problemei rezultatele obţinute în urmaexecuţiei sunt corecte.

Pentru orice program P deosebim trei tipuri de variabile, pe carele vom grupa în trei vectori X, Y şi Z. Componentele vectorului Xdesemnează variabilele de intrare, deci datele presupuse cunoscute în

38

Page 37: Algoritmi

problema rezolvată prin programul P. Componentele vectorului Z suntvariabilele care reprezintă rezultatele cerute de problemă. Însfârşit, componentele vectorului Y sunt variabilele de lucru, carenotează diferitele rezultate intermediare necesare în program.

O problemă nu are sens pentru orice date de intrare. Vom folosipredicatul R(X) pentru a preciza datele pentru care problema are sens.R(X) se numeşte predicat de intrare sau precondiţie. Pentru acele valoriale lui X pentru care predicatul este adevărat problema are sens,pentru celelalte nu are sens să executăm programul P.

Între rezultatele Z ale problemei şi datele iniţiale X (cunoscuteîn problemă) există anumite relaţii. Vom reda aceste relaţii prinpredicatul de ieşire R(X,Z), numit şi postcondiţie. Acesta este corectpentru acele valori a şi b ale vectorilor X şi Z pentru carerezultatele problemei sunt b în cazul când datele iniţiale sunt a şieste fals în caz contrar. Deci, dacă executând programul cu dateleiniţiale a obţinem rezultatele b' şi R(a,b') este fals, acest fapt esteun indiciu că rezultatele obţinute în program nu sunt corecte. Apare oaltă regulă: fiecare variabilă să aibă semnificaţia ei şi să nu fie folosită înscopuri diferite.

4.2 Testarea şi depanarea programelor

Testarea programelor este activitatea prin care programatorulobservă comportarea programului în urma execuţiei lui cu date de test.Evident, primul lucru urmărit este corectitudinea rezultatelorobţinute în urma execuţiei programului cu datele de test folosite.Dar se va urmări şi dacă programul are alte caracteristici ca:utilitate, siguranţă în funcţionare, robusteţe, performanţă. Estebeneficiarul mulţumit de rezultatele care se obţin şi de forma subcare sunt prezentate? Sunt ele obţinute în timp util? Datele de test sunt date de intrare alese pentru variabilele deintrare pentru care se cunosc rezultatele, sau avem unele informaţiidespre rezultate. Executând programul cu aceste date ar trebui săajungem la rezultatele cunoscute.

Corectitudinea rezultatelor în aceste execuţii nu demonstreazăcorectitudinea programului în general. Testarea însă pune adeseori înevidenţă erori făcute în diferite faze ale programării. În privinţaaceasta dăm un citat din Dijkstra: Testarea programelor poate fi un mijloc eficientde a indica prezenţa erorilor, dar din păcate, nu şi un mijloc de a demonstra absenţa lor.

39

Page 38: Algoritmi

Cu toate că ea nu demonstrează corectitudinea programului,testarea măreşte certitudinea corectitudinii lui şi este deocamdatăsingura metodă practică de certificare a programului. Ar fi de doritdemonstrarea apriori a corectitudinii programului, dar rezultatelecunoscute în prezent în această direcţie nu sunt aplicabileprogramelor complexe.

Scopul testării programelor este depistarea şi eliminareaerorilor. Acest lucru este făcut prin execuţia programului cu date detest pentru care se cunosc dinainte rezultatele (sau cel puţin se ştieceva despre ele) şi se observă rezultatele obţinute în urma execuţiei.În cazul în care rezultatele obţinute în urma execuţiei nu sunt celeaşteptate se vor căuta şi elimina erorile. Activitatea care urmăreştedescoperirea cauzelor erorilor şi înlăturarea lor se numeşte depanare.

Se pune problema alegerii datelor de test şi a numărului deexecuţii ce trebuie făcute pentru a putea considera că programul nuare erori. Numărul tuturor seturilor de date de intrare posibile esteteoretic infinit chiar şi pentru probleme simple. Deci nu poate fivorba de o testare exhaustivă. Stabilirea datelor de test se poateface cel puţin pe două căi:

– ţinând seama de specificaţia problemei;– ţinând seama de textul programului.

Aşa cum va rezulta din cele ce urmează, cea mai bună cale esteuna mixtă, în care sunt combinate aceste două posibilităţi.

La testarea după specificaţia problemei, stabilirea datelor detest se face analizând specificaţia problemei. Se recomandă stabilireadatelor de test ţinând seama de specificaţia asupra datelor de intrareşi de specificaţia asupra datelor de ieşire. Această metodă de testareeste adecvată problemelor simple. În cazul unei probleme complexeaplicarea ei este imposibilă datorită numărului foarte mare de cazuriposibile, care ar trebui testate. Însă problema noastră a fostdescompusă în subprobleme mai mici, invizibile în specificaţie şi acăror testare este mai simplă. Privind programul ca o cutie neagră nuvom mai ţine seama de aceste subprobleme. Totuşi, testarea dupăspecificaţia problemei rămâne o metodă utilă în testarea modulelor.

Testarea după textul programului ţine seama, pentru a stabilidatele de test, de instrucţiunile care trebuiesc executate.Considerând că algoritmul este descris printr-o schemă logică, oexecuţie a programului înseamnă parcurgerea unui drum de la START laSTOP în această schemă. Dacă la această execuţie rezultatele obţinutesunt corecte probabil că textul algoritmului pe acest drum estecorect. Ar trebui să verificăm toate blocurile schemei logice şi maiales toate drumurile de la START la STOP posibile. Cu observaţia că încazul a două drumuri ce diferă doar prin faptul că o anumită buclă se

40

Page 39: Algoritmi

execută de n1, respectiv n2 ori le vom considera echivalente între ele.Dintre toate drumurile echivalente între ele vom testa un singur drum,altfel am avea o infinitate de drumuri de testat.

În concluzie vom alege pentru fiecare drum un set de date detest, numărul execuţiilor fiind egal cu numărul acestor drumuri. Dacătoate execuţiile au dat rezultate corecte programul se considerătestat. Dacă însă la o singură execuţie am depistat erori, corectarealor a modificat textul algoritmului şi testarea trebuie reluată petoate drumurile afectate de această schimbare.

Pentru un program complex, deci pentru o schemă logică cu unnumăr foarte mare de drumuri START-STOP, testarea ar fi o activitatecomplexă, constând dintr-un număr foarte mare de execuţii. Încă unmotiv pentru a practica programarea modulară, caz în care testarea seface asupra unor module mai mici şi asupra interfeţei dintre ele, aşacum se va menţiona mai jos.

Stabilirea datelor de test după textul programului are şi uneledezavantaje. În primul rând, programul poate fi incomplet şi să nucorespundă specificaţiilor. Pe drumurile existente el este corect, darlipsesc drumuri care, conform specificaţiilor, ar trebui să existe.Lipsa acestor drumuri este o greşeală 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 sepoate merge oricare ar fi datele de intrare, deci nu putem găsi datede test corespunzătoare acestor drumuri. Adeseori aceste drumuri scotîn evidenţă erori prin simpla analiză a textului. Astfel, însuccesiunea de propoziţii 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 nostrutastarea numărului 2 în loc de 20, ceea ce schimbă complet sensultextului Pseudocod de mai sus.

Adesea este imposibil să se execute programul cu toate datele detest stabilite. În acest caz apare problema alegerii acelei submulţimidin aceste date care să aibă şansa maximă de a depista erorileprezente în program. Testarea minimă care trebuie făcută constă într-un număr de execuţii a programului care să ne asigure că fiecareinstrucţiune din program a fost executată cel puţin odată. Ea înseamnămult mai puţine execuţii decât toate drumurile START-STOP.

41

Page 40: Algoritmi

Există date de test care ne duc pe un anumit drum fără a depistaerori existente în instrucţiunile întâlnite şi alte date de test caredepistează aceste erori. Încă un motiv pentru care se recomandă otestare mixtă.

Ca ordine de folosire a datelor de test în timpul testării, serecomandă mai întâi testarea după specificaţii şi apoi testarea dupătextul programului.

Este necesară şi testarea robusteţei programului, care înseamnăbuna lui comportare la date de intrare intenţionat greşite, pentrucare problema nu are sens. Unele programe intră în aceste condiţii înciclu infinit, altele se termină cu erori de execuţie. Un programrobust nu trebuie să fie afectat de datele de intrare eronate.Comportarea cea mai normală în astfel de situaţii ar fi semnalareaunor mesaje de eroare corespunzătoare.

La un produs program complex testarea este o activitate mult maicomplicată. Este necesară o testare separată a fiecărui modul înparte, o testare a interfeţei dintre module şi o testare a produsuluiîn ansamblu (testarea de integrare).

Testarea de integrare se referă la funcţionarea programuluirealizat în ansamblu. După ce fiecare modul în parte a fost testat şicorectat, deci în ipoteza că fiecare modul în parte este corect, enecesar să se verifice comportarea globală a programului. În aceastăetapă găsirea erorilor, înlăturarea cauzelor care le-a generat şicorectarea lor, poate fifoarte dificilă, mai ales atunci când ele provin dintr-o proiectaregreşită.

Execuţia unui program se poate termina anormal datorită apariţieiunor erori ca:

– împărţiri la zero;– alte erori ce provoacă depăşiri;– neconcordanţa între parametri actuali şi formali;– depăşirea dimensiunii tablourilor.

Dar chiar şi la execuţia normală a programului putem avea erori,unele foarte grave, obţinând rezultate greşite. În ambele situaţiiurmează depanarea programului, adică descoperirea cauzei erorilor şiînlăturarea lor.

O metodă utilă în depanarea programelor, în special pentruîncepători, este inserarea în program a unor tipăriri auxiliare. Maiales în locurile vecine cu instrucţiunile care au provocat eroarea şipentru variabilele implicate în producerea ei. Observarea valorilorunei variabile, a schimbărilor făcute în timpul execuţiei, potdezvălui programatorului cauza erorii. Cu siguranţă îi va arăta că oanumită variabilă ia alte valori decât cele la care se aşteaptă el. De

42

Page 41: Algoritmi

altfel, pe timpul testării unui program, sunt utile semnalărileoricăror semne de eroare. Recomandăm verificarea valorilorvariabilelor imediat după obţinerea acestora.

4.3 Complexitatea algoritmilor

În această secţiune ne va interesa eficienţa unui algoritm. Maiexact, ne interesează să comparăm între ei mai mulţi algoritmi carerezolvă aceeaşi problemă. Care dintre ei este mai bun? Evident, vomcompara numai algoritmi despre care ştim că sunt corecţi.

Putem compara doi algoritmi în raport cu:– cantitatea de memorie necesară;– viteza de lucru, deci timpul necesar rezolvării problemei.

Dacă în urmă cu două decenii volumul de memorie necesarrezolvării unei probleme era un factor important din cauza memorieireduse existente la calculatoarele din acel timp, astăzi acest factora devenit mai puţin important. Calculatoarele actuale au memoriesuficient de mare pentru marea majoritate a algoritmilor întâlniţi.

Timpul necesar execuţiei unui program depinde de număruloperaţiilor ce trebuiesc executate. Iar numărul operaţiilor efectuatedepinde de datele de intrare, deci se schimbă de la o execuţie laalta.

Există însă un cel mai rău caz, pentru acele date de intrarepentru care numărul operaţiilor efectuate este maxim. În acest cazvorbim de complexitate în cel mai rău caz.

De asemenea, putem vorbi de numărul mediu de operaţii efectuateîntr-o execuţie. Dacă numărul execuţiilor posibile este finit atunciacest număr mediu este egal cu numărul operaţiilor efectuate în toateexecuţiile, împărţit la numărul execuţiilor. Sunt însă foarte puţineprograme cu această proprietate. Pentru aproape toate programele, celpuţin teoretic, numărul execuţiilor posibile este infinit.

4.4 Documentarea programelor

În paralel cu elaborarea programului trebuie elaborată şi odocumentaţie. Aceasta va conţine toate deciziile luate în creareaprogramului. Documentarea este activitatea de prezentare a programuluicelor care vor fi interesaţi să obţină informaţii despre el. Aceştiasunt în primul rând persoanele care au realizat programul, apoipersoanele care-l vor folosi şi persoanele solicitate să facăîntreţinerea acestuia.

Adeseori se întâlnesc programe fără nici o altă documentaţie înafara textului propriu-zis al programului. În graba de a termina cât

43

Page 42: Algoritmi

mai repede, programul nu este însoţit de nici o documentaţie şifrecvent nu sunt folosite nici comentarii în textul programului.

Sigur că însăşi textul programului constituie o autodocumentare.Iar comentariile prezente în program dau explicaţii suplimentaredespre program. Este însă necesară o documentaţie completă, scrisă,care va conţine:

– enunţul iniţial al problemei;– specificaţia (vezi secţiunea 4.1);– documentaţia de proiectare (metoda de rezolvare aleasă şiproiectarea algoritmilor folosiţi. Pentru fiecare algoritm vafi prezentată subproblema corespunzătoare, cu specificaţia eişi rolul fiecărei variabile);– documentaţia de programare, care va include textulprogramului;– datele de test folosite; – documentaţie privind exploatarea programului;– modificări făcute în timpul întreţinerii programului.

Menţionăm că cele mai recente produse realizate de firmeleconsacrate au, pe lângă documentaţia scrisă, şi o autodocumentaţie(funcţii HELP).

Referitor la autodocumentare, folosirea comentariilor, alegereacu grijă a denumirii variabilelor, cât şi claritatea textului,obţinută prin indentare şi grijă asupra structurii programului, esteutilă nu numai pe timpul elaborării programului, dar mai ales petimpul întreţinerii şi modificărilor ulterioare.

Denumirea variabilei să fie astfel aleasă încât să redea cât maibine semnificaţia ei.

Cei care au dorit să refolosească programe scrise cu câteva luniîn urmă înţeleg foarte bine diferenţa dintre un program însoţit decomentarii explicative şi un program fără nici o explicaţie. Uitareaacţionează asupra oricărei persoane şi, chiar dacă este posibilă,descifrarea unui program cere timp şi nu este o sarcină prea uşoară.Comentariile sunt recomandate, fiind un mijloc de autodocumentare aprogramului sursă.

Sigur că prima documentaţie a oricărui program este textul sursăpropriu-zis. Este bine ca acest text să poată fi citit cât mai uşor,iar programarea structurată duce la un program mai uşor de citit decâtunul lipsit de orice structură.

Este însă nevoie şi de o documentaţie însoţitoare scrisă, careconstituie documentarea propriu-zisă a programului. Aceasta trebuie săredea toate deciziile făcute în timpul proiectării, să prezintediagrama de structură a întregului produs şi fiecare parte separat.Pentru fiecare modul documentaţia va conţine:

44

Page 43: Algoritmi

– numele acestuia;– datele de intrare;– datele de ieşire;– funcţia realizată de modulul respectiv;– variabilele folosite şi semnificaţia lor;– algoritmul propriu-zis.

Este necesar ca aceste informaţii să se afle şi sub forma unorcomentarii în textul programului. De asemenea, documentaţia va conţineşi textul final al programului.

Este necesară şi o documentaţie de folosire a produsuluirealizat. Beneficiarul nu este interesat de modul în care a fostrealizat programul ci de modul în care îl poate folosi.

O documentare completă a unui program poate fi utilă nu numaipentru folosirea şi întreţinerea programului. Componente ale unuiprodus existent pot fi utile şi în realizarea altor produse. Este însănecesar să se înţeleagă cât mai uşor ce funcţii realizează acestecomponente şi cu ce performanţe. Folosirea acestor componenteexistente, testate şi documentate, duce evident la creştereaproductivităţii în realizarea noului produs.

4.5 Stil în programare

Fiecare programator are stilul să propriu de concepere şiredactare a unui program. Este bine ca el să respecte anumite reguligenerale de programare, astfel încât programele elaborate să aibăanumite calităţi.

Calităţile pe care le poate avea un program sunt următoarele:Corectitudine = proprietatea programului de a respecta specificaţiileşi a da rezultate corecte;Extensibilitate = posibilitatea adaptării programului la uneleschimbări în specificaţie;Robusteţe = abilitatea de a recunoaşte situaţiile în care problema cese rezolvă nu are sens şi de a se comporta în consecinţă (de exemplu,prin mesaje de eroare corespunzătoare);Reutilizabilitate = posibilitatea reutilizării întregului program saua unor părţi din el în alte aplicaţii;Compatibilitate = uşurinţa de combinare cu alte produse program;Portabilitate = posibilitatea de folosire a produsului program pe altesisteme de calcul, diferite de cel pe care a fost conceput;Eficienţă = măsura în care sunt bine folosite resursele sistemului decalcul;

45

Page 44: Algoritmi

Claritate = uşurinţa citirii şi înţelegerii textului programului, astructurilor din care este compus şi a rolului denumirilor şi părţilorsale.

Un produs program este considerat de calitate dacă are calităţilede mai sus, dacă lansat în execuţie dă rezultate corecte, dacă textullui se poate citi şi înţelege, dacă poate fi uşor întreţinut şi dacăeste terminat la data fixată.

Stilul unui programator este dat de măsura în care programul săuare aceste calităţi şi de vizibilitatea lor. Evident, pe lângă acestecalităţi, vizibile şi în text, stilul de programare este dat şi decorectitudinea şi robusteţea produselor realizate. Programul trebuiesă funcţioneze şi la introducerea unor date greşite (pentru careproblema nu are sens), recunoscând această situaţie şi semnalând-o. Înacelaşi timp rezultatele obţinute pentru date pentru care problema aresens trebuie să fie corecte. Iar corectitudinea sau incorectitudineaprogramului este o consecinţă a modului în care programatorul arespectat regulile de programare (vezi capitolul 8) şi a experienţeiobţinute în activitatea de programare.

Privind claritatea algoritmului trebuie să observăm că indentarea(paragrafarea) este un alt mijloc de a mări claritatea scrierii.Textul unui algoritm poate fi scris cuvânt după cuvânt, completând totrândul asemeni textului unui roman. Claritatea lui este mică, după cumurmează:

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

Considerăm că fiecare programator trebuie să respecte anumitereguli de scriere a programelor, cu gândul la claritatea textului. Înunele cărţi sunt date mai multe reguli de indentare. Astfel, Griessugerează următoarele reguli:

46

Page 45: Algoritmi

- instrucţiunile unei secvenţe se vor scrie aliniate,începând toate în aceeaşi coloană;- instrucţiunile unei structuri de calcul (instrucţiunicompuse) se vor scrie începând toate din aceeaşi coloană,aflată cu 2-4 caractere la dreapta faţă de începutulinstrucţiunii compuse;- pe o linie pot fi scrise mai multe instrucţiuni, cucondiţia ca ele să aibă ceva comun. Astfel, 2-4 instrucţiuniscurte de atribuire pot fi scrise pe acelaşi rând. Acestlucru se recomandă în vederea unei scrieri compacte aprogramului. E bine ca un program ce se poate scrie pe opagină, cu respectarea structurii lui, să nu fie întins pedouă pagini !

Considerăm că nu există reguli de scriere obligatorii pentrutoată lumea! Dar fiecare programator trebuie să aibă propriile luireguli de scriere.

Tot privind claritatea scrierii programului, se recomandă cadenumirile variabilelor să fie astfel alese încât să reflectesemnificaţia acestor variabile.

Un alt mijloc de a mări claritatea textului unui program constăîn inserarea comentariilor în text. Comentariile sunt texteexplicative închise între acolade. Ele au rolul de a explicacititorului anumite părţi din program. Am spus deja că în proiectareaalgoritmilor folosim şi propoziţii nestandard care vor fi pe parcursînlocuite cu propoziţii standard. E bine ca aceste propoziţii sărămână în text sub formă de comentarii.

Comentariile vor fi prezente:– în capul programului, pentru a prezenta titlul şi scopulprogramului, perioada realizării lui şi numeleprogramatorului;– în definiţii, pentru a descrie semnificaţia notaţiilorfolosite (a variabilelor, constantelor, SUBPROGRAMilor etc);– în dreapta unor instrucţiuni, pentru a descrie rolulacestora, sau cazul în care se atinge acea instrucţiune;– între părţile unui modul mai lung, pentru a explica rolulfiecărei părţi.

Sperăm că prin exemplele date în acest material am prezentat unstil propriu de programare şi am convins cititorul de necesitateaformării propriului său stil.

47

Page 46: Algoritmi

CAPITOLUL V

CLASE DE ALGORITMI

Căutarea şi Sortarea sunt două dintre cele mai des întâlnitesubprobleme în programare. Ele constituie o parte esenţială dinnumeroasele procese de prelucrare a datelor. Operaţiile de căutare şisortare sunt executate frecvent de către oameni în viaţa de zi cu zi,ca de exemplu căutarea unui cuvânt în dicţionar sau căutarea unuinumăr în cartea de telefon.

Căutarea este mult simplificată dacă datele în care efectuămaceastă operaţie sunt sortate (ordonate, aranjate) într-o anumită ordine(cuvintele în ordine alfabetică, numerele în ordine crescătoare saudescrescătoare).

Sortarea datelor constă în rearanjarea colecţiei de date astfelîncât un câmp al elementelor colecţiei să respecte o anumită ordine.De exemplu în cartea de telefon fiecare element (abonat) are un câmpde nume, unul de adresă şi unul pentru numărul de telefon. Colecţiaaceasta respectă ordinea alfabetică după câmpul de nume.

Dacă datele pe care dorim să le ordonăm, adică să le sortăm, suntîn memoria internă, atunci procesul de rearanjare a colecţiei îl vomnumi sortare internă, iar dacă datele se află într-un fişier (colecţiede date de acelaşi fel aflate pe suport extern), atunci procesul îlvom numi sortare externă.

Fiecare element al colecţiei de date se numeşte articol iar acestala rândul său este compus din unul sau mai multe componente. O cheie Ceste asociată fiecărui articol şi este de obicei unul dintrecomponente. Spunem că o colecţie de n articole este ordonat crescător dupăcheia C dacă C(i) C(j) pentru 1i<jn, iar dacă C(i) C(j) atunci şirul esteordonat descrescător.

5.1 Algoritmi de căutare

În acest subcapitol vom studia câteva tehnici elementare decăutare şi vom presupune că datele se află în memoria internă, într-unşir de articole. Vom căuta un articol după un câmp al acestuia pe careîl vom considera cheie de căutare. În urma procesului de căutare varezulta poziţia elementului căutat (dacă acesta există).

Notând cu k1, k2, ...., kn cheile corespunzătoare articolelor şi cu acheia pe care o căutăm, problema revine la a găsi (dacă există)poziţia p cu proprietatea a = kp.

De obicei articolele sunt păstrate în ordinea crescătoare acheilor, deci vom presupune că

48

Page 47: Algoritmi

k1 < k2 < .... < kn .Uneori este util să aflăm nu numai dacă există un articol cu cheiadorită ci şi să găsim în caz contrar locul în care ar trebui inseratun nou articol având cheia specificată, astfel încât să se păstrezeordinea existentă.

Deci problema căutării are următoarea specificare:Date a,n,(ki, i=1,n);Precondiţia: nN, n1 şi k1 < k2 < .... < kn ;Rezultate p;Postcondiţia: (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 mulţiSUBPROGRAMi.

O primă metodă este căutarea secvenţială, în care sunt examinatesuccesiv toate cheile.

SUBPROGRAMul CautSecv(a,n,K,p) este: {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)} Fie p:=0; {Cazul "încă negasit"} Dacă ak1 atunci p:=1 altfel Dacă a>kn atunci p:=n+1 altfel Pentru i:=2; n execută Dacă (p=0) şi (aki) atunci p:=i sfdacă sfpentru sfdacă sfdacăsf-CautSecv

Se observă că prin această metodă se vor executa în cel mainefavorabil caz n-1 comparări, întrucât contorul i va lua toatevalorile de la 2 la n. Cele n chei împart axa reală în n+1 intervale.Tot atâtea comparări se vor efectua în n-1 din cele n+1 intervale încare se poate afla cheia căutată, deci complexitatea medie are acelaşiordin de mărime ca şi complexitatea în cel mai rău caz.

Evident că în multe situaţii acest algoritm face calcule inutile.Atunci când a fost deja găsită cheia dorită este inutil a parcurgeciclul pentru celelalte valori ale lui i. Cu alte cuvinte este posibilsă înlocuim ciclul PENTRU cu un ciclu CÂTTIMP. Ajungem la un al doileaalgoritm, dat în continuare.

SUBPROGRAMul CautSucc(a,n,K,p) este: {nN, n1 şi}

49

Page 48: Algoritmi

{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).

Fie p:=1; Dacă a>k1 atunci Câttimp pn şi a>kp executş p:=p+1 sfcât sfdacăsf-CautSecv

O altă metodă, numită căutare binară, care este mult maieficientă, utilizează tehnica "divide et impera" privitor la date. Sedetermină în ce relaţie se află cheia articolului aflat în mijloculcolecţiei cu cheia de căutare. În urma acestei verificări căutarea secontinuă doar într-o jumătate a colecţiei. În acest mod, prinînjumătăţiri succesive se micşorează volumul colecţiei rămase pentrucăutare. Căutarea binară se poate realiza practic prin apelul funcţieiBinarySearch(a,n,K,1,n), descrisă mai jos, folosită în SUBPROGRAMul dat încontinuare.

SUBPROGRAMul CautBin(a,n,K,p) este:{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)} Dacă ak1 atunci p:=1 altfel Dacă a>kn atunci p:=n+1 altfel

p:=BinarySearch(a,n,K,1,n) sfdacă sfdacăsf-CautBinFuncţia BinarySearch (a,n,K,St,Dr) este: Dacă StDr-1 atunci BinarySearch:=Dr altfel m:=(St+Dr) Div 2; Dacă aK[m] atunci BinarySearch:=BinarySearch(a,n,K,St,m) altfel BinarySearch:=BinarySearch(a,n,K,m,Dr) sfdacă sfdacăsf-BinarySearch

În funcţia BinarySearch descrisă mai sus, variabilele St şi Drreprezintă capetele intervalului de căutare, iar m reprezintă mijloculacestui interval.

50

Page 49: Algoritmi

Se observă că funcţia BinarySearch se apelează recursiv. Se poateînlătura uşor recursivitatea, aşa cum se poate vedea în următoareafuncţie:

Funcţia BinSeaNerec (a,n,K,St,Dr) este: Câttimp Dr-St>1 execută m:=(St+Dr) Div 2; Dacă aK[m] atunci Dr:=m altfel St:=m sfdacă sfcât BinSeaNerec:=Drsf-BinSeaNerec

5.2 Sortare internă

Prin sortare internă vom înţelege o rearanjare a unei colecţiiaflate în memoria internă astfel încât cheile articolelor să fieordonate crescător (eventual descrescător).

Din punct de vedere al complexităţii algoritmilor problema revinela ordonarea cheilor. Deci specificarea problemei de sortare internăeste următoarea:

Date n,K; {K=(k1,k2,...,kn)}Precondiţia: kiR, i=1,nRezultate K';Postcondiţia: K' este o permutare a lui K, dar ordonată crescător. Deci k1 k2 ... kn.

O primă tehnică numită "Selecţie" se bazează pe următoarea idee: sedetermină poziţia elementului cu cheie de valoare minimă (respectivmaximă), după care acesta se va interschimba cu primul element. Acestprocedeu se repetă pentru subcolecţia rămasă, până când mai rămânedoar 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ă sfpentrusf-Selectie

51

Page 50: Algoritmi

Se observă că numărul de comparări 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ă câte două elemente consecutive iar în cazul în care acestea nu seaflă în relaţia dorită, ele vor fi interschimbate. Procesul decomparare se va încheia în momentul în care toate perechile deelemente consecutive sunt în relaţia de ordine dorită.

SUBPROGRAMul BubbleSort (n,K) este: Repetă Fie kod:=0; {Ipoteza "este ordine"} Pentru i:=2; n execută Dacă ki-1 > ki atunci t := ki-1; ki-1 := ki; ki:=t; kod:=1 {N-a fost ordine!} sfdacă sfpentru pânăcând kod=0 sfrep {Ordonare}sf-BubbleSort

O metodă mai performantă de ordonare, care va fi prezentată încontinuare, se numeşte "QuickSort" şi se bazează pe tehnica "divide etimpera" după cum se poate observa în continuare. Metoda esteprezentată sub forma unei proceduri care realizează ordonarea unuisubşir precizat prin limita inferioară şi limita superioară aindicilor acestuia. Apelul procedurii pentru ordonarea întregului şireste : QuickSort(n,K,1,n), unde n reprezintă numărul de articole alecolecţiei date.

SUBPROGRAMul SortareRapidă (n,K) este: Cheamă QuickSort(n,K,1,n)sf-SortareRapidă

Procedura QuickSort (n,K,St,Dr) va realiza ordonarea subşirului kSt,kSt+1,...,kDr. Acest subşir va fi rearanjat astfel încât kSt să ocupe poziţia luifinală (când şirul este ordonat). Dacă i este această poziţie, şirul vafi rearanjat astfel încât următoarea condiţie să fie îndeplinită:

kj ki kl , pentru st j < i < l dr (*)Odată realizat acest lucru, în continuare va trebui doar să

ordonăm subşirul kSt , kSt+1 , ... ,ki-1 prin apelul recursiv al proceduriiQuickSort(n,K,St,i-1) şi apoi subşirul ki+1,..., kDr prin apelul QuickSort(i+1,Dr).

52

Page 51: Algoritmi

Desigur ordonarea acestor două subşiruri (prin apelul recursiv alprocedurii) mai este necesară doar dacă acestea conţin cel puţin douăelemente.

Procedura QuickSort este prezentată în continuare :SUBPROGRAMul QuickSort (n,K,St,Dr) este: Fie i:=St; j:=Dr; a:=ki; Repetă Câttimp kj >= a şi (i<j) execută j:=j-1 sfcât ki:= kj; Câttimp ki a şi (i<j) execută i:=i+1 sfcât kj:= ki ; pânăcând i=j sfrep Fie ki := a; Dacă St < i-1 atunci Cheamă QuickSort(n,K,St,i-1) sfdacă Dacă i+1 < Dr atunci Cheamă QuickSort(n,K,i+1,Dr) sfdacăsf-QuickSort

Un ultim algoritm care va fi prezentat se numeşte "Merge Sort"(sortare prin interclasare) şi se bazează pe tehnica "divide etimpera". Şirul ce urmează a fi ordonat se împarte în două subşiruricare se ordonează, după care acestea se vor interclasa obţinându-seîntregul şir ordonat. Fiecare subşir se va ordona tot prin despărţirealui în două subşiruri urmată de interclasare şi aşa mai departe pânăcând ordonarea unui subşir se poate rezolva elementar fără a mai finecesară despărţirea lui în alte două subşiruri (lungimea subşiruluieste cel mult 2).

Algoritmul corespunzător este prezentat în secţiunea următoaresub forma unei proceduri recursive care ordonează un subşir precizândlimitele acestuia.

5.3 Interclasare

Fiind date două colecţii de date, ordonate crescător (saudescrescător) după o cheie, se cere să se obţină o colecţie care săfie de asemenea ordonată crescător (respectiv descrescător) dupăaceeaşi cheie şi care să fie formată din articolele colecţiilor date.Acest lucru se poate obţine direct (fără o sortare a colecţiei finale)prin parcurgerea secvenţială a celor două colecţii, simultan cugenerarea colecţiei cerute. Prin compararea a două elemente dinlistele de intrare se va decide care element va fi adăugat în lista deieşire.

53

Page 52: Algoritmi

Deci ne interesează un algoritm de rezolvare a problemei ce areurmătoarea specificare:

Date m, (xi, i=1,m), n, (yi, i=1,n);Precondiţia: {x1 x2 ... xm} şi {y1 y2 ... yn}Rezultate k, (zi, i=1,k);Postcondiţia: {k=m+n} şi {z1 z2 ... zk} şi (z1,z2,..., zk) este o permutare avalorilor (x1, ..., xm,y1,..., yn)

O soluţie posibilă ar fi depunerea componentelor vectorului X şia componentelor vectorului Y în vectorul Z, realizând astfel a douaparte din postcondiţie. Ordonând apoi componentele vectorului Zobţinem soluţia dorită. Acest algoritm, deşi corect, este ineficientşi, în plus, nu este util în sortările externe (vezi secţiunea 5.4).Este important ca la o singură trecere prin vectorii X şi Y să seobţină vectorul Z. Acest lucru este realizat de următorul algoritm deinterclasare:

SUBPROGRAMul Interclasare(m,X,n,Y,k,Z) este: {X are cele m}{componente ordonate nedescrescător}

{La fel Y cu n componente. Cele m+n valori}{se depun în Z, tot ordonate nedescrescător}

Fie i:=1; j:=1; k:=0; Câttimp (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ă sfcât Câttimp (i<=m) execută {Există componente} Cheamă PUNE(i,xi,k,Z) {numai în X} sfcât Câttimp (j<=n) execută {Există componente} Cheamă PUNE(j,yj,k,Z) {numai în Y} sfcâtsf-Interclasare

Aici s-a folosit SUBPROGRAMul PUNE(ind,val,k,Z) care pune în vectorulZ valoarea val şi măreşte indicele ind cu 1, subalgortim dat încontinuare.

SUBPROGRAMul PUNE(ind,val,k,Z) este: {Adaugă val} k:=k+1; {în vectorul Z cu} zk:=val; {k componente şi} ind:=ind+1 {măreşte ind cu 1}sf-PUNE

54

Page 53: Algoritmi

Algoritmul MergeSort de sortare bazat pe interclasare se poatevedea în continuare.

Algoritmul MergeSort este: {Sortare prin interclasare} Citeşte n; Pentru i:=1 ; n execută Citeşte Ki sfpentru Cheamă SortInter (n,K); Pentru i:=1; n execută Tipăreşte Ki sfpentrusf-MergeSort

SUBPROGRAMul SortInter(n, C) este: Cheamă Ordon (1,n,C);sf-SortInter

SUBPROGRAMul Ordon (St,Dr,A) este: {Sortare prin interclasare a}{elementelor ASt,ASt+1,...,ADr}

Dacă St < Dr atunci Fie m:=(St+Dr) Div 2; Cheamă Ordon (St,m,A); Cheamă Ordon (m+1,Dr,A); Cheamă Inter (St,m, m+1,Dr); sfdacăsf-Ordon

SUBPROGRAMul Inter (s1,d1, s2,d2) este: { Interclasare } Fie A:=C; k:=s1-1; Câttimp (s1<=d1) şi (s2<=d2) execută Dacă (C[s1]<C[s2]) atunci Cheamă PUNE(s1,cs1 ,k,A) altfel Cheamă PUNE(s2,cs2 ,k,A) sfdacă sfcât Câttimp (s1<=d1) execută Cheamă PUNE(s1,cs1 ,k,A) sfcât Câttimp (s2<=d2) execută Cheamă PUNE(s2,cs2 ,k,A) sfcât C:=Asf-Inter

5.4 Sortare externă

O problemă cu care ne confruntăm adesea este sortarea uneicolecţii de date aflate pe un suport extern, de volum relativ marefaţă de memoria internă disponibilă. În această secţiune o astfel decolecţie de date o vom numi fişier. În acest caz nu este posibil

55

Page 54: Algoritmi

transferul întregii colecţii în memoria internă pentru a fi ordonatăşi apoi din nou transferul pe suport extern. Dacă datele ce urmează afi sortate ocupă un volum de n ori mai mare decât spaţiul de memorieinternă de care dispunem, atunci colecţia se va împărţi în nsubcolecţii ce vor fi transferate succesiv în memoria internă, se vorsorta pe rând şi vor fi stocate din nou pe suportul extern sortate.Din acest moment prin operaţii de interclasare două câte două se potobţine colecţii de dimensiuni superioare până se obţine toată colecţiaordonată.

La aceste interclasări, pentru a efectua un număr cât mai mic deoperaţii de transfer se recomandă interclasarea colecţiilor dedimensiuni minime, apoi din datele obţinute din nou vor fi alese douăcolecţii de dimensiuni minime şi aşa mai departe până se obţine osingură colecţie care va fi colecţia cerută, adică sortată.

După metodele de sortare externă folosite, se descriu treiprocedee de sortare externă:

– sortarea echilibrată; – sortarea polifazică; – sortarea în cascadă.

Evident că sortarea depinde şi de configuraţia calculatoruluifolosit, dar şi de suportul pe care se află fişierul de sortat şifişierele intermediare create.

Principial sortarea externă presupune parcurgerea a două etapeimportante:

a) Divizarea fişierului de sortat F, în n fişiere H1, H2, ..., Hn,cu sortarea internă a acestora;b) Interclasarea acestor fişiere sortate pentru a ajunge lafişierul dorit G.

56

Page 55: Algoritmi

CAPITOLUL VI

EVOLUŢIA LIMBAJELOR DE PROGRAMARE

Un limbaj de programare este un sistem de convenţii adoptatepentru realizarea unei comunicări – între programator şi calculator .Limbajele folosite pentru programarea unui calculator sunt extrem deasemănătoare limbajelor naturale . Ele sunt compuse din :

cuvinte (rezervate); punctuaţie; propoziţii şi fraze; reguli sintactice etc.

Aşa cum pentru însuşirea unei limbi străine trebuie învăţatecuvintele acesteia şi regulile cu care pot fi manevrate tot aşa pentruînsuşirea unui limbaj de programare trebuie studiate cuvintele şisemnele care îl compun împreună împreună cu regulile de manevrare alor.

De-a lungul timpului,oamenii au inventat masini pentru a calculacat mai eficient.Inaintea calculatoarelor performante din zilelenoastre,au existat alte masini de calcul.

Momentul iniţial al istoriei calculatoarelor este, de obiceilegat de numele matematicianului englez Charles Babbage. El a propusîn anul 1830 o Maşină Analitică care a anticipat în mod fascinant structuracalculatoarelor actuale. Ideile sale au devansat cu peste 100 de aniposibilităţiile tehnologice ale vremii sale. Înaintea a mai fostîncercări în acest domeniu ale lui Leibnitz şi Pascal (sec al XVII-lea) .Următorul moment de referinţă este anul 1937, când Howard Aiken, de laUniversitatea Harvard a propus Calculatorul cu secvenţă de Comandă Automată,bazat pe o combinaţie între ideile lui Babbage şi calculatoareleelertromecanice, produse de firma IBM. Construcţia acestuia a începutîn anul 1939 şi s-a terminat în anul 1944, fiind denumit Mark I . El afost în principal primul calculator electromecanic, fiind alcătuit dincomutatoare şi relee.

Înlocuirea releelor cu tuburi electronice a constituit unimportant pas înainte. Rezultatul a fost concretizat în calculatorulENIAC ( Electronic Numerical Integrator And Computer ), primulcalculator electronic digital. El conţine circa 18.000 de tuburielectronice şi executa 5.000 de adunări pe secundă, având o memoriede 20 de numere reprezentate în zecimal. Programarea sa se realizaprin poziţionarea a circa 6.000 de comutatoare, cu mai multe poziţii.O semnificaţie aparte o are faptul că în arhitectura calculatoarelorMark I şi ENIAC, intrau mai multe elemente de calcul, ce lucrau în

57

Page 56: Algoritmi

paralel la o problemă comună, fiind dirijate de o singură unitate decomandă . Această soluţie a fost aleasă datorită vitezei reduse afiecărei unităţi de calcul, în parte. La versiunea următoare s-arenunţat la această structură paralelă de calcul, deoarece s-aconsiderat că viteza unei unităţi de calcul, realizată cu circuiteelectronice, este suficientă . Soluţia prelucrării paralele a fostreluată ulterior după anii 80’ pentru mărirea performanţelor unuisistem de calcul; astfel în 1996 Firma INTEL a realizat unsupercalculator ce foloseşte peste 7000 de procesoare PENTIUMutilizând tehnica „de calcul masiv” (utilizat pentru simulareatestelor nucleare, în cercetări genetice, spaţiale, meteorologice).

De remarcat că la realizarea primelor calculatoare, în calitatede consultant al echipei, a lucrat şi matematicianul John vonNeumann, unul dintre matematicienii importanţi ai secolului XX. Dealtfel, la realizarea calculatorului EDVAC ( primul calculator cucircuite electronice ) el a stabilit 5 caracteristii principale alecalculatorului cu program memorat :

Trebuie să posede un mediu de intrare, prin intermediul căruiasă se poată introduce un număr nelimitat de operanzi şiinstrucţiuni . Trebuie să posede o memorie, din care să se citeascăinstrucţiunile şi operanzii şi în care să se poată memorarezultatele. Trebuie să posede o secţiune de calcul, capabilă să efectuezeoperaţii aritmetice şi logice, asupra operanzilor dinmemorie. Trebuie de asemenea să posede un mediu de ieşire, prinintermediul căruia un număr nelimitat de rezultate să poatăfi obţinute de către utilizator. Trebuie să aibă o unitate de comandă , capabilă săinterpreteze instrucţiunile obţinute din memorie şi capabilăsă selecteze diferite moduri de desfăşurare a activităţiicalculatorului pe baza rezultatelor calculelor.

Primul calculator comercializat a fost UNIVAC (UNIversalAutomatic Computer ) realizat pe structura calculatorului EDVAC, înanul 1951. În anii 1960 a apărut un nou tip de calculatoare:minicalculatoarele. Aceste maşini erau mai ieftine, mai mici, nu aveanevoie de aer condiţionat şi erau mult mai uşor de folosit (cel puţindupă standardele acelor timpuri) faţă de mainframe-uri. În faţăereziei, preoţii mainframe-urilor s-au înfiorat. Deţinerea unuimainframe era problema corporaţiei, datorită cheltuielilor necesare,dar un departament putea avea propriul minicalculator, pentru căacesta nu necesita spaţii speciale sau specialişti necesari unui

58

Page 57: Algoritmi

mainframe. Pe scurt, minicalculatoarele erau ieftine.Aceastădezvoltare a dus la apariţia unui nou personaj pe scenacalculatoarelor. Minicalculatoarele au adus la înlocuireaprogramatorilor de mainframe, curaţi şi bine îmbrăcaţi, cu o nouăspecie de programatori.

Minicalculatoarele au început să fie introduse în universităţişi alte instituţii de învăţământ, pentru că erau ieftine. Ele erauaccesibile şi proiectate pentru a putea suporta modificări ulterioare,ceea ce a atras un grup de entuziaşti cunoscuţi sub numele de hackeri.Aceşti hackers nu sunt identici cu cei din zilele noastre. Aceihacker-i erau plini de entuziasm faţă de calculatoare, oameni carevoiau să facă programe mai bune, mai rapide şi mai „elegante”.Dinrândurile lor s-au ridicat o parte din oameni care au făcut revoluţiacalculatoarelor personale. Piaţa minicalculatoarelor a crescut repede.Imediat ce departamentele puteau justifica nevoia minicalculatorului,acesta era instalat. Acesta a fost momentul când DEC (DigitalEquipment Corporation) a devenit a doua mare companie producătoare decalculatoare din lume.În privinţa îmbunătăţirilor aduse programelor,gama funcţiilor care pot fi realizate a crescut. Un minicalculatorpoate fi folosit simultan de mai mulţi utilizatori, cu ajutorul unuiprocedeu de împărţire a timpului de folosire a procesului numit time-sharing. Astfel, fiecare utilizator poate să prelucreze date, să creezeprograme sau să utilizeze, ca şi când ar fi singurul utilizator. Acestsistem a fost introdus şi în tehnologia de realizare a mainframe-urilor. Sisteme sofisticate de time-sharing, cu componente disc maiputernice şi programe mai sofisticate, au fost dezvoltate în acelaşitimp pentru mainframe-uri.Aceasta era piaţa calculatoarelor în anii´70: mainframe-uri şi minicalculatoare erau prezente în toatecompaniile şi principalele departamente. Pentru sacinile pe care leputeau rezolva în moduri în care le rezolvau, erau bune. Au adusmetode noi şi eficiente în birouri şi au făcut afacirele maieficiente. Totuşi, au eşuat în mărirea productivităţii personale (încreşterea eficienţei personalului, nu a corporaţiilor).

Apariţia calculatoarelor personaleLa mijlocul anilor ´70 a apărut o nouă tehnologie: miniprocesorul.

Acesta folosea multe tranzistoare conectate pe o pastilă de siliciupentru a realiza un dispozitiv de calcul.Primele microprocesoare aufost, după standardele actuale, destul de simple. Primulmicroprocesor, devine cunoscut ca 4004, a fost proiectat pe patru biţide către inginerul Marcian E. „Ted” Hoff de la Intel, în anul 1969.Clientul care i-a comandat lui Intel microprocesorul a fost o firmăjaponeză, care a dat faliment în 1970; după aceasta Intel nu se putea

59

Page 58: Algoritmi

hotărî dacă să lanseze sau nu circuitul pe piaţă. L-au lansat, şi în1974 existau mai mult de 19 tipuri de microprocesoare pe piaţă,inclusiv Intel 8088, cel care va deveni trambulina actualelorcalculatoare personale.Microprocesoarele au fost iniţial folositedrept controler - dispozitive de control – pentru maşini de spălatveselă şi frigidere. Producătorii şi proiectanţii de calculatoare nuau pierdut ocazia dată de potenţial acestor dispozitive de a fifolosite drept calculatoare.

8080, Z80, CP/MPrimele succese ale pieţei au fost microprocesorul Intel 8080 şi

noul sistem de operare numit CP/M-80 scris pentru acest cercuit. CP/M-80 a fost creat în 1975 de Gary Kildall, fondatorul şi preşedintelecompaniei Digital Research – primul producător al unui sistem deoperare pentru microcalculatoare. Astăzi, compania este o divizie alui Novell Inc. – cea mai mare companie în domeniul sistemelor deoperare în reţea.CP/M este prescurtat de la ControlulProgramului/Microcalculatorului – cel mai sugestiv nume de produs,dacă mai existase unul, de până atunci. Acest sistem de operare afost, la acea dată, extraordinar. Dacă aveai un sistem 8080 sau Z80,cu sistem de operare CP/M, cu 64 kilobiţi de RAM şi o pereche deunităţi de disc flexsibil de 8", aveai „ultimul strigăt” al modeicalculatoarelor şi îl făceai verde de invidie pe orice pasionat. Unsingur lucru le putea depăşi invidia şi cîştiga ura: să ai un disc şio imprimată; ambele necesitau o cheltuială exorbitantă.Discurileacelor timpuri merită puţină atenţie. Primul tip larg răspânditîmpreună cu microcalculatoarele aveau discuri de 14" (comparaţi-le cucele de 3,5" disponibile astăzi) şi un timp de acces suficient pentruo pauză de cafea. Apple Computer, binecunoscută ca avându-şi începuturile într-un

garaj, a apărut în 1976. Apple a fost fondată de legendarii Steve Jobsşi Steve Wozniack, şi este recunoscut drept compania care a pus bazeleindustriei calculatoarelor personale. Deşi povestea lui Visilac şi acalculatorului Apple II este bine cunoscută, merită să o spunem încă odată, pentru că arată motivele care au generat revoluţiacalculatoarelor personale.La mijlocul anilor `70, dacă doreai să faciîncercări de genul „şi dacă” calculând pe mainframe, trebuia să scriiun program, să-l depanezi, să încerci un set de date, să verificirezultatele, să încerci un set de date mai complex s.a.m.d. Era unprocedeu cel puţin laborios şi nu foarte practic, cu excepţia cazuluiîn care priviziunele aveau importanţă pentru corporaţie şi aveaisuficient timp la dispoziţie. Această situaţie a motivat doi studenţide la Harvard Business School să facă primul program de calcul

60

Page 59: Algoritmi

tabelar: Visicalc.Apple II avea la bază un procesor Motorola 6502(proiectat pe 8 biţi), până la 128 kilobiţi de RAM şi utiliza uncasetofon pentru a stoca date şi programe. Apple a încheiat oînţelegere cu realizatorii lui Visicalc pentru a obţine exclusivitateaprogramului pe Apple II. Acestui program i se acordă meritul de a ficatapultat Apple de la un venit de 800.000 de dolari în 1977 la puţinsub 48 de milioane în 1979.Utilizatorii cumpărau Apple II doar pentrua rula Visicalc, şi o dată cu el un raft întreg de aplicaţii, careofereau utilizatorilor, pentru prima dată la un preţ rezonabil, puterede calcul accesibilă şi dedicata

IBM preia controlulCalculatoarele despre care am vorbit, maşinile CP/M şi Apple, nu

erau numite calculatoare personale – acesta nu a fost un termenrecunoscut până în august 1981, data de naştere a calculatorului IMBPC a fost creat de piaţă, datorită acelor sisteme de microcalculatoarecare au făcut posibilă existenţa calculatorului IBM PC.Deşimicroprocesorul care a stat la bază calculatorului IBM PC a fostprodus în 1974, calculatorul IBM PC a fost produs abia în 1981. Intel8088 era un microprocesor pe 16 biţi, care putea lucra cu mai multămemorie şi mai rapid decât predecesorii săi. IBM a delegat o companienecunoscută, numită Microsoft, pentru a realiza un sistem de operare.Restul este, aşa cum o spun ei, istorie. IBM PC a devenit un standard,în realitate o serie de standarde care au adus la vânzarea deaproximativ 100 de milioane de calculatoare personale din 1981.puterea marketing-ului IBM a dus la succesul lui IBM PC. IBM avea banişi poziţia pe piaţă astfel încât să facă calculatorul IBM PC acceptatîn corporaţii. Deşi e uşor să critici IBM pentru greşelile, destul demulte, făcute în dezvoltarea pieţei calculatoarelor personale şi lipsade receptivitate faţă de o piaţă care creştea mai rapid decât puteaacoperi IBM, fără amestecul lui IBM, această piaţă ar fi crescut multmai încet şi mai fragmentat.

Calculatorul IBM PC a continuat tendinţa dată de Apple II,aducând puterea de calcul la îndemâna utilizatorilor. Posibilitatea dea-şi îmbunătăţi şi mări productivitatea personală a fost o atracţieatât de mare, încât oamenii au trecut peste orice pentru a-şi cumpăraun calculator personal. Ei au păcălit bugetele departamentalecumpărându-le ca maşini de scris sau chiar plătind diferenţa dinpropriul buzunar.

Multe companii au avut reţineri în a urma tendinţa de introducerea calculatoarelor personale, dar au descoperit ulterior că acesteaerau folosite din plin de concurenţa. În aceste companii, de obicei,Centrul de Calcul era uluit când descoperea invazia calculatoarelor

61

Page 60: Algoritmi

personale. Fanaticii mainframe-urilor erau probabil cei mai surprinşicând aflau ce se întâmplase.

Aparent peste noapte, Centrul de Calcul pierdea un procent destulde mare din prelucrările de date ale companiei. Teritoriul pe carecredeau că îl stăpânesc era brusc invadat. Ceea ce era probabil celmai tulburător pentru ei era că utilizatorii de calculatoare personalevorbeau despre informaţii şi nu doar despre coloane de date.

Utilizatorii au descoperit că puteau combina şi prelucra cumdoresc datele. Puteau realiza rapoarte despre ceea ce îi interesa. Pede altă parte, dacă ai fi cerut la Centrul de Calcul un raport, ţi-arfi dat doar un raport standard aşa cum le genera mainframe-ul.(Rapoartele standard consumau o „mică pădure” de hârtie, când toţiutilizatorii doreau doar o pagină ).

Astfel a apărut o nouă tendinţă: aceea de a a-ţi realiza singurcalculele. Atunci când utilizatorii doreau să facă simulări financiarede tipul „şi dacă”, ei nu mai trebuiau să meargă, cu pălăria în mână(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 arfi luat în considerare cererea lor.Deja nu mai exista nici oposibilitate pentru Centrul de Calcul de a schimba lucrurile.Corporaţiile aveau toate motivele să susţină noua tendinţă şi înacelaşi timp destule motive de îngrijorare pentru anarhia care secrea. Distribuirea datelor prin companii, cum veţi vedea, avea multeimplicaţii şi exista marele risc de a scăpa totul de sub control.

Revoluţia calculatoarelor personale, mai mult decât orice, aforţat Centrele de Calcul să-şi regândească rolul şi tehnologia pecare o foloseau. Ele nu au avut cu adevărat de ales şi au devenitServicii de gestiunea de informaţie (Management Information Service)sau IT (Information Tehnology) sau orice altceva care conţineacuvântul informaţie. De asemenea, au trebuit să urmeze sau cel puţinsă se obişnuiască cu valul tehnologiilor aduse de calculatoarelorpersonale.

Începutul conectăriiPe timpul CP/M-ului, preţul perifericilor de calitate era

exorbitant. Un disc de 14" şi 10MB, care consuma 5 amperi şi făceazgomot ca un avion care decola, era tot atât de scump ca şi uncalculator. O imprimată matriceală, care nici nu se apropia decalitatea unei letter-quality, era o resursă preţioasă. În momentullansării calculatorului IBM PC preţurile scăzuseră, dar erau încădestul de mari. Pe scurt, perifericele calculatoarelor personale erauca aurul: rare şi scumpe.

62

Page 61: Algoritmi

Nu era practic ca fiecare calculator să aibă disc şi imprimată, deşifără ele productivitatea calculatoarelor personale era mai mică. Oaltă problemă era folosirea în comun a datelor. Dacă aveai nevoie deun document creat de altcineva, trebuia să iei dischetă, să-ţi puipantofii de sport şi să alergi la acel microcalculator să-l iei. Deaici, numele acestui tip de partajare a datelor: „reţea sportivă”.

„Reţeaua sportivă”Acest tip de reţea a ridicat multe probleme. Cum puteai să fii

sigur că documentele cu care lucrai erau la zi, dacă diverse copiimodificate de un număr oarecare de oamenii circulau pe diversedischete? Cum poţi opri furtul documentelor? Cum poţi opri furtuldocumentelor? Şi dacă ultima versiune, şi singura, a unui document seaflă pe o singură dischetă folosită de cineva drept suport pentruceaşcă de cafea? Şi dacă...?Existau sute de probleme cu această reţea şi toate evidenţiau osingură soluţie: nevoia, absolută necesitate, de a schimba documenteleelectrice între calculatoare. Combinaţi cu dorinţa de a schimba, de afolosi în comun discuri şi imprimate scumpe, şi aveţi o problemă lacare să meditaţi. Nevoia de a folosi în comun date şi periferice astimula crearea primei reţele locale de calculatoare, dar aşa cum veţivedea, problema centrală a fost nevoia de a folosi în comunicatie.

Comutatoarele de dateO modalitate de a folosi în comun periferice a fost folosirea

unui comutator de date: un dispozitiv ce permite doar unui utilizator laun moment dat să folosească dispozitivul, ca exemplu o imprimată. Dacăo altă persoană folosea imprimata când doreai tu să o foloseşti,trebuia să aştepţi până termina. Un comutator de date poate ficomparat cu o coadă la bancă. Orice persoană (datele ce vor viimprimate) care se aşează prima coadă (comutatorul) ajunge prima lacasier (imprimanta). Restul trebuia să aştepte până ce aceastatermină.

Comutatorul de date oferă utilizatorului o conexiune pe portulserial sau paralel, pe bază căreia primul utilizator care cereprimeşte dreptul de folosi imprimanta. Calculatorul care nu mai arenevoie de periferic trebuie să trimită o secvenţă de caractere princare spune de fapt „Am terminat”.Aceste dispozitive, deşi erau bune pentru imprimantă şi plotere (eleîncă mai sunt folosite – câteva companii încă le mai oferă ), nupermiteau folosirea în comun a discurilor. De asemenea, necesitau olinie dedicată între calculator şi comutator. Aceasta devenea dificil

63

Page 62: Algoritmi

de realizat când calculatoarele erau răspândite pe o suprafaţă mare,şi imposibil dacă erau mai multe calculatoare.

„Aici servesc discuri” Prima încercare de a realiza ceea ce astăzi numim reţea locală

(LAN) a fost tehnologie, acum învechită, numim disc server. Un discserver era un calculator, prin care, printr-o tehnică de comunicaţieoarecare, era legat de un grup de calculatoare numit clienţi. El rulaun sistem de operare special care era proiectat astfel încât să poatăpermită accesul mai multor clienţi în acelaşi timp la disc şi laimprimată: acest sistem se numeşte sistem de operare pentru reţea(Network Operating System sau NOS).

Funcţionarea reţelei Aplicaţia client/server

Primele aplicaţii de reţea erau în majoritate programe integrate.De exemplu, dacă ofereau o bază de date multiutilizator ele aveau şipartea frontală (front-end) de interacţiune cu utilizatorului şi„motorul” bazei de date (partea de program care lucra cu fişierelebazei de date) pe acelaşi PC. Singura parte care se putea afla înreţea, pe server, era baza de date.

În această configuraţie, calculatorul client realiza toatăprelucrarea datelor (citire, căutare a înregistrărilor dorite întredatele citite etc.). Aplicaţiile acestea pot fi descrise ca având doarclient. Serverul era o simplă „pompă” de date: trimitea utilizatoruluidate din fişierele aflate pe disc sau le primea şi le stoca pe disc.

În ultimii ani au apărut un număr mare de sisteme de bază de datesofisticate care pun în reţea „motorul” de acces la baza de date carese află în parte frontală (front-end) utilizatorul. Acestea se numescsisteme client/server.

O dată cu îmbunătăţirea performanţelor datorită eliminăriisupraîncărcării reţelei cu transferuri mari de date, mai există şiavantajul faptului că serverul poate deservi mai mulţi clienţi înacelaşi timp. Întregul proces de sincronizare al accesului la baza dedate, care trebuia realizat de clienţi, este acum realizat de server,ceea ce face aplicaţiile mai simple şi întregul sistem mai eficient.Bazele de date nu sunt singurele aplicaţii care pot fi realizate însistem client/server. Alte aplicaţii client/server includ servere depoştă electronică, sisteme de vizualizare pe calculator a imaginilorşi urmărire serviciilor de reţea.

Avantajele sistemelor client/server sunt următoarele: securitate mai bună, deoarece accesul la datele din baza dedate server este indirect. Utilizatorii nu pot vedea

64

Page 63: Algoritmi

fişierele de date decât dacă li se dă acest drept în modexplicit. Performanţele pot fi îmbunătăţite uşor, deoarece o mai bunăproiectare a serverului poate duce la o mai bună coordonare autilizatorilor care doresc servicii în acelaşi timp şi, deaici, performanţe mai bune. În cazul severelor de baze dedate prin reţea pentru a găsi ce îi interesează; e suficientca ele să trimită cereri către server, iar serverul le vatrimite doar rezultatele pe care le doresc.

Creşte raportul calitate/preţ. Clienţii trebuie doar să aibăsuficientă putere de calcul pentru a rula partea frontală (front-end).(Când sunt necesare performanţe mai mari, serverul poate fi înlocuitcu un calculator personal mai performant şi, respectiv, mai scump).

Dezavantajele sistemelor client/server: Complexitatea: nu este simplu, de obicei, să configurezi şisă administrezi sisteme client/server.

Necesităţi: pentru a avea mulţi utilizatori, serverul dinsistemele client/server are nevoie de un calculator scump. Aplicaţiilede pe server au tendinţa să devie mai mari şi mai complexe şi aunevoie de mai multă memorie RAM.

Preţ: performanţele serverului scad o dată cu creşterea număruluide utilizatori. Pentru a reface performanţele, serverul de bază dedate trebuie să ruleze pe o maşină dedicată acelui server. Deci, acolounde cândva era un server dedicat general, care funcţiona şi ca serverde bază de date, acum avem un server dedicat general şi un server debaze de date dedicat, ceea ce duce cel puţin la dublarea costului.

Tehnologii de grup

Tehnologiile de grup (groupware) sunt un set de tehnologii careau scopul de a îmbunătăţi productivitatea a doi sau mai mulţiutilizatori care cooperează în realitate unor obiective comune. Ideeaeste ca o dată ce reţeaua uneşte utilizatorii, munca şi comunicărilecu privire la ea pot fi automatizate pentru îmbunătăţirea fluxuluimuncii şi a oportunităţilor. Teoretic, un grup de oameni care muncescîmpreună într-o activitate comună sau pentru obiective comune poate fimult mai eficient decât un grup de oameni care muncesc independent.Deoarece calculatoarele îmbunătăţesc dialogul între membrii grupuluişi urmăresc progresele lor, detaliile nu vor mai fi omise, iardesfăşurarea poate fi foarte uşor de urmărit.

Aceste idei au fost aplicate la procese cum sunt planificate şiadministrate proiectelor. Planificarea în reţea permite unui grupdintr-o reţea să-şi facă orare pe reţea. Când vor să-şi coordonezeactivităţile, de exemplu să stabilească o întâlnire, orarul grupului

65

Page 64: Algoritmi

poate fi examinat şi poate fi găsit momentul când toţi membrii suntdisponibili. Folosind poşta electronică, aceştea pot fi rugaţi să vaşedinţă (sau în organizaţiile mai autoritate li se ordonă).

Alte caracteristici ale aplicaţiei de grup: Sisteme de informare (oferite în sisteme de poştăelectronică cum ar fi cc: Mail). Baze de date folosite în comun. Sisteme de conducere a proiectelor. Servicii de bibliotecă (pentru administrareadocumentaţiilor aparţinând unui grup). Sisteme de control al versiunii (asemănătoare cu serviciulde bibliotecă, dar cu facilităţi de control al arhivării şigăsirii diverselor versiuni de fişier; aceste sisteme sunt deobicei folosite pentru dezvoltarea programelor).

Una dintre cele mai lăudate aplicaţii ale tehnologiilor de grup,Lotus Notes, este un sistem de baze de date cu poştă electronică.Rolul lui Notes este de a răspândi informaţiile deţinute în bazele dedate ale organizaţiilor, la un număr oarecare de utilizatori. Sistemulpermite duplicarea şi sincronizarea mai multor copii de baze de date.

O altă direcţie principală a aplicaţiilor de grup esteposibilitatea urmăririi fluxului muncii. Ideea este că grupurile deutilizatori care sunt într-o reţea pot beneficia de automatizareaactivităţilor de rutină. Mare parte a sistemelor care se ocupă defluxul muncii se bazează pe formulare. Ele primesc date de la opersoană, pe care apoi le transmit, dacă e posibil cu datesuplimentarea din alte surse, celorlalţi membri. Ele au mecanismepentru contabilizarea şi urmărirea tranzacţiilor şi raportareastadiului muncii. Obiectivele vor fi mai rar uitate sau amânate,deoarece calculatoarele sunt mai de încredere decât oamenii. Fluxulmuncii este concept atât de important în reţele, încât multe dintreprincipalele companii producătoare de produse de reţea au investit încompanii care dezvoltă tehnologii de bază pentru suportul fluxuluimuncii.

Problema cu aplicaţiile de grup este că e greu ca oamenii să seobişnuiască cu ea! („Poţi să duci un cal la apă, dar nu poţi să-l facisă bea.”).

NIVELE ALE LIMBAJELOR DE PROGRAMARE“Nivelul” unui limbaj este apreciat prin poziţia pe care o ocupă

pe scara constituită de limbajul recunoscut de microprocesor (limbajmaşină) şi limbajul natural al programatorului (limba română, limbaengleză …) .

66

Page 65: Algoritmi

Un limbaj de nivel scăzut este foarte apropiat de maşină, elmanipulează cu elemente de nivel hardware, fizic, cum ar fi :registru, microprocesor, locaţie de memorie, port de intrare / ieşireetc.

Un limbaj de nivel înalt sau foarte înalt manipulează cu concepteapropiate de limbajul natural, concepte de nivel logic, cum ar fi:colecţie de date, nume de operaţie (sort, writeln, open), variabile,constante ( asemănătoare ca înţeles cu cele din matematică).

Cu ajutorul unui limbaj de nivel înalt programatorul se face multmai uşor înţeles de către calculator . Uneori o singură limie deprogram scrisă cu un astfel de limbaj poate echivala cu sute de liniide program scrise în limbaj maşină. Deci din punct de vedere alreducerii timpului de realizare a unui program şi al siguranţei înfuncţionare (absenţa erorilor de programare) este de preferat unlimbaj de nivel cât mai ridicat (înalt sau foarte înalt). În schimb,pe măsură ce limbajul are un nivel mai ridicat execuţia programuluiconceput cu ajutorul său va fi mai lentă, decât a unui program cerealizează aceleaşi operaţii dar este scris în limbaj de asamblare.

O altă diferenţă esenţială între cele două tipuri de limbaje oreprezintă portabilitatea, adică posibilitatea transferăriiprogramelor pe un alt tip de maşină decât cea pe care au fostconstruite. Din acest punct de vedere limbajul de asamblare esteneportabil deoarece el este specific microprocesorului. Programelerealizate pe un tip de maşină trebuie rescrise integral pentru noultip de maşină , folosind un nou set de instrucţiuni – care deobiceidiferă foarte mult. Lucrurile stau altfel cu programele concepute cuajutorul unui limbaj de nivel înalt, deoarece acestea sunt detaşate demaşină. Între un astfel de program şi calculator se interpunecompilatorul (sau interpretorul) care rezolvă corect transformareafişierului-sursă în fişier - executabil.

Limbaje procedurale – neprocedurale Cele două tipuri de limbaje, procedurale şi neprocedurale, se

diferenţiază prin nivelul de organizare (structurare) a unui program.Limbajele neprocedurale sunt concepute pentru a gândi un program lanivel de instrucţiune, pe când cele procedurale, obligă programatorulsă conceapă programe la nivel de bloc. Într-un limbaj procedural(numit şi limbaj structurat) programele sunt scrise instrucţiune cuinstrucţiune, dar ele sunt organizate logic în blocuri (grupuri deinstrucţiuni) ce realizează o acţiune bine determinată. În general unbloc are un punct de intrare şi un punct de ieşire – nu mai multe.

Un limbaj procedural oferă posibilitatea utilizării unui nivelridicat de concepere a unui program şi duce la realizarea de programe

67

Page 66: Algoritmi

coerente şi protejate la erori. Prin contrast, limbajele neproceduralenu favorizează programatorul în a se desprinde de nivelul„instrucţiune” şi duc deseori la programe greu de controlat – mai alesîn cazul programelor de dimensiuni mari.

Limbajele neprocedurale sunt încă preferate de unii utilizatoridatorită timpului foarte scurt cât decurge învăţarea şi utlizarea lor.

Limbaje orientate Din punctul de vedere al aplicabilităţii unui limbaj, limbajele

pot fi orientate pe o anumită problemă sau concepute pentrusoluţionarea oricărui tip de problemă – limbaje de uz general saualtfel spus, neorientate pe o problemă.

Limbajele orientate prezintă un grad înalt de specificitate pecând un limbaj neorientat reprezintă un cadru general ce permiteintroducerea de către utilizator a conceptelor şi prelucrărilordorite.

Deci, diferenţa esenţială dintre cele două tipuri de limbaje oconstitue nivelul conceptual definit. Cele specializate posedă dejaintegral suportul necesar şi permit programatorului să se concentrezela ansamblul problemei, pe când cele nespecializate lasă în sarcinaprogramatorului manevrarea nivelelor inferioare ale problemei.

Limbaje concurente Un limbaj concurent permite definirea de procese (prelucrări)

paralele, execuţia sa fiind ramificată la un anumit moment de timp.Prin contrast limbajele neconcurente (majoritatea limbajelor) au odesfăşurare 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 scăzut Această categorie de limbaje are un reprezentant autoritar şi

anume: limbajul de asamblare. Diferenţierile care se pot face pentrulimbajele de nivel scăzut sunt următoarele:

după tipul de maşină;Regulile respectate de versiunile limbajului de asamblaresunt :– nouă versiune o include complet pe cea anterioară,– versiunea nouă oferă funcţii suplimentare şi le realizeazăpe cele vechi mai rapid.

după mediul de programare oferit.Aspectul unui limbaj poate fi schimbat radical de mediul de

programare oferit . Pentru limbajul de asamblare există mai multe68

Page 67: Algoritmi

implementări disponibile, începând cu pachete ce operează în mod linieşi culminând cu medii integrate în care toate operaţiile se potdeclanşa de la un acelaşi pupitru de comandă .

Nu sunt luate în considerare decât aceste medii integrate(denumite generic medii Turbo), dintre care se detaşează TurboAsamblorul firmei Borland TASM.

Limbaje de nivel înalt neorientateBASIC

A fost creat în 1964 la Darmooth College (S.U.A.). Denumirea saprovine de la iniţialele cuvintelor Beginner’s Allpurpose SymbolicInstruction Code (Cod de instrucţiuni simbolice, de uz general,destinat începătorilor).

Are următoarele caracteristici fundamentale : – simplu de învăţat; instrucţiunile sale sunt cuvinte din limba

engleză sau prescurtări ale acestora;– neorientat pe un anumit tip de problemă; permite construirea de

aplicaţii; – este un limbaj nestructurat, ceea ce îi permite să fie uşor

învăţat.Din cauză că a cunoscut o largă răspândire, au fost implementate

noi versiuni de Basic: GW-BASIC, QUICK BASIC, TURBO BASIC, VISUALBASIC (Basic for Windows).

FORTRANLimbajul Fortran este decanul de vârstă al limbajelor de largă

folosinţă. A apărut în 1956 şi îşi datorează numele prescurtăriicuvintelor: FORmula TRANslation (Traducere de formule). Iniţialreprezenta un limbaj orientat pe calcule ştiinţifice având definiteconcepte precum: matrice, funcţii trigonometrice, numere reale îndublă precizie. Versiunile ulterioare care au cunoscut o marepopularitate au extins posibilităţile limbajului trasformându-l într-un limbaj eficient, de uz general. În prezent există pentru IBM-PCdouă implementări mai importante ale limbajului: Microsoft Fortran,Fortran for Windows.

Deşi nu poate fi considerat „depăşit” din punct de vedereconceptual (este un limbaj algoritmic – structurat) este neindicatăfolosirea lui datorită absenţei unor medii de programare performanteşi pentru că tendinţa actuală îi este defavorabilă.

PASCAL

69

Page 68: Algoritmi

Conceptualizat în anul 1970 de către Niklaus Wirth, limbajulPASCAL poartă numele matematicianului şi filosofului BLAISE PASCAL,în semn de recunoaştere a meritelor sale în teoretizarea maşinilor decalcul.

Creat după acumularea de cunoştiinţe temeinice în ştiinţalimbajelor formale, din confruntarea cu probleme concrete aleprogramării, limbajul PASCAL a constituit la vremea respectivă unlimbaj modern, menţinându-se ca atare şi în prezent, datorită faptuluică posedă o solidă bază conceptuală.

Limbajul PASCAL a introdus în versiunea sa iniţială noţiunea deprogramare structurată şi ulterior noţiunile de date (structuri)dinamice, date (structuri) definite de utilizator.

În prezent standardul implementărilor PASCAL cuprinde următoareleelemente:

programare structurată de tip algoritmic; definirea de noi funcţii sau proceduri; tipuri de date definibile de către utilizator; structuri de date dinamice; adresări indirecte ale datelor; recursivitate; rutine complete de intrare / ieşire; funcţii de conversie a datelor din ASCII în format internşi invers; set complet de funcţii matematice; funcţii elementare de grafică 2D; posibilitatea inserării direct în sursă a instrucţiunilorîn limbaj de asamblare; posibilitatea definirii de overlay-uri pentru program.

Versiunile standard ale implementărilor PASCAL sunt cele oferitede Microsoft şi Borland, cu avantaj pentru cele din urmă (TURBO PASCAL5.0, TURBO PASCAL 5.5) datorită mediului de lucru performant (de tip“TURBO” ). Combinaţia PASCAL + TURBO a reprezentat un succes imens înrândul programatorilor având ca singur rival cealaltă combinaţie:C+TURBO.

Limbajul C Acest limbaj de programare , cu cel mai scurt nume , a fost creat

în 1971 de către Dennis Ritchie şi Brian Kernigham pentru dezvoltareasistemului de operare UNIX.

Principalele caracteristici ale limbajului sunt: limbaj structurat de nivel înalt; posedă concepte de nivel scăzut, ceea ce permiteexploatarea portabilă a caracteristicilor intime unei maşini;

70

Page 69: Algoritmi

rutine de conversie a datelor foarte evoluate; tipuri de date definibile de către utilizator; gestionarea elaborată a datelor de tip dinamic; definirea de noi funcţii; adresări indirecte ale datelor , variabilelor ( pointer-i ); recursivitate; set complet de funcţii matematice; funcţii pentru realizarea de grafică elementară 2D; funcţii de apel servicii DOS; posibilitatea definirii de overlay-uri pentru un program; concizie deosebită a limbajului. Pentru versiunile standard ale implementărilor limbajului Cexistă medii de programare de tip “TURBO” ce aparţinfirmelor: Microsoft – produsul QUICK C şi firmei Borland –produsele TURBO C.

Limbajul ADA A fost creat special pentru a gestiona totalitatea aplicaţiilor

dezvoltate şi utilizate de N.A.S.A. Noutatea limbajului (de tipstructurat, algoritmic) o constitue concurenţa, deci posibilitatealansării de procese paralele (sincronizate interactiv în finalulexecuţiei lor). Saltul calitativ este evident şi deschide un noudomeniu în programare … dar nu pentru IBM-PC. Versiunileimplementărilor limbajului ADA pe IBM-PC nu posedă tocmai acestă partede concurenţă, reducând limbajul la un simplu limbaj structurat de uzgeneral. Deci, ADA este un limbaj ultramodern din punct de vedereteoretic dar ineficient din punct de vedere practic pentru IBM-PC-uri.

Limbaje orientate pe gestiunea bazelor de date Necesităţile actuale în practica utilizării calculatoarelor se

îndreaptă cu precădere spre gestionarea bazelor de date de maridimensiuni. O explicaţie a acestei orientări e dată de faptul că obază de date reprezintă o informaţie, iar cel ce deţine informaţiicomplete şi rapide într-o anumită problemă este indiscutabil cu un pasînaintea celorlalţi. Concurenţa din domeniul economic poate fi numităpe bună dreptate o bătălie informaţională.

Un sistem de gestionare a bazelor de date (S.G.B.D.) de tipclasic operează cu următorii termeni fundamentali:

câmp – o locaţie în care se poate memora o informaţie binedeterminată; înregistrare – mai multe câmpuri alcătuiesc împreună oînregistrare;

71

Page 70: Algoritmi

baza de date – colecţie de înregistrări.Deci, datele sunt gestionate prin intermediul unei structuri,

organizată ierarhic, la un nivel de organizare logică.Tendinţa modernă în exploatarea bazelor de date constă în

deplasarea interesului către bazele de date relaţionale. Diferenţaesenţială constă în definirea unui nivel logic suplimentar întredatele gestionate. Acestea nu mai sunt privite ca simple fişe izolateîntre ele ci pot fi analizate pe baza legăturilor (relaţiilor) ceexistă între ele.

Noţiunile cu care operează un S.G.B.D. relaţional sunturmătoarele:

tabel – structură fundamentală de “depozitare” a datelor; linie în tabel – echivalentul unei înregistrări clasice; coloană în tabel – echivalentul unui câmp de tip clasic; bază de date – o colecţie de tabele, conectate prinvalorile anumitor coloane.

Această nouă concepţie permite definirea de structuri 1:n. O“înregistrare” poate conţine n valori pentru un “câmp” anumit nu unasingură ca în cazul clasic. Structurile de tip 1:n pot fi rezolvate şicu ajutorul unui S.G.B.D. clasic, dar întreaga gestiune a operaţiilorrevine programatorului pe când un mediu relaţional furnizează dinstart servicii speciale.

Spre deosebire de S.G.B.D.-urile clasice, un mediu relaţionalpresupune ca cerinţă minimală posibilitatea manipulării datelor prinintermediul conexiunilor logice stabilite. Pentru aceasta existădefinit (şi impus ca standard unanim recunoscut) limbajul deinterogare SQL (Structured Query Language – limbaj de cereristructurate). Prin intermediul său sunt permise următoarele operaţii:

regăsire date (conexate logic) ce îndeplinesc o anumităcondiţie; definire ordine de returnare a datelor; redefinire conectări logice ale datelor; exploatare; programare.

Avantajele unui S.G.B.D. clasic sunt: simplitate în manevrare; deci efort de studiu redus; pot funcţiona pe un sistem de calcul ce nu implică resursespeciale, ci doar spaţiu de stocare extern suficient pentruproblema dată; preţ de cost redus faţă de cele relaţionale.

Avantajele unui S.G.B.D. relaţional sunt: nivel logic superior (corelaţii, structuri 1:n ),

72

Page 71: Algoritmi

prelucrări (regăsiri) de date cu un înalt nivel decomplexitate; nivel superior de portabilitate a aplicaţiilor, datelor.

S.G.B.D. -uri clasice dBASE III

Cel mai răspândit sistem de gestiune a bazelor de date estedBASE, în diversele lui versiuni. El poate fi considerat un “BASIC” albazelor de date. La momentul apariţiei a constituit o adevăratărevoluţie în domeniul S.G.B.D.-urilor.

Meritele sale principale care l-au impus atenţiei utilizatorilorşi programatorilor sunt :

foarte simplu de utilizat; limbaj de nivel foarte înalt , simplu de învăţat; interactivitate bună a sistemului; poate funcţiona cu resurse extrem de restrânse; Dezavantajele principale ale dBASE-ului sunt: viteză de lucru extrem de scăzută; limbaj de programare cu lacune greu de surmontat (nu posedăsalturi, funcţii matematice reduse, erori de implementare); aplicaţiile create slab interactive; imposibilitateta conectării cu un alt limbaj.

Cele mai importante implementări ale sale sunt: dBASE III Plusşi dBASE IV.COBOL

A fost creat în 1950 şi reprezenta singura posibilitate degestionare a unei baze de date. Reprezintă în primul rând un limbaj deprogramare special conceput pentru informatica de gestiune. Dacă facemo comparaţie, sugestivă, COBOL este echivalentul FORTRAN-ului pentrusistemele de gestiune a bazelor de date (din punct de vedere istoricşi al performanţelor).

Limbajul este considerat greoi şi inflexibil, iar pentru creareaunui program foarte simplu e nevoie de scrierea unui adevărat eseu. Singurul avantaj real al COBOL-ului este portabilitatea saridicată.FOXBASE

Sistemul dBASE a incintat firmele producătoare de soft,datorită popularităţii sale şi pe de altă parte a calităţilor scăzuteale implementărilor originale furnizate de firma Ashton-Tate. Auapărut noi implementări ale limbajului care au încercat să furnizezeunelte profesionale pe baza acestui suport conceptual.

73

Page 72: Algoritmi

Versiunile FOXBASE 2.10 şi FOXBASE PRO se constitue înmedii performante atât pentru programatori cât şi pentru utilizatori.ISIS

Este distribuit gratis de către UNESCO, ceea ce îl face cuadevărat interesant. Caracteristicile ce îl fac interesant sunt:

interactivitate bună; posibilitate definire structuri 1:n; suport de reţea locală; un limbaj intern (o versiune de PASCAL) cu care seprelucrează datele; adaptabilitate foarte bună.

S.G.B.D. –uri relaţionaleORACLE

Se poate afirma fără teama de a greşi că ORACLE reprezintă celmai performant S.G.B.D. disponibil la momentul actual. Pe lângă faptulcă posedă avantajele unui mediu de tip relaţional ORACLE este gânditca un sistem exhaustiv pentru rezolvarea problemelor de utilizare sauprogramare.

Limbajul intern folosit este SQL Plus şi este permisă conectareacu alte limbaje externe evoluate (orientate către C). Putem menţiona:

viteză de lucru foarte bună; exploatare interactivă la nivel SQL; limitări de lucru greu sau imposibil de atins (maxim 65535caractere într-un câmp, număr nelimitat de câmpuri, deînregistrări); exploatare eficientă a spaţiului pe disc (memorareacâmpurilor în format variabil).

Oracle este implementat pe majoritatea tipurilor de computeremari, ceea ce oferă portabilitatea aplicaţiilor, dar mai alesposibilitatea conectării la calculatoare puternice.PARADOX

Reprezintă un S.G.B.D. cu adevărat profesional. El îndeplineştetoate cerinţele unui produs cu adevărat modern şi performant şi anume:

interactivitate foarte bună; viteză de lucru mare; servicii şi auxiliare; limbaj de programare evoluat (PAL – Paradox ApplicationLanguage), dotat cu compilator.

Limbaje orientate pe calcul tabelar

74

Page 73: Algoritmi

Aplicaţiile împreună cu limbajele implementate pentru rezolvareaproblemelor descrise în continuarea nu pot fi considerate medii deprogramare propriu-zise.

Aplicaţiile de tip „tabelă de calcul” au fost concepute înajutorul funcţionarilor, pentru a prelua o parte imensă din rutina delucru inerentă unor astfel de activităţi.

Denumirea provine din limba engleză şi este o traducere pentrutermenul „spread-sheet” (spread-întindere, desfăşurare, foaie, tabel;sheet-schemă, diagramă, a acoperi cu un strat). În traducere directăaceasta ar însemna – pentru cazul de faţă – organizarea unei foi (aunui tabel).

Iată cum funcţionează un program de tip spread-sheet: elementul de lucru îl reprezintă un tabel; un tabel este format din linii şi coloane; intersecţia unei linii cu o coloană se cheamă celulă; tabelul este vizualizat pe ecran prin intermediul uneiferestre; în fiecare celulă poate exista una din entităţileurmătoare: text, numere, formule, secvenţe de program,macroinstrucţiuni.

Pe lângă aceste caracteristici specifice unui spread-sheetcerinţele minimale ale unui pachet de calcul tabelar includ:

posibilitatea „căutărilor inverse” (de la rezultatul unuicalcul, la valorile care l-au generat); posibilitatea de lucru multi-tabel (mai multe tabelelsimultan); funcţii de editare şi formatare a textului (editor de texteobişnuit); funcţii grafice (diagrame, prezentări); sistem de gestiune a bazelor de date (pentru celulele unuitabel); tipărire de calitate (posibilitatae de a lucra cu mai multetipuri de imprimante, exploatarea rezoluţiei unei imprimantelaser, set bogat de fonturi).

Spre deosebire de limbajele de programare propriu-zise, celefolosite de spread-sheet-uri sunt special concepute pentru a fifolosite de nespecialişti (uşor de învăţat, uşor de utilizat).

Un astfel de limbaj (de tip interpretor) se constituie într-uncadru general pentru rezolvarea problemelor funcţionarilor din diversedomenii de activitate.

O aplicaţie realizată cu un spread-sheet poate fi modificată şiadusă la zi direct de către utilizator, fără a mai fi necesară

75

Page 74: Algoritmi

intervenţia programatorului. Produsul obţinut are flexibilitatemaximă, iar efortul necesar realizării lui este minim.

Dezavantajele principale ale aplicaţiilor realizate cu ajutorulunui spread-sheet le constitue imposibilitatea depăşirii cadrului de„programare” oferit şi dificultatea de a realiza prelucrări foartecomplexe . Însă aceste dezavantaje sunt mai mult teoretice deoarece nueste cazul de a realiza aplicaţii cu astfel de cerinţe folosind unspread-sheet. Programele de calcul tabelar rezolvă în mod strălucit oproblemă punctuală.

Cele mai cunoscute şi răspândite produse de tip „calcul tabelar”sunt: LOTUS 1-2-3

Lotus 1-2-3, produs al firmei Lotus Development este în mod sigurcel mai răspândit produs din această categorie. Datorită popularităţiisale el s-a constituit într-un adevărat standard (neoficial) pentruspread-sheet-uri. La nivel de ansamblu, LOTUS se preuintă ca oaplicaţie cu bună interactivitate. Reproşurile ce i se pot aduce sunt:meniurile (uneori stufoase şi nelogic ramificate) şi help-ul care nutotdeauna este la obiect.QUATRO PRO 2.0

Spread-sheet-ul QUATRO, realizat de firma Borland este cel mainou şi puternic produs din categoria sa. El combină într-un modfericit tot ceea ce este pozitiv la rivalii săi adăugând şi multefacilităţi proprii.EXCEL

Produsul firmei Microsoft, EXCEL este o aplicaţie carefuncţionează sub Windows. De aici rezultă în mod direct unele dincaracteristicile sale (utilizare mai comodă, meniuri foarte clare şistandardizate, funcţii grafice deosebit de puternice, viteză de lucruinferioară lui Quatro).

Câteva specificaţii tehnice pentru EXCEL ar fi: tabelă cu dimensiunea maximă de 1638 x 256 celule; lăţimea maximă a unei coloane este de 255 caractere; tabelele şi grafica pot exista pe foi distincte; funcţioneauă după principiul WYSIWYG; se pot folosi maxim 4 fonturi la un moment dat; limbaj de programare puternic şi flexibil; posibilitatea definirii de macroinstrucţiuni; nu posedă funcţie de salvare automată; conţine suport de funcţionare în reţea; detectează prezenţa coprocesorului matematic şi face uz defacilităţile acestuia;

76

Page 75: Algoritmi

lucrează cu memoria expandată.

Alte limbaje orientate Limbaje orientate pe calcul matematic simbolic

Specialiştii din domeniul cercetării matematice au la dispoziţieunelte de lucru extrem de utile pentru eliminarea calculului matematicrutinier. În acest scop au fost create limbaje de programare care potrecunoaşte şi rezolva formule sau ecuaţii matematice complexe.Expresiile manevrate pot conţine operaţii algebrice elementare,operatori de derivare, de integrare, operatori diferenţiali care suntrecunoscuţi de sistem ca atare. În plus sunt oferite instrucţiuniabsolut necesare pentru a controla un program.

Cele mai importante produse de acest gen sunt REDUCE, SYMNON,MATHCAD, MATHEMATICA, MATHLAB.

Limbaje orientate pe programarea inteligenţei artificialeAcest tip de limbaje diferă esenţial de cele algoritmice.

Modalitatea de programare este descriptivă şi are intenţia declaratăde simulare a raţionamentului uman. Pentru rezolvarea unei problemesunt furnizate seturile de reguli şi informaţii necesare, iar apoi sedescrie în ce constă problema ca atare. Limbajul este capabil săopereze deducţiile (deciziile) necesare pentru a rezolva problemaîntr-un caz particular ce apare în practică.

Aşadar, aceste limbaje descriu problema de rezolvat (în termeniideducţiilor logice) pe când limbajele de tip algoritmic descriu metodade rezolvare a problemei. Domeniile de aplicabilitate pentru limbajelede programare a inteligenţei artificiale sunt cu predilecţie:realizarea de sisteme expert (programe ce înlocuiesc experţii umani),computerizarea procesului de producţie, robotică, tratarea limbajelornaturale.

Cele mai importante limbaje de acest tip sunt: PROLOG (PROgramming in LOGic) creat în 1973 şi implementatpe PC-uri abia în 1986 de firma Borland sub forma Turbo-Prolog. LISP (LISt Processing language) conceput în 1976 şiimplementat pe PC-uri de firma Microsoft sub forma MuLISP.

GENERAŢII DE CALCULATOAREGeneraţia I (1946-1956) caracterizată prin:

Hardware: relee, tuburi electronice; Software: programe cablate, cod maşină, limbaj deasamblare; Capacitate de memorie: 2 Kocteţi;

77

Page 76: Algoritmi

Viteză de operare: 10.000 de operaţii/sec.; Calulatoare: ENIAC, UNIVAC, IBM;

Generaţia a II–a (1957-1963) marcată de apariţia tranzistorului Hardware: tranzistoare, memorii cu ferite, cablaj imprimat; Software: limbaj de nivel înalt ( Algol, Fortan) Memorie: 32 Kocteţi; Viteza: 200.000 de instrucţiuni/sec Calculatoare: IBM 7040, NCR501;

Generaţia a III–a (1964- 1981) caracterizată prin: Hardware: circuite integrate (la început pe scară redusă,apoi pe scară medie şi largă; scara de integrare se referă lanumărul de componente electronice pe unitatea de suprafaţă),cablaje imprimate multistrat, discuri magnetice, aparariţiaprimelor microprocesoare; Software: limbaje de nivel foarte înalt, programareorientată pe obiecte B.Pascal, programare structurată LISP,primele programe pentru grafică şi baze de date . Memorie: 1÷2 Mocteţi ; Viteza: 5.000.000 de operaţii/sec; Calculatoare: IBM 370, FELIX Comunicaţii: Primele comunicaţii prin satelit, transmisiade date prin fibră optică.

Generaţia a IV-a (1982-1989) caracterizată prin: Hardware: circuite integrate pe scară foarte mare (VLSI),sisteme distribuite de calcul, apar microprocesoarele de16/32 biţi, primele elemente optice (discurile optice); Software: Pachete de programe de largă utilizare, sistemeexpert, sisteme de operare, se perfecţioneaza limbajele deprogramare orientate pe obiect, baze de date relaţionale; Memorie: 8÷10 Mocteţi; Viteza: 30 de milioane de instrucţiuni/sec; Caculatoare: INDEPENDENT, CORAL, IBM (apar mai multeversiuni)

Generaţia a V-a (1991-2002) în curs de dezvolatare Hardware: circuite integrate pe scară ultralargă ULSI(proiectare circuite integrate 3D), arhitecturi paralele,alte soluţii arhitecturale noi (reţele neurale etc.),proiectele galiu-arsen. Software: limbaje concurente, programare funcţională,prelucrare simbolică, baze de cunoştiinţe, sisteme expertevoluate, programe de realitate virtuală, acum apar şisistemele de operare windows. Această perioadă este marcată

78

Page 77: Algoritmi

de apariţia internetului şi extinderea rapidă a acesteireţele mondiale. Memorie: de la zeci, sute de Mocteţi până la Gocteţi; Viteza: 1G de instrucţiuni /sec – 3 G de instrucţiuni/sec Comunicaţiile: au atins un nivel nemaiîntâlnit, emisiileradio de ordinul GHz, reţele globale pe fibră optică, reţelede comunicare prin satelit. Calculatoare: o gamă foarte largă de calculatoare.

79

Page 78: Algoritmi

CAPITOLUL VII

LIMBAJUL VISUAL BASIC

7.1 Programarea aplicaţiilor Windows

Pentru realizarea unei aplicaţii pot fi avute în vedere douătehnologii de programare şi anume:

– programare procedurală– programare orientată spre obiecte şi dirijată deevenimente.

În programarea procedurală, o aplicaţie este constituită din unulsau mai multe programe care se vor executa într-o anumită ordine,fiecare program fiind constituit dintr-o secvenţă de instrucţuniscrise într-un limbaj de programare.

Acesta era modul clasic de realizare a aplicaţiilor şi sistemelorinformatice şi are o serie de dezavantaje printre care: productivitatescăzută în realizarea programelor, efort mare pentru realizareaprogramelor şi mai ales a interfeţelor etc.

Apariţia tehnologiei orientate obiect, a mediilor visuale deprogramare şi a sistemului de operare Windows a condus la apariţia şidezvoltarea unei noi tehnologii de programare a aplicaţiilor windowsşi anume programarea orientată pe obiecte şi dirijată de evenimente,tehnologie ce va fi prezentată în cele ce urmează în cadrul limbajuluiVisual Basic.

O aplicaţie Windows afişează unul sau mai multe ecrane careconţin obiecte cu care va interacţiona utilizatorul pentru a controlaevoluţia programului. Într-un mediu de programare vizual, obiecteleprincipale sunt formele şi controalele desenate în forme (formă = ofereastră) Aceste obiecte pot fi create prin selecţie şi depunerefolosind barele de instrumente ale mediului respectiv.

Spre exemplu, bara cu instrumente Visual Basic permite creareaunei varietăţi de obiecte printre care: forme, butoane, casete culistă, casete derulante combinate, casete de validare, butoane radio(butoane de opţiune), etc. Fiecare din aceste obiecte are uncomportament predefinit. Spre exemplu când se execută click pe unbuton acesta trece în poziţia apăsat şi apoi revine în poziţianormală. Pentru a schimba comportamentul obiectului acestuia trebuiesă-i ataşaţi cod de program (instrucţiuni) corespunzător, cod ce se vaexecuta atunci când are loc un anumit eveniment (spre exemplu în cazulbutonului evenimentul este click).

Evenimentele se produc ca urmare a unei acţiuni a utilizatorului(ex. evenimentul click corespunde apăsării butonului stâng al mouse-

80

Page 79: Algoritmi

ului pe obiectul respectiv), sau în urma execuţiei coduluiprogramului, sau pot fi declanşate de către sistem.

Majoritatea obiectelor vor răspunde unui anumit număr deevenimente generate de către utilizator printre care click-uri, dubluclick-uri, apăsări de taste sau trageri şi eliberări ale obiectului.Limbajul Visual Basic pune la dispoziţia utilizatorului un mediu dedezvoltare care permite crearea de programe orientate spre obiecte şiconduse de evenimente. Pentru lucrul cu obiecte conduse de evenimentese parcurg următoarele etape:

– se creează o nouă formă căreia i se dă un nume;– se desenează şi se denumesc obiectele ce urmează a fiafişate în forma respectivă;– se ataşează fiecărui obiect codul ce va fi executat carăspuns la evenimente generate de utilizator sau de sistem.

Va rezulta o interfaţă grafică cu care interacţioneazăutilizatorul pentru a controla evoluţia programului. Rezumând putemspune că în programarea orientată spre obiecte şi dirijată deevenimente, obiectele au un comportament predefinit care poate fimodificat de utilizator prin ataşare de cod corespunzător şi acesteobiecte răspund la evenimente declanşate fie ca urmare a acţiuniiutilizatorului asupra obiectelor, fie ca urmare a execuţiei coduluiataşat, fie declanşate de sistem.

7.2 Proprietăţi şi metode

Un obiect este definit de un set de proprietăţi cum ar fi:dimensiune, culoare, poziţie pe ecran, comportament (ex. dacă un butonradio este activ sau nu la un moment dat etc.). O metodă este oprocedură (succesiune de instrucţiuni) asociată unei anumite acţiuni aunui obiect. Spre exemplu în Visual Basic există o metodă Moveasociată majorităţii obiectelor (permite mutarea obiectelor).

Deci proprietăţile descriu obiectele iar metodele definescacţiunile obiectelor, iar pe de altă parte proprietăţile reprezintădate iar metodele reprezintă cod (instrucţiuni). Astfel în gramaticaprogramării orientate spre obiecte :

– obiectele sunt substantive;– proprietăţile sunt adjective;– metodele sunt verbe.

Utilizarea notaţiei cu punct pentru referirea proprietăţilor şimetodelor

Referirea unei proprietăţi se face astfel:Obiect . Proprietate = Valoare

81

Page 80: Algoritmi

Exemplu - fie forma frmForma1 şi variabila dColor în care memorămculoarea de fond a formei

dColor = frmForma1.BackColor (citeşte culoarea curentă şi odepune în dColor)

frmForma1.BackColor = QBColor (Blue) – stabileşte noua culoare defond a formei la valoarea Blue.

Referirea metodelor se face asemănător cu referireaproprietăţilor, însă în plus metodele pot necesita precizarea unorinformaţii suplimentare.

Exemplu - pentru mutarea obiectului Buton1 în colţul din stângasus al formei curente se apelează metoda Move şi se precizeazăcoordonatele colţului din stânga sus:

Buton1.Move 0,0Stabilire proprietăţi şi executare metode

Proprietăţile unui obiect pot fi setate în faza de proiectare(atunci când se desenează sau se modifică formele) utilizând fişaProperties a formei sau obiectului din formă (fişa este automatvizualizată la selecţia obiectului respectiv: forma, buton, etc.). Deasemenea fişa Properties poate fi vizualizată prin click dreapta şiselecţie Properties.

Proprietăţile pot fi modificate şi prin program în momentulexecuţiei formei, dacă codul de program asociat conţine instrucţiunicare referă şi setează proprietăţi (ca în exemplul de mai sus în careschimbăm culoarea fondului formei).

Spre deosebire de proprietăţi, metodele pot fi executate numai înmomentul execuţiei programului (eventual în momentul depanăriiprogramului utilizând facilitatea Debugger a programului VisualBasic).Denumirea obiectelor

Orice obiect are proprietăţile:Name - numele utilizat în scrierea coduluiCapture - numele dat obiectului pentru a putea fi

identificat de utilizator. Visal Basic dă nume implicite obiectelor.Este indicat ca utilizatorul să dea nume obiectelor (Name) utilizândurmătoarea convenţie:

– un prefix format din 3 litere mici (ex. frm pentru formă,cmd pentru buton de comandă, etc.)– un şir de caractere care identifică obiectul (ex. Forma1,Ecran1, Buton1, etc.).

În tabelul următor sunt prezentate convenţiile de denumire aobiectelor din Visual Basic:

82

Page 81: Algoritmi

Obiect Prefix Exemplu

Formă Frm frmForma1Buton de comandă cmd, btn cmdButon, btnOKCasetă de text Txt txtCaseta1

Bare de derulare- orizontală- verticală

hsbvsb

Meniu Mnu mnuMeniuPrincCasetă de validare ChkCasetă cu lista Lst

Cadru FraImagine Img

Buton de opţiune(radio)

Opt optBO1

7.3 Instrucţiunile VBA

Generalităţi

Există trei categorii de instrucţiuni Visual Basic: instrucţiuni de declarare (prezentate la declarareavariabilelor) prin care se denumesc şi se declară tipulpentru variabile, constante şi proceduri; instrucţiuni de atribuire (prezentate în continuare) princare se atribuie valori variabilelor sau constantelor; instrucţiuni executabile (prezentate în continuare) careiniţiază acţiuni: execută metode sau proceduri, controleazăfluxul execuţiei codului.

În mediul de dezvoltare VBA, sintaxa instrucţiunilor esteverificată automat după ce se trece la instrucţiunea următoare (prinEnter).

Continuarea instrucţiunilorO instrucţiune poate să fie scrisă pe mai multe linii prin

utilizarea caracterului de continuare a liniei "_" precedat de unspaţiu. De exemplu, crearea prin program a unui tabel într-un documentWord:

ActiveDocument.Tables.Add Range:=Selection.Range, _NumRows:=3, _NumColumns:= 3

83

Page 82: Algoritmi

unde, pe lângă continuarea liniilor se va remarca utilizareaargumentelor numite la apelul metodei de adăugare a unui nou tabel lacolecţia de tabele a documentului.

Două instrucţiuni pot fi scrise pe o aceeaşi linie dacă suntseparate cu caracterul ":".

Etichetarea liniilorO linie poate fi identificată:

printr-o etichetă: orice nume, care respectă regulilegenerale, care începe în prima coloană a liniei şi se terminăcu caracterul ":" printr-un număr: orice combinaţie de cifre, care începe înprima coloană a liniei şi este unic în modulul respectiv.

Identificatorii de linii pot fi utilizaţi în instrucţiuni decontrol, desi codul astfel construit nu respectă regulile programăriistructurate.

ComentariiTextele explicative (necesare documentării codului) pot fi

introduse pe linii separate sau în continuarea liniei de cod.O linie de comentariu începe cu un apostrof (') sau cu cuvântul

Rem urmat de un spaţiu.Comentariul de pe aceeaşi linie cu o instrucţiune se introduce

printr-un apostrof urmat de comentariu.

OperatoriÎn formarea expresiilor de diverse tipuri, operatorii sunt cei

utilizaţi aproape general în limbajele de programare de nivel înalt.Pentru fixarea termenilor şi notaţiilor sunt totuşi prezentaţi, pecategorii, însoţiţi, acolo unde este cazul de scurte explicaţii.

Operatori aritmetici

Operator

Semnificaţie

Observaţii

^ Ridicareala putere

rezultatul este Double sau Variant(Double) cuexcepţia: dacă un operand este Null,rezultatul este tot Null

* Înmulţirea rezultatul este dat de cel "mai precis"factor, ordinea crescătoare a "preciziei"fiind, pentru înmulţire, Byte, Integer,Long, Single, Currency, Double şi Decimal.Dacă o expresie este Null, rezultatul este

84

Page 83: Algoritmi

Null. O expresie Empty este considerată ca0. Pentru excepţii se va studia Help –*(operator).

/ Împărţirea rezultatul este, în general, Double sauVariant(Double). Dacă o expresie este Null,rezultatul este Null. O expresie Emptyeste considerată ca 0. Pentru excepţii seva studia Help – /(operator).

\ Împărţireaîntreagă

înainte de împărţire, operanzii suntrotunjiţi la Byte, Integer sau Long.Rezultatul este Byte, Variant(Byte), Integer,Variant (Integer), Long, sau Variant(Long). Dacăo expresie este Null, rezultatul este Null.O expresie Empty este considerată ca 0.

Mod Restulîmpărţirii

operanzii sunt rotunjiţi la întregi şi seobţine restul împărţirii. Rezultatul esteByte, Variant(Byte), Integer, Variant (Integer),Long, sau Variant(Long). Dacă o expresieeste Null, rezultatul este Null. O expresieEmpty este considerată ca 0.

+ Adunareanumerică

sauconcatenar

eaşirurilor

în general, operanzi numerici producadunarea, iar operanzi şiruri producconcatenarea. În cazul numeric,rezultatul este de tipul cel "mai precis"al operanzilor, ordinea de "precizie"fiind pentru adunare şi scădere: Byte,Integer, Long, Single, Double, Currency şiDecimal. Deoarece operanzii pot fi oriceexpresie, pentru o informare completă (deexemplu operanzi Variant) se va studia Help– +(operator).

- Scădereasau

inversareasemnului

operanzii pot fi doar numerici.Rezultatul este de tipul cel "mai precis"al operanzilor, ordinea de "precizie"fiind pentru adunare şi scădere: Byte,Integer, Long, Single, Double, Currency şiDecimal. Dacă o expresie este Null,rezultatul este Null. O expresie Emptyeste considerată ca 0. Pentru excepţii se

85

Page 84: Algoritmi

va studia Help – -(operator).

Operatori de comparareRelaţiile care există între diferite tipuri de entităţi se pot

evidenţia prin comparaţii având una dintre formele următoare:result = expression1 comparisonoperator expression2result = object1 Is object2result = string Like pattern

underesult este o variabilă numericăexpression este o expresie oarecarecomparisonoperator este un operator relaţionalobject este un nume de obiectstring este o expresie şir oarecarepattern este o expresie String sau un domeniu de caractere.

Operatorii de comparare sunt cei uzuali: < (mai mic), <= (mai micsau egal), > (mai mare), >= (mai mare sau egal), = (egal), <>(diferit, neegal).

Rezultatul este True (dacă este adevărată relaţia), False (dacărelaţia este neadevărată), Null (dacă cel puţin un operand este Null).

Operatorul Is produce True dacă variabilele se referă la acelaşiobiect şi False în caz contrar.

Operatorul Like compară două şiruri cu observaţia că al doileatremen este un şablon. Prin urmare rezultatul este True dacă primulşir operand este format după şablon, False în caz contrar. Atunci cândun operand este Null, rezultatul este tot Null.

Comportarea operatorului Like depinde de instrucţiunea OptionCompare, care poate fi:

Option Compare Binary, ordinea este cea a reprezentăriiinterne binare, determinată în Windows de codul de pagină. Option Compare Text, compararea este insenzitivă lacapitalizarea textului, ordinea este determinată de setărilelocale ale sistemului.

Construcţia şablonului poate cuprinde caractere wildcard, listede caractere, domenii de caractere:

un caracter oarecare oricâte caractere (chiar nici unul) # o cifră oarecare (0–9). [charlist] oricare dintre caracterele enumerate în listă,un domeniu de litere poate fi dat prin utilizarea cratimei.

86

Page 85: Algoritmi

[!charlist] orice caracter care nu este în listăObservaţie. Pentru a utiliza în şablon caracterele speciale cu valoarede wildcard se vor utiliza construcţii de tip listă: [[], [?] etc.Paranteza dreapta va fi indicată singură: ].Pentru alte observaţii utile se va studia Help – Like operator.

Operatori de concatenarePentru combinarea şirurilor de caractere se pot utiliza

operatorii & şi +.În sintaxa

expression1 & expression2unde 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 seconvertesc la Variant(String). Expresiile Null sau Empty sunt tratateca şiruri de lungime zero ("").

87

Page 86: Algoritmi

Operatori logiciPentru operaţiile logice sunt utilizaţi următorii operatori, uzuali în

programare.Operator Semnificaţie Observaţii

And conjuncţialogică

Null cu False dă False, Null cu True sau cuNull dă Null. Operatorul And realizeazăşi operaţia de conjuncţie bit cu bitpentru expresii numerice.

Eqv echivalenţalogică

Dacă o expresie este Null, rezultatuleste Null. Eqv realizează şi comparareabit cu bit a două expresii numerice,poziţionând cifrele binare alerezultatului după regulile de calculale echivalenţei logice: 0 Eqv 0 este 1etc.

Imp implicaţialogică

True Imp Null este Null, False Imp * esteTrue, Null Imp True este True, Null Imp False(sau Null) este Null. Operatorul Imprealizează şi compararea bit cu bit adouă expresii numerice, poziţionândcifrele binare ale rezultatului dupăregulile de calcul ale implicaţieilogice: 1 Imp 0 este 0, în restrezultatul este 1.

Not negaţialogică

Not Null este Null. Prin operatorul Not sepoate inversa bit cu bit valorile uneivariabile, poziţionându-secorespunzător un rezultat numeric.

Or disjuncţialogică

Null Or True este True, Null cu False (sauNull) este Null. Operatorul Or realizeazăşi o comparaţie bit cu bit a douăexpresii numerice poziţionând biţiicorespunzători ai rezultatului dupăregulile lui Or logic.

Xor disjuncţiaexclusivă

Dacă un operand este Null, atuncirezultatul este Null. Se poate efectuaoperaţia de sau exclusiv şi bit cu bitpentru două expresii numerice[b1+b2(mod 2)].

88

Page 87: Algoritmi

Instrucţiuni de atribuireAtribuirea se poate efectua prin instrucţiunea Let (pentru valori

atribuite variabilelor şi proprietăţilor), Set (pentru atribuirea deobiecte la o variabilă de tip obiect), Lset şi Rset (pentru atribuirispeciale de şiruri sau tipuri definite de utilizator).

Instrucţiunea LetAtribuie valoarea unei expresii la o variabilă sau proprietate.

[Let] varname = expressionunde varname este nume de variabilă sau de proprietate.

Este de remarcat forma posibilă (şi de fapt general utilizată)fără cuvântul Let.Observaţii. Valoarea expresiei trebuie să fie compatibilă ca tip cuvariabila (sau proprietatea): valori numerice nu pot fi atribuitevariabilelor de tip String şi nici reciproc.

Variabilele Variant pot primi valori numerice sau String,reciproc nu este valabil decât dacă valoarea expresiei Variant poatefi interpretată compatibilă cu tipul variabilei: orice Variant poatefi atribuit unei variabile de tip String (cu excepţia Null), doarVariant care poate fi interpretat nuric poate fi atribuit uneivariabile de tip numeric.

La atribuirea valorilor numerice pot avea loc conversii la tipulnumeric al variabilei.

Atribuirea valorilor de tip utilizator poate fi efectuată doardacă ambii termeni au acelaşi tip definit. Pentru alte situaţii se vautiliza instrucţiunea Lset.

Nu se poate utiliza Let (cu sau fără cuvântul Let) pentru legareade obiecte la variabile obiect. Se va utiliza în această situaţieinstrucţiunea Set.

Instrucţiunea LSetCopie, cu aliniere la stânga, un şir de caractere (valoarea

expresiei din dreapta) într-o variabila de tip String. Deoarececopierea este binară, poate fi utilizată pentru atribuiri între tipuriutilizator diferite (rezultatul este impredictibil deoarece nu se facenici o verificare de tipuri/componente ale valorilor de tip record).Sintaxa este

LSet stringvar = stringLSet varname1 = varname2

unde stringvar, string reprezintă variabila de tip String şi expresiade acelaşi tip implicate într-o atribuire de şiruri.varname1, varname2 sunt denumiri de variabile, de tipuridefinite de utilizator (vezi instrucţiunea Type) diferite.

89

Page 88: Algoritmi

Zona de memorie alocată celei de a doua variabile estecopiată (aliniată la stânga) în zona de memorie a primeivariabile.

Caracterele care rămân neocupate se completează cu spaţii, iardacă zona de unde se copie este mai mare, caracterele din dreapta sepierd (sunt trunchiate).

Instrucţiunea LSetCopie, cu aliniere la dreapta, un şir de caractere (valoarea

expresiei din dreapta) într-o variabila de tip String. Sintaxa esteRSet stringvar = string

Caracterele rămase neocupate în variabilă sunt completate cuspaţii. Instrucţiunea RSet nu se poate utiliza (analog lui LSet)pentru tipuri definite de utilizator.

Instrucţiuni executabileExecuţia unui program are loc, în lipsa oricărui control,

instrucţiune cu instrucţiune, de la stânga la dreapta şi de sus înjos. Acest sens poate fi modificat, într-o oarecare măsură, prinordinea de precedenţă a operaţiilor în evaluarea expresiilor. Esteevident că o asemenea structură simplă nu poate cuprinde toateaspectele programării şi din acest motiv necesitatea structurilor decontrol a fluxului execuţiei. Unele instrucţiuni au fost păstrate doardin motive de compatibilitate cu versiunile iniţiale ale limbajului,în locul lor fiind preferate structuri mai evoluate sau similare altorlimbaje de programare.

Instrucţiuni de transfer (GoSub…Return, GoTo, OnError, On…GoSub, On…GoTo)

Această categorie cuprinde instrucţiunile prin care controlulexecuţiei este transferat la o altă instrucţiune din procedură. Îngeneral, utilizarea acestor comenzi nu produce programe foartestructurate (în sensul programării structurate) şi prin urmare, pentruo mai mare claritate a codului, pot fi înlocuite cu alte structuri deprogramare.

GoSub…ReturnÎn cadrul unei proceduri un grup de instrucţiuni poate fi

organizat ca o subrutină (similar unei proceduri on-line, nenumite)identificată prin linia de început. Transferul controlului la acestgrup de instrucţiuni şi revenirea la locul apelului se poate efectuaprin GoSub…Return cu sintaxa

GoSub line

90

Page 89: Algoritmi

...line...Return

unde line este o etichetă de linie sau un număr de linie din aceeaşiprocedură.

Pot exista mai multe instrucţiuni Return, prima executată producesaltul la instrucţiunea care urmează celei mai recente instrucţiuniGoSub executate.

GoToRealizează tranferul controlului execuţiei la o linie din aceeaşi

procedură.GoTo line

unde line este o etichetă de linie sau un număr de linie din aceeaşiprocedură.

On ErrorPermite controlul erorilor prin transferul controlului la rutine

de tratare. Observaţie. Este prezentată în secţiunea dedicată controluluierorilor.

On…GoSub, On…GoToPermit o ramificare multiplă, după valoarea unei expresii. Se

recomandă, pentru claritatea codului, utilizarea structurii SelectCase în locul acestor structuri.

On expression GoSub destinationlistOn expression GoTo destinationlist

undeexpression este o expresie numerică având valoare întreagă(după o eventuală rotunjire) între 0 şi 255 inclusiv.destinationlist este o listă de etichete de linii sau numere delinii, separate prin virgule (elementele pot fi de ambelecategorii), din aceeaşi procedură cu instrucţiunea.

Dacă valoarea expresiei este negativă sau mai mare decât 255 seproduce o eroare.

Dacă valoarea expresiei, fie ea k, este în domeniul rangurilorlistei, atunci se transferă controlul la linia identificată de al k-leaelement al listei.

Dacă valoarea expresiei este 0 sau mai mare decât numărul deelemente din listă, transferul se efectuează la linia care urmeazăinstrucţiunea On...GoSub sau On...GoTo.

91

Page 90: Algoritmi

Instrucţiuni de terminare sau oprire a programului (DoEvents, End,Exit, Stop)

Terminarea execuţiei programului sau oprirea temporară (pauza) sepot realiza prin instrucţiunile enumerate aici.

DoEventsDeşi nu este o instrucţiune VBA ci este o funcţie, includerea ei

este naturală prin aceea că permite cedarea controlului către sistemulde operare, care poate astfel să funcţioneze în regim de multitasking.Acţiunea poate fi realizată şi prin alte tehnici (de exempluutilizarea unui Timer etc.). Sintaxa este

DoEvents( )Funcţia returnează, în general, valoarea 0. Controlul este redat programului după ce sistemul de operare a

terminat procesarea evenimentelor din coada de evenimente, ca şiprocesarea tuturor caracterelor din coada SendKeys.Observaţie. Pentru alte observaţii se va studia documentaţia comenziiDoEvents.

EndTermină execuţia unei proceduri (sub forma prezentată aici) sau

indică sfârşitul codului unei structuri de tip bloc (cum ar fi EndFunction, End If etc., prezentate la structurile respective).

Sintaxa, în ipostaza opririi execuţiei, este:End

Prin această instrucţiune, care poate fi plasată oriunde înprogram, execuţia este terminată imediat, fără a se mai executaeventualele instrucţiuni scrise pentru tratarea unor evenimentespecifice sfârşitului de program (Unload, Terminate etc.).

Fişierele deschise prin Open sunt închise şi toate variabilelesunt eliberate. Obiectele create din modulele clasă sunt distruse, iarreferinţele din alte aplicaţii la asemenea obiecte sunt invalidate.Memoria este eliberată.

ExitPrin instrucţiunea Exit, sub una din multiplele ei forme, se

întrerupe o ramură de execuţie (cum ar fi o procedură, o structurăiterativă etc.) pentru a se continua nivelul apelant. Sintaxa este

Exit DoExit ForExit FunctionExit Property

92

Page 91: Algoritmi

Exit Subşi efectele sunt prezentate la structurile respective. Nu trebuieconfundată cu instrucţiunea End.

StopEfectul instrucţiunii este dependent de modul de execuţiei a

programului. Dacă se execută varianta compilată a programului(fişierul .exe) atunci instrucţiunea este similară instrucţiunii End(suspendă execuţia şi închide fişierele deschise). Dacă execuţia estedin mediul VBA, atunci se suspendă execuţia programului, dar nu seînchid fişierele deschise şi nu se şterge valoarea variabilelor.Execuţia poate fi reluată din punctul de suspendare.

StopInstrucţiunea 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 construcţiilor de tip bloc prezentate în aceastăsecţiune se poate repeta, în mod controlat, un grup de instrucţiuni.În cazul unui număr nedefinit de repetiţii, condiţia de oprire poatefi testată la începutul sau la sfârşitul unui ciclu, prin alegereastructurii adecvate.

Do…LoopSe vor utiliza structuri Do…Loop pentru a executa un grup de

instrucţiuni de un număr de ori nedefinit aprioric. Dacă se cunoaştenumărul de cicluri, se va utiliza structura For…Next.

Înainte de continuare se va testa o condiţie (despre care sepresupune că poate fi modificată în instrucţiunile executate).Diferitele variante posibile pentru Do…Loop diferă după momentulevaluării condiţiei şi decizia luată.

Do [{While | Until} condition][statements][Exit Do][statements]Loop

sauDo[statements][Exit Do][statements]Loop [{While | Until} condition]

unde

93

Page 92: Algoritmi

condition este o expresie care valoare de adevăr True sauFalse. O condiţie care este Null se consideră False.statements sunt instrucţiounile care se repetă atâta timp(while) sau până când (until) condiţia devine True.

Dacă decizia este de a nu continua ciclarea, atunci se va executaprima instrucţiune care urmează întregii structuri (deci de după liniacare începe cu Loop).

Se poate abandona ciclarea oriunde în corpul structurii prinutilizarea comenzii Exit Do (cu această sintaxă). Dacă apare o comandăExit Do se poate omite chiar şi condiţia din enunţ întrucât execuţiase va termina prin această decizie.

Structurile Do pot fi inserate (dar complet) unele în altele. Oterminare (prin orice metodă) a unei bucle transferă controlul lanivelul Do imediat superior.Execuţia structurilor este explicată în tabelul următor

Do While…Loop Testează condiţia la începutulbuclei, execută bucla numai dacărezultatul este True şi continuăastfel până când o nouă evaluareproduce False.

Do Until…Loop Testează condiţia la începutulbuclei, execută bucla numai dacărezultatul este False şi continuăastfel până când o nouă evaluareproduce True.

Do…Loop While Se execută întotdeauna bucla odată, se testează condiţia lasfârşitul buclei şi se repetăbucla atât timp cât condiţia esteTrue. Oprirea este pe condiţiefalsă.

Do…Loop Until Se execută întotdeauna bucla odată, se testează condiţia lasfârşitul buclei şi se repetăbucla atât timp cât condiţia esteFalse. Oprirea este pe condiţieadevărată.

 

For…Next

94

Page 93: Algoritmi

Atunci când se cunoaşte numărul de repetări ale unui bloc deinstrucţiuni, se va folosi structura For…Next. Structura utilizează ovariabilă contor, a cărei valoare se modifică la fiecare ciclu,oprirea fiind atunci când se atinge o valoare specificată. Sintaxaeste:

For counter = start To end [Step step][statements][Exit For][statements]Next [counter]

undecounter este variabila contor (numără repetările), de tipnumeric. Nu poate fi de tip Boolean sau element de tablou.start este valoarea iniţială 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 şinegativă. statements sunt instrucţiunile care se repetă. Dacă nu sespecifică, atunci singura acţiune este cea de modificare acontorului de un număr specificat de ori.

Acţiunea este dictată de pasul de incrementare şi relaţia dintrevaloarea iniţială şi cea finală.

Instrucţiunile din corpul structurii se execută dacăcounter <= end pentru step >= 0 sau counter >= end pentru step < 0.

După ce toate instrucţiunile s-au executat, valoarea step esteadăugată la valoarea contorului şi instrucţiunile se execută din noudupă acelaşi test ca şi prima dată, sau bucla For…Next este terminatăşi se execută prima instrucţiune de după linia Next.

Specificarea numelui contorului în linia Next poate clarificatextul sursă, mai ales în cazul când există structuri For…Nextîmbricate.

Corpul unei bucle For…Next poate include (complet) o altăstructură For…Next. În asemenea situaţii, structurile îmbricatetrebuie să aibă variabile contor diferite.Instrucţiunile Exit For pot fi plasate oriunde în corpul unei bucle şiprovoacă abandonarea ciclării. Controlul execuţiei se transferă laprima instrucţiune de după linia Next.

For Each…NextSimilară structurii For…Next, structura For Each…Next repetă un

grup de instrucţiuni pentru fiecare element dintr-o colecţie de

95

Page 94: Algoritmi

obiecte sau dintr-un tablou (cu excepţia celor de un tip utilizator).Este utilă atunci când nu se cunoaşte numărul de elemente sau dacă semodifică, în timpul execuţiei, conţinutul colecţiei.

Sintaxa este:For Each element In group[statements][Exit For][statements]Next [element]

undeelement este variabila utilizată pentru parcurgereaelementelor. Dacă se parcurge o colecţie de obiecte, atuncielement poate fi Variant, o variabilă generică de tip Object,sau o variabilă obiect specifică pentru biblioteca de obiectereferită. Pentru parcurgerea unui tablou, element poate fidoar o variabilă de tip Variant.group este numele colecţiei de obiecte sau al tabloului.statements este grupul de istrucţiuni executate pentru fiecareelement.

Execuţia unei structuri For Each…Next este: Se defineşte element ca numind primul element din grup (dacănu există nici un element, se transferă controlul la primainstrucţiune de după Next – se părăseşte bucla fărăexecutarea instrucţiunilor). Se execută instrucţiunile din corpul buclei For. Se testează dacă element este ultimul element din grup. Dacărăspunsul este afirmatif, se părăseşte bucla. Se defineşte element ca numind următorul element din grup. Se repetă paşii 2 până la 4.

Instrucţiunile Exit For sunt explicate la For…Next. Buclele ForEach...Next pot fi îmbricate cu condiţia ca elementele

utilizate la iterare să fie diferite. Observaţie. Pentru ştergerea tuturor obiectelor dintr-o colecţie se vautiliza For…Next şi nu For Each…Next. Se va utiliza ca număr deobiecte colecţie.Count.

While…WendExecută un grup de instrucţiuni atât timp cât este adevărată o

condiţie. SintaxaWhile condition[statements]Wend

96

Page 95: Algoritmi

Este recomandat să se utilizeze o structură Do…Loop în loculacestei structuri.

WithProgramarea orientată pe obiecte produce, datorită calificărilor

succesive, construcţii foarte complexe atunci când se numescproprietăţile unui obiect. În cazul modificărilor succesive ale maimultor proprietăţi ale aceluiaşi obiect, repetarea zonei de calificarepoate produce erori de scriere şi conduce la un text greu de citit.Codul este simplificat prin utilizarea structurii With…End With. Oasemenea structură execută o serie de instrucţiuni pentru un obiectsau pentru o variabilă de tip utilizator. Sintaxa este:

With object[statements]End With

undeobject este numele unui obiect sau a unui tip definit deutilizatorstatements sunt instrucţiunile care se execută pentruentitatea precizată.

Permiţând omiterea recalificărilor din referinţele la obiectulprecizat, orice construcţie de tipul ".nume" este interpretată îninstrucţiunile 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 calificările eventuale laacest obiect vor fi efectuate.

Nu se recomandă saltul în şi dintr-un bloc With. Structuri de decizie (If…Then…Else, Select Case)Ramificarea firului execuţiei după rezultatul verificării unei

condiţii este o necesitate frecventă în orice implementare. Pe lângă structurile prezentate, se pot utiliza trei funcţii care

realizează alegeri în mod liniarizat (pe o linie de cod): Choose(),Iif(), Switch().

If…Then…ElseO asemenea structură, întâlnită de altfel în toate limbajele de

programare, execută un grup de instrucţiuni ca răspuns la îndeplinireaunei condiţii (compusă sau nu din mai multe condiţii testatesecvenţial). Sintaxa permite o mare varietate de forme:

If condition Then [statements] [Else elsestatements]sau

If condition Then

97

Page 96: Algoritmi

[statements][ElseIf condition-n Then[elseifstatements] ...[Else[elsestatements]]End If

undecondition are una din formele: expresie numerică sau şir carese poate evalua True sau False (Null este interpretat False);expresie de forma TypeOf objectname Is objecttype, evaluată Truedacă objectname este de tipul obiect specificat înobjecttype.statements, elsestatements, elseifstatements sunt blocurile deinstrucţiuni executate atunci când condiţiile corespunzătoaresunt True.

La utilizarea primei forme, fără clauza Else, este posibil să sescrie mai multe instrucţiuni, separate de ":", pe aceeaşi linie.

Verificarea condiţiilor implică evaluarea tuturor subexpresiilor,chiar dacă prin jocul operanzilor şi operatorilor rezultatul poate fiprecizat mai înainte (de exemplu OR cu primul operand True).

Select CaseInstrucţiunea Select Case se poate utiliza în locul unor

instrucţiuni ElseIf multiple (dintr-o structură If…Then…ElseIf) atuncicând se compară aceeaşi expresie cu mai multe valori, diferite întreele. Instrucţiunea Select Case furnizează, prin urmare, un sistem deluare a deciziilor similar instrucţiunii If…Then…ElseIf. Totuşi,Select Case produce un un cod mai eficient şi mai inteligibil. Sintaxaeste:

Select Case testexpression[Case expressionlist-n[statements-n]] ...[Case Else[elsestatements]]End Select

undetestexpression este o expresie numerică sau şir.expressionlist-n este lista, separată prin virgule, a uneia saumai multe expresii de forma:expression. expression To expression. Cuvântul To introduce un intervalde valori, valoarea minimă fiind prima specificată.

98

Page 97: Algoritmi

Is comparisonoperator expression. Se va utiliza Is cuoperatori de comparare (exceptând Is şi Like) pentru aspecifica un domeniu de valori. statements-n reprezintă una sau mai multe instrucţiuni care sevor executa dacă testexpression este egală cu un element dinexpressionlist-n.elsestatements reprezintă una sau mai multe instrucţiuni care sevor executa dacă testexpression nu este egală cu nici un elementdin listele liniilor Case.

Dacă testexpression se potriveşte cu un element dintr-o listăCase, se vor executa instrucţiunile care urmează această clauză Casepână la următoarea clauză Case, sau până la End Select. Controlexecuţiei trece apoi la instrucţiunea care urmează liniei finale EndSelect. Rezultă că dacă testexpression se regăseşte în mai multeliste, doar prima potrivire este considerată.

Clauza Case Else are semnificaţia uzuală "altfel, în rest, în cazcontrar etc.", adică introduce instrucţiunile care se execută atuncicând expresia de test nu se potriveşte nici unui element din listeleclauzelor Else. Dacă aceasta este situaţia şi nu este specificată oclauză Case Else, atunci execuţia urmează cu prima instrucţiune dedupă End Select.

Instrucţiunile Select Case pot fi scufundate unele în altele,structurile interioare fiind complete (fiecare structură are EndSelect propriu, includerea este completă).

Apeluri de proceduri şi programeÎn această secţiune se prezintă doar funcţia Shell(), deoarece

despre proceduri şi apelul lor s-a discutat în capitolul 1.

Funcţia Shell()Execută un program executabil şi returnează un Variant(Double)

reprezentând ID-ul de task al programului în caz de succes; în cazcontrar returnează zero. Sintaxa este

Shell(pathname[,windowstyle])unde

pathname este Variant (String). Conţine numele programuluicare se execută, argumentele necesare şi poate da caleacompletă (dacă este nevoie).windowstyle este Variant (Integer) şi precizează stilulferestrei în care se va executa programul (implicit esteminimizat, cu focus).

Valorile posibile pentru argumentul windowstyle suntConstanta Valoar Semnificaţia

99

Page 98: Algoritmi

numită eaVbHide 0 Fereastra este ascunsă iar focus-ul este pe

fereastra ascunsă.VbNormalFocus 1 Fereastra are focus-ul şi este dimensionată şi

poziţionată normal.VbMinimizedFocu

s2 Fereastra este afişată ca o icoană (minimizată)

dar are focus-ul.VbMaximizedFocu

s3 Fereastră maximizată, cu focus.

VbNormalNoFocus 4 Fereastra este normală (restaurată la mărimea şipoziţia cea mai recentă) dar nu are focus-ul.

Fereastra activă curentă îşi păstrează focus-ul.VbMinimizedNoFo

cus6 Fereastră minimizată, fără focus. Fereastra

activă curentă îşi păstrează focus-ul.

Dacă funcţia Shell nu poate porni programul specificat se vasemnala eroare. Programul pornit prin Shell se execută asincron, decinu există certitudinea că acest program se termină înainte de execuţiainstrucţiunilor care urmează liniei Shell.

100

Page 99: Algoritmi

CAPITOLUL VIII

REGULI IMPORTANTE PRIVIND ALEGEREA UNUI LIMBAJ DE PROGRAMARE

Prezentăm în continuare mai multe reguli importante, majoritateadintre ele prezente şi explicate în secţiunile anterioare.

1. Defineşte complet problema.

Această indicaţie, foarte importantă în activitatea deprogramare, pare fără sens pentru unii cititori. Dar nu se poaterezolva o problemă dacă nu se cunoaşte această problemă. Specificareacorectă şi completă a problemei nu este o sarcină trivială, ci unafoarte importantă şi adeseori chiar dificilă. Programul trebuie sărespecte această specificaţie, să fie construit având tot timpul înfaţă această specificaţie, să i se demonstreze corectitudinea înraport cu această specificaţie, să fie testat şi validat ţinând seamade această specificaţie.

2. Gândeşte mai întâi, programează pe urmă.

Începând cu scrierea specificaţiilor problemei, trebuie pusă înprim plan gândirea. Este specificaţia problemei corectă? Întremetodele de rezolvare posibile, care ar fi cea mai potrivită scopuluiurmărit? În paralel cu proiectarea algoritmului demonstreazăcorectitudinea lui. Verifică corectitudinea fiecărui pas înainte de amerge mai departe.

3. Nu folosi variabile neiniţializate.

Este vorba de prezenţa unei variabile într-o expresie fără ca înprealabil această variabilă să fi primit valoare. Este o eroare foartefrecventă a programatorilor începători (dar nu numai a lor). Destulecompilatoare permit folosirea variabilelor neiniţializate,neverificând dacă o variabilă a fost iniţializată înaintea folosiriiei. Alte compilatoare iniţializează automat variabilele numerice cuvaloarea zero. Cu toate acestea nu e bine să ne bazăm pe o asemeneainiţializare ci să atribuim singuri valorile iniţiale corespunzătoarevariabilelor. Programul realizat trebuie să fie portabil, să nu sebazeze pe specificul unui anumit compilator.

4. Verifică valoarea variabilei imediat după obţinerea acesteia.

101

Page 100: Algoritmi

Dacă o variabilă întreagă trebuie să ia valori într-un subdomeniuc1..c2 verifică respectarea acestei proprietăţi. Orice încălcare a eiindică o eroare care trebuie înlăturată. Valoarea variabilei poate ficalculată sau introdusă de utilizator. În primul caz, verificareatrebuie făcută după calcul, în al doilea caz se recomandă caverificarea să urmeze imediat după citirea valorii respectiveivariabile.

5. Cunoaşte şi foloseşte metodele de programare.

Este vorba de programarea Top-Down, Rafinarea în paşi 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, reducând costulrealizării programelor. De asemenea, folosirea unor componenteexistente (deci testate) măreşte gradul de fiabilitate a produselorsoft realizate şi scurtează perioada de realizare a acestora. Evident,dacă o parte din SUBPROGRAMii necesari programului sunt deja scrişi şiverificaţi, viteza de lucru va creşte prin folosirea lor. Foloseştedeci bibliotecile de componente reutilizabile existente şiconstruieşte singur astfel de biblioteci, care să înglobeze experienţaproprie.

O bună programare modulară elimină legăturile între două moduleprin variabile globale. Se recomandă ca fiecare modul să realizeze oactivitate bine definită şi independentă de alt modul. Comunicareaîntre două module trebuie să se realizeze numai prin mecanismulparametrilor formali-actuali.

6. Amână pe mai târziu detaliile nesemnificative.

Această regulă stabileşte priorităţile de realizare acomponentelor unui program; în primul rând se acordă atenţieaspectelor esenţiale, începând cu modulul principal. În fiecare fazădă atenţie lucrurilor importante. De exemplu, este inutil să se piardătimp cu scrierea unor părţi de program pentru tipărirea rezultatelorşi a constata ulterior că rezultatele nu sunt cele dorite, sau nu suntcorecte.

Nu uita însă că pentru beneficiar "Detaliile nesemnificative suntsemnificative". Beneficiarii ţin foarte mult la forma rezultatelor şi,adeseori, judecă programatorii după această formă. E păcat de muncadepusă dacă tipărirea rezultatelor lasă o impresie proastă asuprabeneficiarului.

102

Page 101: Algoritmi

7. Evită artificiile.

Prin folosirea artificiilor în programare, a prescurtărilor şisimplificărilor se pierde adesea din claritatea programului şi, multmai grav, uneori se ajunge chiar la introducerea unor erori. În plusse poate pierde portabilitatea programului.

Există însă situaţii în care prin anumite artificii se câştigăeficienţă în execuţie sau se face economie de memorie. Dacă acest fapteste important atunci artificiile sunt binevenite, în caz contrar nuse recomandă folosirea lor.

8. Foloseşte constante simbolice.

Folosirea intensivă a constantelor simbolice este recomandatăoriunde în textul sursă trebuie scris un număr (la declarareatablourilor, la precizarea limitelor de variaţie a unor variabile,etc.). Prin utilizarea acestor constante se măreşte gradul degeneralitate a textului scris, iar în situaţia în care valoarea uneiconstante trebuie schimbată, modificarea este mult mai uşoară (doar lalocul definiţiei constantei) şi nu duce la erori. Ea implică numaidefiniţia constantei, nu modificarea valorii concrete în toateinstrucţiunile programului.

9. Verifică corectitudinea algoritmului şi programului în fiecareetapă a elaborării lor.

Detectarea şi eliminarea unei erori imediat după comiterea eiduce la creşterea vitezei de realizare a produsului, evitându-seactivităţi inutile de depanare. Se recomandă demonstrareacorectitudinii fiecărui algoritm folosit, întrucât erorile semnalateîn timpul testării sunt adeseori greu de descoperit şi, câteodată,imposibil de eliminat altfel decât prin rescrierea modulului sauprogramului respectiv. Urmează testarea fiecărui subprogram imediatdupă ce a fost scris (codificat). Acest lucru se potriveştecodificării bottom-up şi sugerează o abordare sistematică aactivităţii de codificare. Dacă pentru proiectare se pot folosioricare dintre metodele indicate, în codificare (şi testarea aferentăcodificării), abordarea de jos în sus este esenţială. Sugerăm caaceastă testare să se facă independent de programul în care se vafolosi subprogramul testat. Este adevărat că activitatea de testarenecesită un anumit timp, dar ea este utilă cel puţin din trei punctede vedere:

103

Page 102: Algoritmi

– scoate în evidenţă erorile provocate de proiectareaalgoritmului sau codificarea neadecvată a acestuia;– facilitează detectarea erorilor, deoarece dimensiuneaproblemei este mai mică; în fapt nu se pierde timp cuscrierea unui program de test, ci se câştigă timp, deoarecela fiecare nivel de detaliere se vor folosi numai componentetestate deja; ceea ce rămâne de testat la nivelul respectiveste gestiunea corectă a apelurilor respectivelor componente;– obligă implementatorul să gândească încă o utilizare (celpuţin) a respectivului subprogram, independentă de cea pentrucare a fost iniţial conceput.

10. Foloseşte denumiri sugestive pentru identificatorii utilizaţi înprogram.

Fiecare identificator (nume de variabilă, de tip de date, deconstante, de subprograme) îşi are rolul şi semnificaţia lui într-unprogram. E bine ca denumirea să reflecte această semnificaţie, mărindastfel claritatea textului programului.

Unii programatori exagerează însă, folosind identificatori lungi,obţinuţi prin concatenarea mai multor cuvinte. E clar că denumireaalesă redă semnificaţia variabilei, dar claritatea textului scade,lungimea programului creşte şi citirea lui devine greoaie.

11. Cunoaşte şi respectă semnificaţia fiecărei variabile.

Fiecare variabilă are o semnificaţie. În demonstrareacorectitudinii algoritmului această semnificaţie se reflectă, de celemai multe ori, printr-un predicat invariant. O greşeală frecventăfăcută de unii programatori constă în folosirea unei variabile în maimulte scopuri.

12. Foloseşte variabile auxiliare numai acolo unde este strictnecesar.

Fiecare variabilă trebuie să aibă o semnificaţie proprie, iar îndemonstrarea corectitudinii programului, acesteia i se ataşează uninvariant, care trebuie verificat.

Folosirea necontrolată a mai multor variabile auxiliare, rupereaunor expresii chiar lungi în subexpresii cu diferite denumiri, potduce la reducerea clarităţii programului.

13. Prin scriere redă cât mai fidel structura programului.

104

Page 103: Algoritmi

Importanţa indentării şi spaţierii pentru claritatea programuluiau fost arătate anterior. Fiecare programator trebuie să aibăpropriile reguli de scriere, care să scoată cât mai bine în evidenţăstructura programului şi funcţiile fiecărei părţi a acestuia.

14. Nu uita să testezi programul chiar dacă ai demonstratcorectitudinea lui.

Sunt cunoscute demonstraţii greşite pentru unele teoreme celebredin matematică. Şi o demonstraţie a corectitudinii unui program poatefi greşită. Dar, chiar dacă demonstrarea corectitudinii algoritmuluieste validă, programul poate conţine greşeli de codificare, deintroducere (tastare) sau pot fi alte cauze care generează erori.

15. Nu recalcula limitele şi nu modifica variabila de ciclare îninteriorul unei structuri repetitive dată prin propoziţia PseudocodPENTRU.

O astfel de practică poate duce la erori greu de detectat şiîncalcă regulile programării structurate. Atunci când este necesară schimbarea variabilei de ciclare sau alimitelor se recomandă folosirea uneia din structurile repetitiveREPETĂ sau CÂTTIMP.

16. Nu ieşi forţat din corpul unei structuri repetitive redată prinpropoziţia Pseudocod PENTRU.

Instrucţiunea Pseudocod PENTRU corespunde unui număr cunoscut deexecuţii ale corpului ciclului. În situaţia când corpul conţine şitestarea condiţiei de continuare a ciclării, recomandăm a se folosistructurile REPETĂ sau CÂTTIMP şi nu PENTRU.

17. Elaborează documentaţia programului în paralel cu realizarea lui.

Aşa cum s-a arătat în mai multe locuri din acest material, pedurata de viaţă a unui program se iau mai multe decizii. E bine caaceste decizii să rămână consemnate împreună cu rezultatul final alfiecărei faze din viaţa programului (specificarea problemei,proiectarea algoritmilor, programul propriu-zis, datele de testfolosite). Vor rezulta documentaţii de analiză, proiectare,implementare şi exploatare. Primele trei sunt necesare la întreţinerea

105

Page 104: Algoritmi

aplicaţiei, trebuind a fi actualizate ori de câte ori se producmodificări, iar ultima este necesară celor care exploatează aplicaţia.Pe lângă acestea, un program bun va trebui să posede şi o componentăde asistenţă on-line (funcţie help), care contribuie la asigurarea aceea ce am numit interfaţă prietenoasă.

18. Foloseşte comentariile.

Rolul comentariilor a fost explicat în secţiunea 4.4. Este foartegreu să descifrăm un program lipsit de comentarii, chiar dacă estevorba de propriu; program scris în urmă cu câteva luni sau ani dezile. Orice program sau modul trebuie să fie însoţit de comentariiexplicative dacă dorim să-l refolosim şi nu trebuie să scriem programecare să nu poată fi refolosite. Minimum de comentarii într-un modultrebuie să conţină specificarea acestui modul şi semnificaţia fiecăreivariabile.

106

Page 105: Algoritmi

CAPITOLUL IX

METODA BACKTRACKING

La dispoziţia celor care rezolvă probleme cu ajutorulcalculatorului există mai multe metode. Dintre acestea cel mai desutilizate sunt:

– metoda Greedy;– metoda Divide et impera;– metoda Branch and Bound;– metoda Backtracking;

Metoda backtracking se aplică problemelor în care soluţia poatefi reprezentată sub forma unui vector – x = (x1, x2, x3, …xk,… xn) €S, unde S este mulţimea soluţiilor problemei şi S = S1 x S2 x… x Sn,şi Si sunt mulţimi finite având s elemente si xi € si , (¥)i = 1..n.

Pentru fiecare problemă se dau relaţii între componentelevectorului x, care sunt numite condiţii interne; soluţiile posibilecare satisfac condiţiile interne se numesc soluţii rezultat. Metoda degenerare a tuturor soluţiilor posibile si apoi de determinare asoluţiilor rezultat prin verificarea îndeplinirii condiţiilor internenecesită foarte mult timp.

Metoda backtracking evită această generare şi este mai eficientă.Elementele vectorului x, primesc pe rând valori în ordinea crescătoarea indicilor, x[k] va primi o valoare numai daca au fost atribuitevalori elementelor x1.. x[k-1]. La atribuirea valorii lui x[k] severifica îndeplinirea unor condiţii de continuare referitoare la x1…x[k-1]. Daca aceste condiţii nu sunt îndeplinite, la pasul k, acestlucru înseamnă ca orice valori i-am atribui lui x[k+1], x[k+1], ..x[n] nu se va ajunge la o soluţie rezultat.

Metoda backtracking construieşte un vector soluţie în modprogresiv începând cu prima componentă a vectorului şi mergând spreultima 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 x1…x[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ă îndeplinireacondiţiilor de continuare.Pot apărea următoarele situaţii :

a) x[k] îndeplineşte condiţiile de continuare. Daca s-a ajunsla soluţia finală (k = n) atunci se afişează soluţiaobţinută. Daca nu s-a ajuns la soluţia finală se trece lagenerarea elementului următor – x [k-1];

107

Page 106: Algoritmi

b) x[k] nu îndeplineşte condiţiile de continuare. Se încearcăurmătoarea valoare disponibila din S[k]. Daca nu se găseştenici o valoare în S[k] care să îndeplinească condiţiile decontinuare, se revine la elementul x[k-1] şi se reiaalgoritmul pentru o nouă valoare a acestuia. Algoritmul seîncheie când au fost luate in considerare toate elementelelui S1.

Problemele rezolvate prin această metodă necesită timp mare deexecuţie, de aceea este indicat sa se folosească metoda numai daca nuavem alt algoritm de rezolvare.

Dacă mulţimile S1,S2,…Sn au acelaşi număr k de elemente, timpulnecesar de execuţie al algoritmului este k la n. Dacă mulţimile S1,S2.. Sn nu au acelaşi număr de elemente, atunci se notează cu „m”minimul cardinalelor mulţimilor S1…Sn si cu „M”, maximul. Timpul deexecuţie este situat în intervalul [m la n .. M la n]. Metodabacktracking are complexitatea exponenţială, in cele mai multe cazurifiind ineficientă. Ea insa nu poate fi înlocuită cu alte variante derezolvare mai rapide în situaţia în care se cere determinarea tuturorsoluţiilor unei probleme.

Generarea permutărilor. Se citeşte un număr natural n. Să segenereze toate permutările mulţimii {1, 2, 3, …,n}.

Generarea permutărilor se va face ţinând cont că orice permutareva fi alcătuită din elemente distincte ale mulţimii A. Din acestmotiv, la generarea unei permutări, vom urmări ca numerele să fiedistincte.

Prezentăm algoritmul corespunzător cazului n=3:

1 2 31 2 2 2 2

1 1 1 1 1 1

1 2 33 3 3 3 11 1 1 1 2 2

1 2 3 11 1 1 2 3 32 2 2 2 2 2

se încarcă în stivă pe nivelul 1 valoarea 1; încărcarea valorii 1 pe nivelul al 2-lea nu este posibilă,întrucât această valoare se găseşte şi pe nivelul 1 alstivei;

108

Page 107: Algoritmi

încărcarea valorii 2 pe nivelul al 2-lea este posibilă,deoarece această valoare nu mai este întâlnită; valoarea 1 din nivelul al 3-lea se regăseşte pe nivelul 1; valoarea 2 din nivelul al 3-lea se regăseşte pe nivelul al2-lea; valoarea 3 pe nivelul al 3-lea nu e întâlnită pe nivelurileanterioare; întrucât nivelul 3 este completat corect.Tipărim: 1 2 3……Algoritmul continuă până când stiva devine vidă.

Programul sursa este urmatorul:

Private Sub CommandButton14_Click() cit_n "n = ", n back_permEnd Sub

Sub back_perm() Dim k As Integer k = 1 While k > 0 Do succesor am_suc, st, k If am_suc = True Then valid1 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 WendEnd 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

109

Page 108: Algoritmi

NextEnd 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 bEnd 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 IfEnd Sub

Stiva este acea formă de organizare a datelor (structură de date) cu proprietateacă operaţiile de introducere şi scoatere a datelor se fac în vârful ei.

Stivele se pot simula utilizând vectori.Fie ST(i) un vector. ST(1), ST(2), ..., ST(n) pot reţine numai

litere sau numai cifre. O variabilă K indică în permanentă vârfulstivei.

Exemplificăm, în continuare, modul de lucru cu stiva:

În stiva iniţial vidă se introduce litera A, vârful stivei vafi la nivelul 1 (k-1);

introducem în stivă litera B, deci k va lua valoarea 2;

scoatem din stivă pe B (A nu poate fi scos);

scoatem din stivă pe A; stiva rămâne vidă

În mod practic la scoaterea unei variabile din stivă, scade cu 1valoarea variabilei ce indică vârful stivei, iar atunci când scriemceva în stivă, o eventuală valoare reziduală se pierde:

A

BA

A

110

Page 109: Algoritmi

Pe un anumit nivel se retine, de regulă, o singură informaţie(literă sau cifră), însă este posibil; aşa cum va rezulta dinexemplele, prezentate în lucrare, să avem mai multe informaţii, caz încare avem de a face cu stive duble, triple, etc.

Întreaga teorie a recursivităţii se bazează pe structura de tipstivă.

Prezentarea tehnicii BacktrackingAceastă tehnică se foloseşte în rezolvarea problemelor care

îndeplinesc simultan următoarele condiţii:– soluţia lor poate fi pusă sub forma unui vectorS=x1,x2, ...,xn, cu x1 € A1, x2 € A2 …, xn € An

– mulţimile A1, A2 , …., An sunt mulţimi finite, iar elementelelor se consideră că se află într-o relaţie de ordine binestabilită;– nu se dispune de o altă metodă de rezolvare, mai rapidă – x1 x2 …, xn pot fi la rândul lor vectori;– A1, A2 …, An pot coincide.

La întâlnirea unei astfel de probleme, dacă nu cunoaştem aceastătehnică, suntem tentaţi să generăm toate elementele produsuluicartezian A1,A2 …,An si fiecare element să fie testat dacă este soluţie.Rezolvând problema în acest mod, timpul de execuţie este atât de mare,încât poate fi considerat infinit, algoritmul neavând nici o valoarepractică.

De exemplu, dacă dorim să generăm toate permutările unei mulţimifinite A, nu are rost să generăm produsul cartezian AxAx.....xA,pentru ca apoi, să testăm, pentru fiecare element al acestuia, dacăeste sau nu permutare (nu are rost să generăm 1.1,1.......1, pentru caapoi să constatăm că nu am obţinut o permutare, când de la a douacifră 1 ne puteam da seama că cifrele nu sunt distincte).

Tehnica Backtracking are la bază un principiu extrem de simplu:– se construieşte soluţia pas cu pas: x1, x2 …,xn

– dacă se constată că, pentru o valoare aleasă, nu avem cumsă ajungem la soluţie, se renunţă la acea valoare şi se reiacăutarea din punctul în care am rămas.

Concret:– se alege primul element x, ce aparţine lui A;– presupunând generate elementele x1,x2 …,xk , aparţinândmulţimilor A1, A2 …,Ak, se alege (dacă există) xk+1, primulelement disponibil din mulţimea Ak+1, apar două posibilităţi: 1) Nu s-a găsit un astfel de element, caz în care caz în carese reia căutarea considerând generate elementele x1,x2 …,xk+1

111

Page 110: Algoritmi

, iar aceasta se reia de la următorul element al mulţimii Ak

rămas netestat;2) A fost găsit, caz în care se testează dacă acestaîndeplineşte anumite condiţii de continuare apărând astfeldouă posibilităţi: îndeplineşte, caz în care se testează dacă s-a ajuns lasoluţie si apar din nou două posibilităţi:- s-a ajuns la soluţie, se tipăreşte soluţia si se reiaalgoritmul considerând generate elementele x1,x2 …,xk, (secaută în continuare, un alt element al mulţimii Ak, rămasnetestat);- nu s-a ajuns la soluţie, caz în care se reia algoritmulconsiderând generate elementele x1,x2 …,xk , si se caută unprim element xk+2 € Ak.

nu le îndeplineşte caz în care se reia algoritmulconsiderând generate elementele x1,x2 …, xk , iar elementulxk-1 se caută între elementele mulţimii A, rămase netestate.

Algoritmii se termină atunci când nu există nici un element x1 € A1

netestat.Observaţie: tehnica Backtracking are ca rezultat obţinerea tuturorsoluţiilor problemei. În cazul în care se cere o sigură soluţie sepoate forţa oprirea, atunci când a fost găsită.

Am arătat că orice soluţie se generează sub formă de vector. Vomconsidera că generarea soluţiilor se face intr-o stivă. Astfel, x1 € A1,se va găsi pe primul nivel al stivei, x2 € A2 se va găsi pe al doileanivel al stivei,... xk € Ak se va găsi pe nivelul k al stivei. În acestfel, stiva (notată ST) va arăta astfel:

ST

.

.

.

xk

x2

x1

112

Page 111: Algoritmi

Nivelul k+1 al stivei trebuie iniţializat (pentru a alege, înordine, elementele mulţimii k+1 ). Iniţializarea trebuie făcută cu ovaloare aflată (în relaţia de ordine considerată, pentru mulţimea Ak+1 )înaintea tuturor valorilor posibile din mulţime. De exemplu, pentrugenerarea permutărilor mulţimii {1,2.....n}, orice nivel al stivei valua valori de la 1 la n. Iniţializarea unui nivel (oarecare) se facecu valoarea 0. Procedura de iniţializare o vom numi INIT şi va aveadoi parametri: k (nivelul care trebuie iniţializat si ST (stiva)).

Găsirea următorului element al mulţimii Ak (element care a fostnetestat) se face cu ajutorul procedurii SUCCESOR (AS,ST,K).Parametrul AS (am succesor) este o variabilă booleană. În situaţia încare am găsit elementul, acesta este pus în stivă şi AS ia valoareaTRUE, contrar (nu a rămas un element netestat) AS ia valoarea FALSE..

Odată ales un element, trebuie văzut dacă acesta îndeplineştecondiţiile de continuare (altfel spus, dacă elementul este valid).Acest test se face cu ajutorul procedurii VALID (EV,ST,K).

Testul dacă s-a ajuns sau nu la soluţia finală se face cuajutorul funcţiei SOLUTIE(k) iar o soluţie se tipăreşte cu ajutorulprocedurii TIPAR. Prezentăm în continuare rutina Backtracking:

k:=1; CALL init(1,st);while k>0 do CALL succesor (as, st, k) ; if as then CALLvalid(ev,st,k) then loop until (not as) or (as and ev) ; if as then if solutie(k) then CALL tipar else k:=k+l; CALL init ( k, st ); end; else k:=k-1 wend

Observaţie: Problemele rezolvate prin această metodă necesită un timpîndelungat. Din acest motiv, este bine să utilizăm metoda numai atuncicând nu avem la dispoziţie un alt algoritm mai eficient. Cu toate căexistă probleme pentru care nu se pot elabora alţi algoritmi maieficienţi, tehnica backtracking trebuie aplicată numai în ultimăinstanţă.

113

Page 112: Algoritmi

Fiind dată o tablă de şah, de dimensiune n, xn, se cer toatesoluţiile de aranjare a n dame, astfel încât să nu se afle două damepe aceeaşi linie, coloană sau diagonală (dame să nu se atacereciproc).

Exemplu: Presupunând că dispunem de o tablă de dimensiune 4x4, osoluţie ar fi următoarea:

Observăm că o damă trebuie să fie plasată singură pe linie. Plasămprima damă pe linia 1, coloana 1.

A doua damă nu poate fi aşezată decât în coloana 3.

Observăm că a treia damă nu poate fi plasată în linia 3. Încercămatunci plasarea celei de-a doua dame în coloana 4.

A treia damă nu poate fi plasată decât în coloana 2.

DD

DD

D

DD

DD

DD

D114

Page 113: Algoritmi

În această situaţie dama a patra nu mai poate fi aşezată.Încercând să avansăm cu dama a treia, observăm că nu este posibil

să o plasăm nici în coloana 3, nici în coloana 4, deci o vom scoate depe tablă. Dama a doua nu mai poate avansa, deci şi ea este scoasă depe tablă. Avansăm cu prima damă în coloana 2.

A doua damă nu poate fi aşezată decât în coloana 4.

Dama a treia se aşează în prima coloană.

Acum este posibil să plasăm a patra damă în coloana 3 si astfelam obţinut o soluţie a problemei.

Algoritmul continuă în acest mod până când trebuie scoasă de petablă prima damă.

D

DD

DD

D

DD

DD

115

Page 114: Algoritmi

Pentru reprezentarea unei soluţii putem folosi un vector cu ncomponente (având în vedere că pe fiecare linie se găseşte o singurădamă).

Exemplu pentru soluţia găsită avem vectorul ST ce poate fiasimilat unei stive.

Două dame se găsesc pe aceeaşi diagonală dacă si numai dacă esteîndeplinită condiţia: |st(i)-st(j)|=|i-j| ( diferenţa, în modul, întrelinii si coloane este aceeaşi).

În general ST(i)=k semnifică faptul că pe linia i dama ocupăpoziţia k.

3 ST(4)1 ST(3)4 ST(2)2 ST(1)

Exemplu: în tabla 4 x4 avem situaţia:

st(1)= 1 i = 1 st(3)= 3 j = 3|st(1) - st(3)| = |1 – 3| = 2 |i – j| = |1 – 3| = 2

sau situaţia

st(1) = 3 i = 1st(3) = 1 j = 3|st(i) - st(j)| = |3 – 1| = 2|i – j| = |1 – 3| = 2

Întrucât doua dame nu se pot găsi în aceeaşi coloană, rezultă căo soluţie este sub formă de permutare. O primă idee ne conduce lagenerarea tuturor permutărilor si la extragerea soluţiilor pentruproblema ca două dame să nu fie plasate în aceeaşi diagonală. Aproceda astfel, înseamnă că lucrăm conform strategiei backtracking.Aceasta presupune ca imediat ce am găsit două dame care se atacă, săreluăm căutarea.

Iată algoritmul, conform strategiei generate de backtracking:

DD

DD

DD

DD

116

Page 115: Algoritmi

– În prima poziţie a stivei se încarcă valoarea 1, cusemnificaţia că în linia unu se aşează prima damă în coloană.– Linia 2 se încearcă aşezarea damei în coloana 1, acestlucru nefiind posibil întrucât avem doua dame pe aceeaşicoloană.– În linia 2 se încearcă aşezarea damei în coloana 2 , însăacest lucru nu este posibil, pentru că damele se găsesc peaceiaşi diagonală (|st(1)-st(2)|=|1-2|);– Aşezarea damei 2 în coloana 3 este posibilă.– Nu se poate plasa dama 3 în coloana 1, întrucât în liniile1-3 damele ocupa acelaşi coloană.– Şi această încercare eşuează întrucât damele de pe 2 şi 3sunt pe aceeaşi diagonală.– Damele de pe 2-3 se găsesc pe aceeaşi coloană.– Damele de pe 2-3 se găsesc pe aceeaşi diagonală.– Am coborât în stivă mutând dama de pe linia 2 şi coloana 3în coloana 4.

Algoritmul se încheie atunci când stiva este vidă. Semnificaţiaprocedurilor utilizate este următoarea:

INIT - nivelul k al stivei este iniţializat cu 0;SUCCESOR - măreşte cu 1 valoarea aflată pe nivelul k alstivei în situaţia în care aceasta este mai mică decât n şiatribuie variabilei EV valoarea TRUE, în caz contrar,atribuie variabilei EV valoarea FALSE;VALID - validează valoarea pusă pe nivelul k al stivei,verificând dacă nu avem două dame pe aceeaşi linie(st(k)=st(i)), sau dacă nu avem două dame pe aceeaşidiagonală (st(k)-st(i)=|k-i|)caz în care variabilei EV i seatribuie FALSE; în caz contrar, variabilei EV i se atribuieTRUE;SOLUTIE - verifică dacă stiva a fost completată până lanivelul n inclusiv;TIPAR - tipăreşte o soluţie.

Subprogramele prezentate in limbajul Visual Basic sunt descrisemai jos:

Global n As Integer, am_suc As Boolean, ev As Boolean Type stiva ss(100) As Integer End TypeGlobal st As stiva

Sub init(k As Integer, st As stiva) st.ss(k) = 0

117

Page 116: Algoritmi

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 IfEnd 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 NextEnd Sub

Function solutie(k As Integer) As Integer If k = n Then solutie = True Else solutie = False End IfEnd 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 bEnd 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

118

Page 117: Algoritmi

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 WendEnd Sub

Sub Button2_Click() n = InputBox("n=", ib_title) backEnd Sub

Produsul cartezian a n mulţimi. Se dau mulţimile de mai jos şi secere 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 reţinenumerele k1, k2, …kn. Utilizăm metoda backtracking, uşor modificată dinurmătoarele motive:

a) Orice element aflat la nivelul k al stivei este valid,motiv pentru care procedura valid nu face altceva decât săatribuie variabilei ev valoarea TRUE.b) Limita superioară pe nivelul k al stivei este dată deA(k).

Modul de concepere a algoritmului rezultă din cele ce urmează:

1 2 3 1

119

Page 118: Algoritmi

1 1 1 2 2

1 1 1 1 1 1

2 3 1 2 3

2 2 3 3 3 3

1 1 1 1 1 1

Observaţii:Algoritmul prezentat aici este de tip backtracking? Întrebarea aresens pentru că este absent mecanismul de întoarcere. Vom admite că şiaceasta 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_cartEnd Sub

Sub cit_n(mes As String, nnn As Integer) Do nnn = InputBox(mes, y) Loop Until n > 0 And n < 100End 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) NextEnd Sub

Sub tipar(mes As String, n As Integer, a As vector) sir = "" For i = 1 To n sir = sir + Str$(a.v(i)) + "," Next MsgBox mes + " " + sirEnd Sub

Sub back_prod_cart()

120

Page 119: Algoritmi

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 WendEnd Sub

Sub valid_prod(ev As Boolean, st As stiva, k As Integer) ev = TrueEnd SubFunction solutie(k As Integer) As Boolean If k = n Then solutie = True Else solutie = False End IfEnd 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 IfEnd SubSub init(k As Integer, st As stiva) st.ss(k) = 0End Sub

121

Page 120: Algoritmi

Generarea aranjamentelor. Se citesc n şi p. Să se genereze toatearanjamentele de n luate câte p.

Din analiza problemei rezultă următoarele:– stiva are înălţimea p;– fiecare nivel ia valori între 1 şi n;– elementele plasate pe diverse niveluri trebuie să fiedistincte.

Algoritmul este asemănător cu cel de la permutări, cu deosebireacă aici stipa are înălţimea p.

Private Sub CommandButton17_Click() cit_n "n = ", n cit_n "p = ", p back_aranjEnd Sub

Sub back_aranj() Dim k As Integer k = 1 init k, st While k > 0 Do succesor am_suc, st, k If am_suc = True Then valid1 ev, st, k End If Loop Until (Not am_suc) Or (am_suc And ev) If am_suc Then If solutie1(k) Then tipar_rr Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd 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

122

Page 121: Algoritmi

NextEnd 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 bEnd Sub

Function solutie1(k As Integer) As Boolean If k = p Then solutie1 = True Else solutie1 = False End IfEnd Function

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 IfEnd SubSub init(k As Integer, st As stiva) st.ss(k) = 0End Sub

Generarea combinărilor. Se citesc n şi p numere naturale, np. Secere să se genereze toate submulţimile cu p elemente ale mulţimii {1,2, 3, …, n}.

Pentru rezolvarea problemei trebuie ţinut cont de următoarele:– stiva are înălţimea p;– elementele aflate pe niveluri diferite ale stivei trebuiesă fie distincte;– pentru a evita repetiţia elementele se aşează în ordinecrescătoare: pe nivelul k se va afla o valoare mai mare decâtpe nivelul k-1 şi mai mică sau egală cu n-p+k.

Private Sub CommandButton18_Click() cit_n "n = ", n cit_n "p = ", p

123

Page 122: Algoritmi

back_combEnd Sub

Sub back_comb() Dim k As Integer k = 1 init k, st While k > 0 Do succesor_c am_suc, st, k If am_suc = True Then valid_c ev, st, k End If Loop Until (Not am_suc) Or (am_suc And ev) If am_suc Then If solutie1(k) Then tipar_rr Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd 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 IfEnd 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

124

Page 123: Algoritmi

End If End IfEnd Sub

Function solutie1(k As Integer) As Boolean If k = p Then solutie1 = True Else solutie1 = False End IfEnd Function

Sub tipar_col() Dim i As Integer, b As String b = " " For i = 1 To nb = b + "Tara = " + Str$(i) + "; culoarea " + Str$(st.ss(i)) + " " Next MsgBox bEnd Sub

Problema comis-voiajorului. Un comis voiajor trebuie să vizitezeun număr n de oraşe. Iniţial, el se află într-unul dintre ele, notat1. Comis – voiajorul doreşte să nu treacă de două ori prin acelaşioraş, iar la întoarcere să revină în oraşul 1. Cunoscând legăturileexistente între oraşe, se cere să se tipărească toate drumurileposibile pe care le poate efectua comis – voiajorul.

Exemplu: În figura alăturată sunt simbolizate cele 6 oraşe,precum şi drumurile existente între ele.

2 3

1 4

125

Page 124: Algoritmi

6 5

Comis - voiajorul are următoarele posibilităţi de parcurgere:1, 2, 3, 4, 5, 6, 1;1, 2, 5, 4, 3, 6, 1;1, 6, 3, 4, 5, 2, 1;1, 6, 5, 4, 3, 2, 1;

Legăturile existente între oraşe sunt date în matricea An,n.Elementele matricei A pot fi 0 sau 1 (matricea este binară).

1, dacă există drum între oraşele i şi j;A(i,j) = 0 , altfelSe observă că A(i,j) = A(j,i), oricare ar fi i,j {1, 2, 3, …, n}

– matricea este simetrică.Pentru rezolvarea problemei folosim stiva st. la baza stivei

(nivelul 1) se încarcă numărul 1. Prezentăm în continuare modul derezolvare a problemei.

2 De la oraşul 1 la oraşul 2 există drum, deci se va urcaîn stivă;1

2 Oraşul 2 se mai găseşte în stivă, deci nu esteacceptat;2

1

3 De la oraşul 2 la oraşul 3 se găseşte drum; prin oraşul3 nu s-a mai trecut, deci oraşul 3 este acceptat.2

1

Algoritmul continuă în acest mod până se ajunge din nou lanivelul 1, caz în care algoritmul se încheie.

Un succesor, între 2 şi n, aflat pe nivelul k al stivei, esteconsiderat valid dacă sunt îndeplinite următoarele condiţii:

– nu s-a mai trecut prin oraşul simbolizat de succesor, deciacesta nu se regăseşte în stivă;– există drum între oraşul aflat la nivelul k-1 şi cel aflatla nivelul k;– dacă succesorul se găseşte la nivelul n, să existe drum dela el la oraşul 1.

126

Page 125: Algoritmi

Observaţii:1.Problemele rezolvate prin această metodă necesită un timpîndelungat de execuţie. Din acest motiv este bine să utilizămmetoda atunci numai atunci când nu mai avem la dispoziţie unalt algoritm mai eficient2.Menţionăm că nu există probleme pentru care nu se cunoscalgoritmi eficienţi de rezolvare, deci backtracking esteindicată.3.Rezolvarea iterativă încalcă principiul stivei atunci cândverificăm condiţiile de continuare, sau atunci când tipărimsoluţia găsită, pentru că accesăm orice nivel al stivei.Consider că o structură trebuie folosită ca atare atunci cândeste strict necesar. De exemplu, chiar şi segmentul de stivăal calculatorului poate fi accesat oriunde. Asta nu înseamnăcă acolo nu se utilizează din plin „mecanismul” stivei.

Sub back_comis() Dim k As Integer k = 1 init k, st While k > 0 Do succesor_col am_suc, st, k If am_suc = True Then valid_col ev, st, k End If Loop Until (Not am_suc) Or (am_suc And ev) If am_suc Then If solutie(k) Then tipar_col Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd Sub

Sub succesor_comis(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

127

Page 126: Algoritmi

End IfEnd SubSub valid_comis(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 NextEnd Sub

Sub tipar_comis() Dim i As Integer, b As String b = " " For i = 1 To n b = b + "Tara = " + Str$(i) + "; vizitat " + Str$(st.ss(i)) + " " Next MsgBox bEnd Sub

PROBLEMA COLORĂRII HĂRŢILOR

Enunţ:Fiind dată o hartă cu n ţări, se cer toate soluţiile de colorare

a hărţii, utilizând cel mult patru culori, astfel încât două ţări defrontieră comună să fie colorate diferit. Este demonstrat faptul căsunt suficiente numai patru culori pentru ca orice hartă să poată ficolorată.Rezolvare:

Pentru exemplificare, vom considera următoarea hartă unde ţărilesunt numerotate cu cifre cuprinse între 1 şi 5:

O soluţie a acestei probleme este următoarea:ţara 1 – culoarea 1

1

2 3

4

5

Figura 9.1 Harta

128

Page 127: Algoritmi

ţara 2 – culoarea 2ţara 3 – culoarea 1ţara 4 – culoarea 3ţara 5 – culoarea 4

Harta este furnizată programului cu ajutorul unei matrice An,n

1, dacă ţara i se învecinează cuţara j;A(i,j) =

0 , altfelMatricea A este simetrică. Pentru rezolvarea problemei se

utilizează stiva st, unde nivelul k al stivei simbolizează ţara k, iarst[k] culoarea ataşată ţării k. Stiva are înălţimea n şi pe fiecarenivel ia valori între 1 şi 4.

Sub back_col() Dim k As Integer k = 1 init k, st While k > 0 Do succesor_col am_suc, st, k If am_suc = True Then valid_col ev, st, k End If Loop Until (Not am_suc) Or (am_suc And ev) If am_suc Then If solutie(k) Then tipar_col Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd 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 IfEnd Sub

129

Page 128: Algoritmi

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 NextEnd Sub

Sub tipar_col() Dim i As Integer, b As String b = " " For i = 1 To nb = b + "Tara = " + Str$(i) + "; culoarea " + Str$(st.ss(i)) + " " Next MsgBox bEnd 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 bEnd Sub

130

Page 129: Algoritmi

CAPITOLUL X

METODA DIVIDE ET IMPERA

Metoda de programare DIVIDE ET IMPERA consta in impartireaproblemei initiale de dimensiuni [n] in doua sau mai multe probleme dedimensiuni reduse. In general se executa impartirea in douasubprobleme de dimensiuni aproximativ egale si anume [n/2]. Impartireain subprobleme are loc pana cand dimensiunea acestora devine suficientde mica pentru a fi rezolvate in mod direct(cazul de baza). Duparezolvarea celor doua subprobleme se executa faza de combinare arezultatelor in vederea rezolvarii intregii probleme.

Metoda DIVIDE ET IMPERA se poate aplica in rezolvarea uneiprobleme care indeplineste urmatoarele conditii:

se poate descompune in (doua sau mai multe) suprobleme; aceste suprobleme sunt independente una fata de alta (osubproblema nu se rezolva pe baza alteia si nu se folosesterezultate celeilalte); aceste subprobleme sunt similare cu problema initiala; la randul lor subproblemele se pot descompune (daca estenecesar) in alte subprobleme mai simple; aceste subprobleme simple se pot solutiona imediat prinalgoritmul simplificat.

Deoarece putine probleme indeplinesc conditiile de maisus ,aplicarea metodei este destul de rara.

Dupa cum sugereaza si numele "desparte si stapaneste "etapelerezolvarii unei probleme (numita problema initiala) in DIVIDE ETIMPERA sunt :

descompunerea problemei initiale in subproblemeindependente, smilare problemei de baza, de dimensiuni maimici; descompunerea treptata a subproblemelor in alte subproblemedin ce in ce mai simple, pana cand se pot rezolvaimediata ,prin algoritmul simplificat; rezolvarea subproblemelor simple; combinarea solutiilor gasite pentru construirea solutiilorsubproblemelor de dimensiuni din ce in ce mai mari; combinarea ultimelor solutii determina obtinerea solutieiproblemei initiale

Metoda DIVIDE ET IMPERA admite o implementare recursiva, deorecesubproblemele sunt similare problemei initiale, dar de dimensiunimai mici.

131

Page 130: Algoritmi

Principiul fundamental al recursivitatii este autoapelarea unuisubprogram cand acesta este activ; ceea ce se intampla la un nivel, seintampla la orice nivel, avand grija sa asiguram conditia de terminareale apelurilor repetate. Asemanator se intampla si in cazul metodeiDIVITE ET IMPERA; la un anumit nivel sunt doua posibilitati:

s-a ajuns la o (sub)problema simpla ce admite o rezolvareimediata caz in care se rezolva (sub)problema si se revinedin apel (la subproblema anterioara, de dimensiuni mai mari); s-a ajuns la o (sub)problema care nu admite o rezolvareimediata, caz in care o descompunem in doua sau mai multesubprobleme si pentru fiecare din ele se continua apelurilerecursive (ale procedurii sau functiei).

In etapa finala a metodei DIVIDE ET IMPERA se produce combinareasubproblemelor (rezolvate deja) prin secventele de revenire dinapelurile recursive.

Etapele metodei DIVIDE ET IMPERA (prezentate anterior) se potreprezenta prin urmatorul subprogram general (procedura saufunctie )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 obtineSOL1; Se combina solutiile SOL 1,... ,SOL K si se obtine SOL;Sfarsit_subprogram;

Deci, subprogramul DIVIMP se apeleaza pentru problema initialaPROB; aceasta admite descompunerea in K subprobleme simple; pentruacestea se reapeleaza recursiv subprogramul; in final se combinasolutiile acestor K subprobleme.

De obicei problema initiala se descompune in doua subprobleme maisimple; in acest caz etapele generale ale metodei DIVIDE ET IMPERA sepot reprezenta concret, in limbaj pseudocod, printr-o procedurarecursiva astfel:

Procedura DIVIMP(li,ls,sol); Daca ((ls-li)<=eps) Atunci REZOLVA (li,ls,sol); Altfel DIVIDE (li,m,ls); DIVIMP(li,msol1); DIVIMP(m,ls,sol2); COMBINA(sol1,sol2,sol);Sfarsit_procedura;

132

Page 131: Algoritmi

Procedura DIVIMP se apeleaza pentru problema initiala care aredimensiunea intre limita inferioara (li) si limita inferioara(ls);daca (sub)problema este simpla (ls-li<=eps), atunci procedura REZOLVAii afla solutia imediat si se produce intoarcerea din apelul recursiv;daca (sub)problema este (inca) complexa, atunci procedura DIVIDE oimparte in doua subprobleme, alegand pozitia m intre limitele li sils; pentru fiecare din cele doua subprobleme se reapeleaza recursivprocedura DIVIMP; in final, la intoarcerile din apeluri se producecombinarea celor doua soluitii sol1 si sol2 prin apelul proceduriiCOMBINA.

PROBLEMA TURNURILOR DIN HANOIPrezentarea algoritmului rezolvarii

Fie trei tije verticale notate A,B,C. Pe tija A se gasesc asezaten discuri de diametre diferite, in ordinea crescatoare a diametrelor,privind de sus in jos. Initial, tijele B si C sunt goale. Sa seafiseze toate mutarile prin care discurile de pe tija A se muta petija B, in aceeasi ordine, folosind ca tija de manevra C siresspectand urmatoarele reguli:

– la fiecare pas se muta un singur disc;– un disc se poate aseza numai peste un disc cu diametrul maimare.

Rezolvarea acestei probleme se bazeaza pe urmatoareleconsiderente logice:

– daca n=1, atunci mutarea este immediata AàB (mut discul depe A pe B); – daca n=2, atunci sirul mutarilor este: AàC,AàB,CàB;– daca n>2 procedam astfel :- mut (n-1) discuri AàC;- mut un disc AàB;- mut cele (n-1) discuri CàB.

Observam ca problema initiala se descompune in trei subproblememai simple, similare problemei initiale: mut (n-1)discuri AàC, mutultimul disc pe B, mut cele (n-1)discuri C-->B. Dimensiunile acestorsubprobleme sunt: n-1,1,n-1.

Aceste subprobleme sunt independente, deoarece tijele initial (pecare sunt dispuse discurile), tijele finale si tijele intermediaresunt diferite. Notam H(n,A,B,C)=sirul mutarilor a n discuri de pe A peB, folosind C.

PENTRU n=1 AàB n>1 H(n,A,B,C)= H(n-1,A,C,B),AB, H(n-1,C,B,A)

133

Page 132: Algoritmi

Sortare rapida (quicksort)Un tablou V se completeaza cu n elemente numere reale .Sa se

ordoneze crescator folosind metoda de sortare rapida .Solutia problemei se bazeaza pe urmatoarele etape implementate in

programul principal: se apeleaza procedura “quick” cu limita inferioara li=1 silimita superioara ls=n; functia”poz” realizeaza mutarea elementului v[i] exact pepozitia ce o va ocupa acesta in vectorul final ordonat ;functia”poz” intoarce (in k ) pozitia ocupata de acestelement; in acest fel , vectorul V se imparte in doua parti: li …k-1si k+1…ls; pentru fiecare din aceste parti se reapeleaza procedura”quick”, cu limitele modificate corespunzator; in acest fel, primul element din fiecare parte va fipozitionat exact pe pozitia finala ce o va ocupa in vectorulfinal ordonat (functia”poz”); fiecare din cele doua parti va fi, astfel, inpartita inalte doua parti; procesul continua pana cand limitelepartilor ajung sa se suprapuna ,ceea ce indica ca toateelementele vectorului au fost mutate exact pe pozitiile ce levor ocupa in vectorul final ;deci vectorul este ordonat ; in acest moment se produc intoarcerile din apelurilerecursive si programul isi termina executia.

Observaţii:– daca elementul se afla in stanga, atunci se compara cuelementele din dreapta lui si se sar (j:=j-1) elementele maimari decat el;– daca elementul se afla in dreapta, atunci se compara cuelemente din stanga lui si se sar (i:=i+1) elementele maimici decat el.

Sortare prin interclasare(mergesort)Tabloul unidimensional V se completeaza cu n numere reale. Sa se

ordoneze crescator folosind sortare prin interclasare.Sortarea prin interclasare se bazeaza pe urmatoarea logica:

– vectorul V se imparte, prin injumatatiri succesive,invectori din ce in ce mai mici; – cand se ating vectorii de maxim doua elemente, fiecaredintre acestia se ordoneaza printr-o simpla comparare aelementelor;

134

Page 133: Algoritmi

– cate doi astfel de mini-vectori ordonati se interclaseazasuccesiv pana se ajunge iar la vectorul V.

Observaţii: – mecanismul general de tip Divide et Impera se gasesteimplementat in procedura “divi”;– astfel de abordare a problemei sortarii unii vector conducela economie de timp de calcul, deoarece operatia deinterclasare a doi vectori deja ordonati este foarte rapida,iar ordonarea independenta celor doua jumatati (mini-vectori)consuma in total aproximativ a doua parte din timpul care arfi necesar ordonarii vectorului luat ca intreg .

CONCLUZII LA TEHNICA  DIVIDE ET IMPERA 

Sortare prin insertie binaraSa se ordoneze crescator un tablou unidimensional V de n numere

reale, folosind sortarea prin insertie binara.Pentru fiecare element v[i] se procedeaza in patru pasi:

1. se considera ordonate elementele v[1],v[2],….,v[i-1];2. se cauta pozitia k pe care urmeaza s-o ocupe v[i] intreelementele v[1],v[2],…,v[i-1] (procedura “poz” prin cautarebinara);3. se deplaseaza spre dreapta elementele din pozitiilek,k+1,…,n (procedura “deplasare”);4. insereaza elementul v[i] in pozitia k(procedura”deplasare”);

Se obtine o succesiune de k+1 elemente ordonate crescator.

Analiza a complexitatii timp pentru algoritmii Divide et ImperaAlgoritmii de tip Divide et Impera au buna comportare in timp,

daca se indeplinesc urmatoarele conditii:– dimensiunile subprogramelor (in care se imparte problemainitiala) sunt aproximativ egale (“principiul balansarii”);– lipsesc fazele de combinare a solutiilor subproblemelor(cautare binara).

135

Page 134: Algoritmi

CAPITOLUL XI

METODA GREEDY

11.1 Algoritmi greedy

Pusi in fata unei probleme pentru care trebuie sa elaboram unalgoritm, de multe ori “nu stim cum sa incepem”. Ca si in orice altaactivitate, exista cateva principii generale care ne pot ajuta inaceasta situatie. Ne propunem sa prezentam in urmatoarele capitoletehnicile fundamentale de elaborare a algoritmilor. Cateva din acestemetode sunt atat de generale, incat le folosim frecvent, chiar dacanumai intuitiv, ca reguli elementare in gandire.

11.2 Tehnica greedy

Algoritmii greedy (greedy = lacom) sunt in general simpli si suntfolositi la probleme de optimizare, cum ar fi: sa se gaseasca cea maibuna ordine de executare a unor lucrari pe calculator, sa se gaseascacel mai scurt drum intr-un graf etc. In cele mai multe situatii deacest fel avem:

multime de candidati (lucrari de executat, varfuri alegrafului etc) o functie care verifica daca o anumita multime de candidaticonstituie o solutie posibila o functie care verifica daca o multime de candidati estefezabila, adica daca este posibil sa completam aceasta multimeastfel incat sa obtinem o solutie posibila, nu neaparatoptima, a problemei o functie de selectie care indica la orice moment care este celmai promitator dintre candidatii inca nefolositi o functie obiectiv care da valoarea unei solutii (timpulnecesar executarii tuturor lucrarilor intr-o anumita ordine,lungimea drumului pe care l-am gasit etc); aceasta estefunctia pe care urmarim sa o optimizam (minimizam/maximizam)

Pentru a rezolva problema noastra de optimizare, cautam o solutieposibila care sa optimizeze valoarea functiei obiectiv. Un algoritmgreedy construieste solutia pas cu pas. Initial, multimea candidatilorselectati este vida. La fiecare pas, incercam sa adaugam acesteimultimi cel mai promitator candidat, conform functiei de selectie.Daca, dupa o astfel de adaugare, multimea de candidati selectati numai este fezabila, eliminam ultimul candidat adaugat; acesta nu va maifi niciodata considerat. Daca, dupa adaugare, multimea de candidatiselectati este fezabila, ultimul candidat adaugat va ramane de acum

136

Page 135: Algoritmi

incolo in ea. De fiecare data cand largim multimea candidatilorselectati, verificam daca aceasta multime nu constituie o solutieposibila a problemei noastre. Daca algoritmul greedy functioneazacorect, prima solutie gasita va fi totodata o solutie optima aproblemei. Solutia optima nu este in mod necesar unica: se poate cafunctia obiectiv sa aiba aceeasi valoare optima pentru mai multesolutii posibile. Descrierea formala a unui algoritm greedy generaleste:

 function greedy(C)     {C este multimea candidatilor}     S ← ø    {S este multimea in care construim solutia}     while not solutie(S) and C ≠ ø do         x ← un element din C care maximizeaza/minimizeazaselect(x) C ← C \ {x}         if fezabil(S {x}) then S ← S {x}    if  solutie(S)        then   return S                            else   return “nu există soluţie”

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-ipese de viitor si fara sa se razgandeasca. Daca un candidat esteinclus in solutie, el ramane acolo; daca un candidat este exclus dinsolutie, el nu va mai fi niciodata reconsiderat. Asemenea unuiintreprinzator rudimentar care urmareste castigul imediat in daunacelui de perspectiva, un algoritm greedy actioneaza simplist. Totusi,ca si in afaceri, o astfel de metoda poate da rezultate foarte bunetocmai datorita simplitatii ei.

Functia select este de obicei derivata din functia obiectiv; uneoriaceste doua functii sunt chiar identice.

Un exemplu simplu de algoritm greedy este algoritmul folositpentru rezolvarea urmatoarei probleme. Sa presupunem ca dorim sa damrestul unui client, folosind un numar cat mai mic de monezi. In acestcaz, elementele problemei sunt:

candidatii: multimea initiala de monezi de 1, 5, si 25 unitati, in care presupunem ca din fiecare tip de moneda avemo 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

137

Page 136: Algoritmi

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; sedoreste 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 combinarileposibile 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 pentrufiecare 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 necesarfiecarui client este cunoscut in prealabil: pentru clientul i estenecesar un timp ti, 1 ≤ i ≤ n. Dorim sa minimizam timpul total de asteptare

(timpul de asteptarepentru 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 1este servit primul, clientul 2 asteapta pana este servit clientul 1 siapoi este servit, clientul 3 asteapta pana sunt serviti clientii 1, 2si apoi este servit. Timpul total de asteptare a celor trei clientieste 38.

Ordinea                         T1    2    3          5+(5+10)+(5+10+3)        =    381    3    2          5+(5+3)+(5+3+10)          =    312    1    3          10+(10+5)+(10+5+3)      =    432    3    1          

138

Page 137: Algoritmi

10+(10+3)+(10+3+5)      =    413    1    2          3+(3+5)+(3+5+10)          =    29     ←optim3    2    1          3+(3+10)+(3+10+5)        =    34

Algoritmul greedy este foarte simplu: la fiecare pas seselecteaza clientul cu timpul minim de servire din multimea de clientiramasa. 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 intregia < b cu

Interschimbam pe ia cu ib in I; cu alte cuvinte, clientul care afost servit al b-lea va fi servit acum al a-lea si invers. Obtinem onoua ordine de servire J, care este de preferat deoarece

Prin metoda greedy obtinem deci intotdeauna planificarea optima aclientilor.

Problema poate fi generalizata pentru un sistem cu mai multestatii de servire.

Interclasarea optima a sirurilor ordonateSa presupunem ca avem doua siruri S1 si S2 ordonate crescator si ca

dorim sa obtinem prin interclasarea lor sirul ordonat crescator carecontine elementele din cele doua siruri. Daca interclasarea are locprin deplasarea elementelor din cele doua siruri in noul sir rezultat,atunci numarul deplasarilor este #S1 + #S2.

Generalizand, sa consideram acum n siruri S1, S2, ..., Sn, fiecaresir Si, 1 ≤ i ≤ n, fiind format din qi elemente ordonate crescator (vomnumi qi lungimea lui Si). Ne propunem sa obtinem sirul S ordonatcrescator, continand exact elementele din cele n siruri. Vom realizaacest lucru prin interclasari succesive de cate doua siruri. Problemaconsta in determinarea ordinii optime in care trebuie efectuate acesteinterclasari, astfel incat numarul total al deplasarilor sa fie catmai mic. Exemplul de mai jos ne arata ca problema astfel formulata nu

139

Page 138: Algoritmi

este banala, adica nu este indiferent in ce ordine se facinterclasarile.

Fie sirurile S1, S2, S3 de lungimi q1 = 30, q2 = 20, q3 = 10. Dacainterclasam pe S1 cu S2, iar rezultatul il interclasam cu S3, numarultotal al deplasarilor este (30+20)+(50+10)=110. Daca il interclasam peS3 cu S2, iar rezultatul il interclasam cu S1, numarul total aldeplasarilor este (10+20)+(30+30)=90.

Atasam fiecarei strategii de interclasare cate un arbore binar incare valoarea fiecarui varf este data de lungimea sirului pe care ilreprezinta. Daca sirurile S1, S2, ..., S6 au lungimile q1 = 30, q2 = 10,q3 = 20, q4 = 30, q5 = 50, q6 = 10, doua astfel de strategii de interclasare suntreprezentate prin arborii din Figura 11.1.

Figura 11.1 Reprezentarea strategiilor deinterclasare.

Observam ca fiecare arbore are 6 varfuri terminale, corespunzandcelor 6 siruri initiale si 5 varfuri neterminale, corespunzand celor 5interclasari care definesc strategia respectiva. Numerotam varfurilein felul urmator: varful terminal i, 1 ≤ i ≤ 6, va corespunde siruluiSi, iar varfurile neterminale se numeroteaza de la 7 la 11 in ordineaobtinerii interclasarilor respective (Figura 11.2).

Figura 11.2 Numerotarea varfurilor arborilordin Figura 11.1

140

Page 139: Algoritmi

Strategia greedy apare in Figura 11.1b si consta in a interclasamereu cele mai scurte doua siruri disponibile la momentul respectiv.

Interclasand sirurile S1, S2, ..., Sn, de lungimi q1, q2, ..., qn,obtinem pentru fiecare strategie cate un arbore binar cu n varfuriterminale, numerotate de la 1 la n, si n–1 varfuri neterminale,numerotate de la n+1 la 2n–1. Definim, pentru un arbore oarecare A deacest tip, lungimea externa ponderata:

unde ai este adancimea varfului i. Se observa ca numarul total dedeplasari de elemente pentru strategia corespunzatoare lui A estechiar 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 intotdeaunainterclasarea optima a n siruri ordonate, deci strategia cu arborelede lungime externa ponderata minima.

Demonstratie: Demonstram prin inductie. Pentru n = 1, proprietateaeste verificata. Presupunem ca proprietatea este adevarata pentru n–1siruri. Fie A arborele strategiei greedy de interclasare a n siruri delungime q1 ≤ q2 ≤ ... qn. Fie B un arbore cu lungimea externa ponderataminima, corespunzator unei strategii optime de interclasare a celor nsiruri. In arborele A apare subarborele

reprezentand prima interclasare facuta conform strategiei greedy. Inarborele B, fie un varf neterminal de adancime maxima. Cei doi fii aiacestui varf sunt atunci doua varfuri terminale qj si qk. Fie B'arborele obtinut din B schimband intre ele varfurile q1 si qj, respectivq2 si qk. Evident, L(B') ≤ L(B). Deoarece B are lungimea externaponderata minima, rezulta ca L(B') = L(B). Eliminand din B' varfurile q1

si q2, obtinem un arbore B" cu n–1 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 arborelestrategiei greedy de interclasare a sirurilor de lungimeq1+q2, q3, ..., qn. Cum A se obtine din A' atasand la varful q1+q2 fiii q1

141

Page 140: Algoritmi

si q2, iar B' se obtine in acelasi mod din B", rezulta caL(A) = L(B') = L(B). Proprietatea este deci adevarata pentru orice n.

La scrierea algoritmului care genereaza arborele strategieigreedy de interclasare vom folosi un min-heap. Fiecare element al min-heap-ului este o pereche (q, i) unde i este numarul unui varf dinarborele strategiei de interclasare, iar q este lungimea sirului pecare il reprezinta. Proprietatea de min-heap se refera la valoarea luiq.

Algoritmul interopt va construi arborele strategiei greedy. Un varfi al arborelui va fi memorat in trei locatii diferite continand:

LU[i]    =   lungimea sirului reprezentat de varfST[i]    =   numarul fiului stangDR[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 2n–1 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-heapsi de extragere din min-heap necesita un timp in ordinul lui log n.Restul operatiilor necesita un timp constant. Timpul total pentruinteropt este deci in O(n log n).

Coduri HuffmanO alta aplicatie a strategiei greedy si a arborilor binari cu

lungime externa ponderata minima este obtinerea unei codificari catmai compacte a unui text.

Un principiu general de codificare a unui sir de caractere esteurmatorul: se masoara frecventa de aparitie a diferitelor caracteredintr-un esantion de text si se atribuie cele mai scurte coduri, celormai frecvente caractere, si cele mai lungi coduri, celor mai putinfrecvente caractere. Acest principiu sta, de exemplu, la baza coduluiMorse. Pentru situatia in care codificarea este binara, exista o

142

Page 141: Algoritmi

metoda eleganta pentru a obtine codul respectiv. Aceasta metoda,descoperita de Huffman (1952) foloseste o strategie greedy si senumeste codificarea Huffman. O vom descrie pe baza unui exemplu.

Fie un text compus din urmatoarele litere (in paranteze figureazafrecventele 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 estedata de frecventa pe care o reprezinta.

Etichetam muchia stanga cu 1 si muchia dreapta cu 0. Rearanjamtabelul de frecvente:

S (10), I (29), O (9), {P, T} (45 = 9)Multimea {P, T} semnifica evenimentul reuniune a celor doua evenimente

independente corespunzatoare aparitiei literelor P si T. Continuam procesul, obtinandarborele

In final, ajungem la arborele din Figura 11.3, in care fiecarevarf terminal corespunde unei litere din text.

Pentru a obtine codificarea binara a literei P, nu avem decat sascriem secventa de 0-uri si 1-uri in ordinea aparitiei lor pe drumulde la radacina catre varful corespunzator lui P: 1011. Procedamsimilar 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 varfurileterminale avand valorile f1, f2, ..., fn, prin care se obtine ocodificare binara a textului. Un arbore de codificare nu trebuie inmod necesar sa fie construit dupa metoda greedy a lui Huffman,alegerea varfurilor care sunt fuzionate la fiecare pas putandu-se facedupa diverse criterii. Lungimea externa ponderata a unui arbore decodificare este:

143

Page 142: Algoritmi

Figura 11.3 Arborele de codificareHuffman.

unde ai este adincimea varfului terminal corespunzator literei i. Seobserva ca lungimea externa ponderata este egala cu numarul total decaractere din codificarea textului considerat. Codificarea cea maicompacta a unui text corespunde deci arborelui de codificare delungime externa ponderata minima. Se poate demonstra ca arborele decodificare Huffman minimizeaza lungimea externa ponderata pentru totiarborii de codificare cu varfurile terminale avand valorilef1, f2, ..., fn. Prin strategia greedy se obtine deci intotdeaunacodificarea binara cea mai compacta a unui text.

Arborii de codificare pe care i-am considerat in acesta sectiunecorespund unei codificari de tip special: codificarea unei litere nueste prefixul codificarii nici unei alte litere. O astfel decodificare este de tip prefix. Codul Morse nu face parte din aceastacategorie. Codificarea cea mai compacta a unui sir de caractere poatefi intotdeauna obtinuta printr-un cod de tip prefix. Deci,concentrandu-ne atentia asupra acestei categorii de coduri, nu ampierdut nimic din generalitate.

11.4  Arbori parţiali de cost minim

Fie G = <V, M> un graf neorientat conex, unde V este multimeavarfurilor si M este multimea muchiilor. Fiecare muchie are un costnenegativ (sau o lungime nenegativa). Problema este sa gasim osubmultime A   M, astfel incat toate varfurile din V sa raminaconectate atunci cand sunt folosite doar muchii din A, iar sumalungimilor muchiilor din A sa fie cat mai mica. Cautam deci osubmultime A de cost total minim. Aceasta problema se mai numeste siproblema conectarii oraselor cu cost minim, avand numeroase aplicatii.

Graful partial <V, A> este un arbore si este numit arborele partial decost minim al grafului G (minimal spanning tree). Un graf poate avea mai

144

Page 143: Algoritmi

multi arbori partiali de cost minim si acest lucru se poate verificape un exemplu.

Vom prezenta doi algoritmi greedy care determina arborele partialde cost minim al unui graf. In terminologia metodei greedy, vom spuneca o multime de muchii este o solutie, daca constituie un arbore partialal grafului G, si este fezabila, daca nu contine cicluri. O multimefezabila de muchii este promitatoare, daca poate fi completata pentru aforma solutia optima. O muchie atinge o multime data de varfuri, dacaexact un capat al muchiei este in multime. Urmatoarea proprietate vafi folosita pentru a demonstra corectitudinea celor doi algoritmi.

Proprietatea 11.2 Fie G = <V, M> un graf neorientat conex in carefiecare muchie are un cost nenegativ. Fie W   V o submultime strictaa 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 costminim care atinge W. Atunci, A   {m} este promitatoare.

Demonstratie: Fie B un arbore partial de cost minim al lui G, astfelincat A   B (adica, muchiile din A sunt continute in arborele B). Unastfel 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 matinge W, trebuie sa mai existe cel putin o muchie m' care atinge siea pe W (altfel, ciclul nu se inchide). Eliminandu-l pe m', cicluldispare si obtinem un nou arbore partial B' al lui G. Costul lui m estemai mic sau egal cu costul lui m', deci costul total al lui B' este maimic sau egal cu costul total al lui B. De aceea, B' este si el unarbore partial de cost minim al lui G, care include pe m. Observam caA   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 greedyaleg muchiile una cate una intr-o anumita ordine, aceasta ordine fiindspecifica fiecarui algoritm.

Algoritmul lui KruskalArborele partial de cost minim poate fi construit muchie, cu

muchie, dupa urmatoarea metoda a lui Kruskal (1956): se alege intaimuchia de cost minim, iar apoi se adauga repetat muchia de cost minimnealeasa anterior si care nu formeaza cu precedentele un ciclu. Alegemastfel #V–1 muchii. Este usor de dedus ca obtinem in final un arbore.Este insa acesta chiar arborele partial de cost minim cautat?

145

Page 144: Algoritmi

Inainte de a raspunde la intrebare, sa consideram, de exemplu, graful dinFigura 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.

Figura 11.4 Un graf si arborele sau partialde cost minim.

Pasul Muchiaconsiderata

Componentele conexe alesubgrafului <V, A>

initializare

— {1}, {2}, {3}, {4}, {5},{6}, {7}

1 {1, 2} {1, 2}, {3}, {4}, {5},{6}, {7}

2 {2, 3} {1, 2, 3}, {4}, {5}, {6},{7}

3 {4, 5} {1, 2, 3}, {4, 5}, {6},{7}

4 {6, 7} {1, 2, 3}, {4, 5}, {6, 7}5 {1, 4} {1, 2, 3, 4, 5}, {6, 7}6 {2, 5} respinsa (formeaza ciclu)7 {4, 7} {1, 2, 3, 4, 5, 6, 7}

Tabelul 11.1 Algoritmul lui Kruskal aplicatgrafului din Figura 11.4a.

Multimea A este initial vida si se completeaza pe parcurs cumuchii acceptate (care nu formeaza un ciclu cu muchiile deja existentein 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 padureaprecedenta unind doua componente. Fiecare componenta conexa este la

146

Page 145: Algoritmi

randul ei un arbore partial de cost minim pentru varfurile pe care leconecteaza. Initial, fiecare varf formeaza o componenta conexa. Lasfarsit, vom avea o singura componenta conexa, care este arborelepartial de cost minim cautat (Figura 11.4b).

Ceea ce am observat in acest caz particular este valabil sipentru cazul general, din Proprietatea 11.2 rezultand:

Proprietatea 11.3 In algoritmul lui Kruskal, la fiecare pas, grafulpartial <V, A> formeaza o padure de componente conexe, in care fiecarecomponenta conexa este la randul ei un arbore partial de cost minimpentru varfurile pe care le conecteaza. In final, se obtine arborelepartial de cost minim al grafului G. 

Pentru a implementa algoritmul, trebuie sa putem manipulasubmultimile formate din varfurile componentelor conexe. Folosimpentru aceasta o structura de multimi disjuncte si procedurile de tipfind si merge. In acest caz, este preferabil sa reprezentam graful ca olista de muchii cu costul asociat lor, astfel incat sa putem ordonaaceasta lista in functie de cost. Iata algoritmul:

function Kruskal(G = <V, M>){initializare}sorteaza M crescator in functie de costn ← #VA ← ø {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-1return A

Pentru un graf cu n varfuri si m muchii, presupunand ca sefolosesc procedurile find3 si merge3, numarul de operatii pentru cazulcel mai nefavorabil este in:

147

Page 146: Algoritmi

O(m log m) pentru a sorta muchiile. Deoarecem ≤ n(n–1)/2, rezulta O(m log m)   O(m log n). Mai mult,graful fiind conex, din n-1 ≤ m rezulta si O(m log n)   O(m log m), deci O(m log m) = O(m log n). O(n) pentru a initializa cele n multimi disjuncte. Cele cel mult 2m operatii find3 si n–1 operatii merge3necesita un timp in O((2m+n-1) lg* n). Deoarece O(lg* n)   O(log n) si n-1 ≤ m, acest timp este si in O(m log n). O(m) pentru restul operatiilor.

Deci, pentru cazul cel mai nefavorabil, algoritmul lui Kruskalnecesita un timp in O(m log n).

O alta varianta este sa pastram muchiile intr-un min-heap.Obtinem astfel un nou algoritm, in care initializarea se face intr-untimp in O(m), iar fiecare din cele n–1 extrageri ale unei muchiiminime se face intr-un timp in O(log m) = O(log n). Pentru cazul celmai nefavorabil, ordinul timpului ramane acelasi cu cel al vechiuluialgoritm. Avantajul folosirii min-heap-ului apare atunci cand arborelepartial de cost minim este gasit destul de repede si un numarconsiderabil de muchii raman netestate. In astfel de situatii,algoritmul vechi pierde timp, sortand in mod inutil si aceste muchii.

Algoritmul lui PrimCel de-al doilea algoritm greedy pentru determinarea arborelui

partial de cost minim al unui graf se datoreaza lui Prim (1957). Inacest algoritm, la fiecare pas, multimea A de muchii alese impreuna cumultimea U a varfurilor pe care le conecteaza formeaza un arborepartial de cost minim pentru subgraful <U, A> al lui G. Initial,multimea U a varfurilor acestui arbore contine un singur varf oarecaredin V, care va fi radacina, iar multimea A a muchiilor este vida. Lafiecare pas, se alege o muchie de cost minim, care se adauga laarborele precedent, dand nastere unui nou arbore partial de cost minim(deci, exact una dintre extremitatile acestei muchii este un varf inarborele precedent). Arborele partial de cost minim creste “natural”,cu cate o ramura, pina cand va atinge toate varfurile din V, adicapina cand U = V. Functionarea algoritmului, pentru exemplul din Figura11.4a, este ilustrata in Tabelul 11.2. La sfarsit, A va contineaceleasi muchii ca si in cazul algoritmului lui Kruskal. Faptul caalgoritmul functioneaza intotdeauna corect este exprimat de urmatoareaproprietate, pe care o puteti demonstra folosind Proprietatea 11.2.

Pasul Muchiaconsiderata

U

initializ — {1}

148

Page 147: Algoritmi

are1 {2, 1} {1, 2}2 {3, 2} {1, 2, 3}3 {4, 1} {1, 2, 3, 4}4 {5, 4} {1, 2, 3, 4,

5}5 {7, 4} {1, 2, 3, 4,

5, 6}6 {6, 7} {1, 2, 3, 4,

5, 6, 7}

Tabelul 11.2 Algoritmul lui Prim aplicatgrafului 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> allui G. In final, se obtine arborele partial de cost minim al grafuluiG.  Descrierea formala a algoritmului este data in continuare.

function Prim-formal(G = <V, M>){initializare} A ←ø    {va contine muchiile arborelui partial de costminim} U ← {un varf oarecare din V}{bucla greedy} while U ≠ V do gaseste {u, v} de cost minim astfel ca u   V \ U si v   U      A ← A  {{u, v}}      U ← U  {u}  return A

Pentru a obtine o implementare simpla, presupunem ca: varfuriledin V sunt numerotate de la 1 la n, V = {1, 2, ..., n}; matriceasimetrica C da costul fiecarei muchii, cu C[i, j] = + , daca muchia{i, j} nu exista. Folosim doua tablouri paralele. Pentru fiecare i   V \ U, vecin[i] contine varful din U, care este conectat de i printr-omuchie de cost minim; mincost[i] da acest cost. Pentru i   U, punemmincost[i] = –1. Multimea U, in mod arbitrar initializata cu {1}, nueste reprezentata explicit. Elementele vecin[1] si mincost[1] nu sefolosesc.

149

Page 148: Algoritmi

function Prim(C[1 .. n, 1 .. n]){initializare; numai varful 1 este in U} A ← ø for i ← 2 to n do   vecin[i]  ← 1   mincost[i] ← C[i, 1]{bucla greedy} repeat n–1 times       min ← +         for j ← 2 to n do          if 0 < mincost[ j] < min  then   min ← mincost[ j]             k ← j           A ← A   {{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] ← kreturn A

Bucla principala se executa de n–1 ori si, la fiecare iteratie,buclele for din interior necesita un timp in O(n). Algoritmul Primnecesita, deci, un timp in O(n2). Am vazut ca timpul pentru algoritmullui 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 den(n–1)/2. In acest caz, algoritmul Kruskal necesita un timp inO(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 sialgoritmul Kruskal necesita un timp in O(n log n), fiind probabil maieficient 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 varfurilorsi M este multimea muchiilor. Fiecare muchie are o lungime nenegativa.Unul din varfuri este desemnat ca varf sursa. Problema este sadeterminam lungimea celui mai scurt drum de la sursa catre fiecarevarf din graf.

Vom folosi un algoritm greedy, datorat lui Dijkstra (1959). Notamcu C multimea varfurilor disponibile (candidatii) si cu S multimea

150

Page 149: Algoritmi

varfurilor deja selectate. In fiecare moment, S contine acele varfuri acaror distanta minima de la sursa este deja cunoscuta, in timp cemultimea C contine toate celelalte varfuri. La inceput, S contine doarvarful sursa, iar in final S contine toate varfurile grafului. Lafiecare pas, adaugam in S acel varf din C a carui distanta de la sursaeste cea mai mica.

Spunem ca un drum de la sursa catre un alt varf este special, dacatoate varfurile intermediare de-a lungul drumului apartin lui S.Algoritmul lui Dijkstra lucreaza in felul urmator. La fiecare pas alalgoritmului, un tablou D contine lungimea celui mai scurt drumspecial catre fiecare varf al grafului. Dupa ce adaugam un nou varf vla S, cel mai scurt drum special catre v va fi, de asemenea, cel maiscurt dintre toate drumurile catre v. Cand algoritmul se termina, toatevarfurile din graf sunt in S, deci toate drumurile de la sursa catrecelelalte varfuri sunt speciale si valorile din D reprezinta solutiaproblemei.

Presupunem, pentru simplificare, ca varfurile sunt numerotate,V = {1, 2, ..., n}, varful 1 fiind sursa, si ca matricea L da lungimeafiecarei muchii, cu L[i, j] = + , daca muchia (i, j) nu exista. Solutiase 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 n–2 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 prezentatiin Tabelul 11.3.

151

Page 150: Algoritmi

Figura 11.5 Un graforientat.

Pasul v C Dinitializ

are— {2, 3, 4,

5}[50, 30,100, 10]

1 5 {2, 3, 4}[50, 30, 20,10]

2 4 {2, 3} [40, 30, 20,10]

3 3 {2} [35, 30, 20,10]

Tabelul 11.3 Algoritmul lui Dijkstra aplicatgrafului din Figura 11.5.

Observam ca D nu se schimba daca mai efectuam o iteratie pentrua-l scoate si pe {2} din C. De aceea, bucla greedy se repeta de doarn-2 ori.

Se poate demonstra urmatoarea proprietate:

Proprietatea 11.5. In algoritmul lui Dijkstra, daca un varf ii)   este in S, atunci D[i] da lungimea celui mai scurt drum dela sursa catre i;ii)    nu este in S, atunci D[i] da lungimea celui mai scurt drumspecial de la sursa catre i. 

La terminarea algoritmului, toate varfurile grafului, cu exceptiaunuia, sunt in S. Din proprietatea precedenta, rezulta ca algoritmullui Dijkstra functioneaza corect.

Daca dorim sa aflam nu numai lungimea celor mai scurte drumuri,dar si pe unde trec ele, este suficient sa adaugam un tablouP[2 .. n], unde P[v] contine numarul nodului care il precede pe v incel mai scurt drum. Pentru a gasi drumul complet, nu avem decat saurmarim, in tabloul P, varfurile prin care trece acest drum, de ladestinatie la sursa. Modificarile in algoritm sunt simple:

152

Page 151: Algoritmi

– initializeaza P[i] cu 1, pentru 2 ≤ i ≤ n– continutul buclei for cea mai interioara se inlocuieste cuif 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 oriSa presupunem ca aplicam algoritmul Dijkstra asupra unui graf cu n

varfuri si m muchii. Initializarea necesita un timp in O(n). Alegerealui v din bucla repeat presupune parcurgerea tuturor varfurilorcontinute 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 interioaraefectueaza 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 nusub 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 saconsideram doar varfurile w adiacente lui v. Aceasta nu poate duce lamodificarea ordinului timpului total al algoritmului, daca nu reusimsa scadem si ordinul timpului necesar pentru alegerea lui v din buclarepeat. De aceea, vom tine varfurile v din C intr-un min-heap, in carefiecare element este de forma (v, D[v]), proprietatea de min-heapreferindu-se la valoarea lui D[v]. Numim algoritmul astfel obtinutDijkstra-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 n–2 extrageri estenevoie de un timp in O(n log n).

Pentru a testa daca “D[w] > D[v]+L[v, w]”, bucla for interioaraconsta 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 prilejsunt testate exact muchiile adiacente lui; rezulta ca numarul total deastfel 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 totalpentru operatiile percolate este deci in O(m log n).

In concluzie, algoritmul Dijkstra-modificat necesita un timp inO(max(n, m) log n). Daca graful este conex, atunci m ≥ n si timpuleste in O(m log n). Pentru un graf rar este preferabil sa folosimalgoritmul Dijkstra-modificat, iar pentru un graf dens algoritmul Dijkstraeste 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

153

Page 152: Algoritmi

formeaza un arbore partial al celor mai scurte drumuri pentru G. Desigur, acestarbore depinde de alegerea radacinii i si el difera, in general, dearborele partial de cost minim al lui G.

Problema gasirii celor mai scurte drumuri care pleaca din acelasipunct se poate pune si in cazul unui graf neorientat.

11.6  Euristica greedy

Pentru anumite probleme, se poate accepta utilizarea unoralgoritmi despre care nu se stie daca furnizeaza solutia optima, darcare furnizeaza rezultate “acceptabile”, sunt mai usor de implementatsi mai eficienti decat algoritmii care dau solutia optima. Un astfelde algoritm se numeste euristic.

Una din ideile frecvent utilizate in elaborarea algoritmiloreuristici consta in descompunerea procesului de cautare a solutieioptime in mai multe subprocese succesive, fiecare din acestesubprocese constand dintr-o optimizare. O astfel de strategie nu poateconduce intotdeauna la o solutie optima, deoarece alegerea uneisolutii optime la o anumita etapa poate impiedica atingerea in final aunei solutii optime a intregii probleme; cu alte cuvinte, optimizarealocala nu implica, in general, optimizarea globala. Regasim, de fapt,principiul care sta la baza metodei greedy. Un algoritm greedy, desprecare nu se poate demonstra ca furnizeaza solutia optima, este unalgoritm 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 trebuiecolorate astfel incat oricare doua varfuri adiacente sa fie coloratediferit. Problema este de a obtine o colorare cu un numar minim deculori.Folosim urmatorul algoritm greedy: alegem o culoare si un varfarbitrar de pornire, apoi consideram varfurile ramase, incercand sa lecoloram, fara a schimba culoarea. Cand nici un varf nu mai poate ficolorat, schimbam culoarea si varful de start, repetand procedeul.

Figura 11.6 Un graf care154

Page 153: Algoritmi

va fi colorat.

Daca in graful din Figura 11.6 pornim cu varful 1 si il coloramin rosu, mai putem colora tot in rosu varfurile 3 si 4. Apoi, schimbamculoarea si pornim cu varful 2, colorandu-l in albastru. Mai putemcolora cu albastru si varful 5. Deci, ne-au fost suficiente douaculori. Daca coloram varfurile in ordinea 1, 5, 2, 3, 4, atunci seobtine o colorare cu trei culori.

Rezulta ca, prin metoda greedy, nu obtinem decat o solutieeuristica, care nu este in mod necesar solutia optima a problemei. Dece suntem atunci interesati intr-o astfel de rezolvare? Totialgoritmii cunoscuti, care rezolva optim aceasta problema, suntexponentiali, deci, practic, nu pot fi folositi pentru cazuri mari.Algoritmul greedy euristic propus furnizeaza doar o solutie“acceptabila”, dar este simplu si eficient.

Un caz particular al problemei colorarii unui graf corespundecelebrei probleme a colorarii hartilor: o harta oarecare trebuie colorata cuun numar minim de culori, astfel incat doua tari cu frontiera comunasa fie colorate diferit. Daca fiecarui varf ii corespunde o tara, iardoua varfuri adiacente reprezinta tari cu frontiera comuna, atuncihartii ii corespunde un graf planar, adica un graf care poate fidesenat in plan fara ca doua muchii sa se intersecteze. Celebritateaproblemei consta in faptul ca, in toate exemplele intalnite, colorareas-a putut face cu cel mult 4 culori. Aceasta in timp ce, teoretic, seputea demonstra ca pentru o harta oarecare este nevoie de cel mult 5culori.

Problema colorarii unui graf poate fi interpretata si incontextul planificarii unor activitati. De exemplu, sa presupunem cadorim sa executam simultan o multime de activitati, in cadrul unorsali de clasa. In acest caz, varfurile grafului reprezinta activitati,iar muchiile unesc activitatile incompatibile. Numarul minim de culorinecesare pentru a colora graful corespunde numarului minim de salinecesare.

11.6.2 Problema comis-voiajorului

Se cunosc distantele dintre mai multe orase. Un comis-voiajorpleaca dintr-un oras si doreste sa se intoarca in acelasi oras, dupace a vizitat fiecare din celelalte orase exact o data. Problema estede a minimiza lungimea drumului parcurs. Si pentru aceasta problema,toti algoritmii care gasesc solutia optima sunt exponentiali.

Problema poate fi reprezentata printr-un graf neorientat, in careoricare doua varfuri diferite ale grafului sunt unite intre ele

155

Page 154: Algoritmi

printr-o muchie, de lungime nenegativa. Cautam un ciclu de lungimeminima, care sa se inchida in varful initial si care sa treaca printoate varfurile grafului.

Conform strategiei greedy, vom construi ciclul pas cu pas,adaugand la fiecare iteratie cea mai scurta muchie disponibila cuurmatoarele proprietati:

nu formeaza un ciclu cu muchiile deja selectate (exceptandpentru ultima muchie aleasa, care completeaza ciclul)

nu exista inca doua muchii deja selectate, astfel incat celetrei muchii sa fie incidente in acelasi varf

La: 2 3 4 5 6

De la:1 3 10 11 7 252 6 12 8 263 9 4 204 5 155 18

Tabelul 11.4 Matricea distantelor pentruproblema comis-voiajorului.

De exemplu, pentru sase orase a caror matrice a distantelor estedata 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 gasitciclul optim, deoarece ciclul (1, 2, 3, 6, 4, 5, 1) are lungimea 56.

156

Page 155: Algoritmi

CAPITOLUL XII

STUDII DE CAZ – APLICAŢII

157

Page 156: Algoritmi

1. Să se determine toate numerele perechile de numere gemene pana la oanumita valoare n. Două numere sunt gemene dacă sunt ambele prime şi diferenţa 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

158

Page 157: Algoritmi

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

2. Să se citească o valoare naturala n cu valori cuprinse intre 1 şi 100.

Sub cit_n(mes As String, nnn As Integer) Do nnn = InputBox(mes, y) Loop Until n > 0 And n < 100End Sub

3. Citirea unui vector cu n componente

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) NextEnd Sub

4. Tipărirea unui tablou cu n componente

Sub tipar(mes As String, n As Integer, a As vector) sir = "" For i = 1 To n sir = sir + Str$(a.v(i)) + "," Next MsgBox mes + " " + sirEnd Sub

5. Generarea permutărilor utilizănd metoda backtracking

159

Page 158: Algoritmi

Private Sub CommandButton14_Click() cit_n "n = ", n back_permEnd Sub

6. Generarea produsului cartezian a n mulţimi utilizând metoda backtracking

Private Sub CommandButton16_Click() Dim a As vector cit_n "n=", n cit_date "a", n, a tipar " multimile sunt : ", n, a back_prod_cartEnd Sub

7. Generarea permutărilor utilizănd metoda backtracking

Private Sub CommandButton17_Click() cit_n "n = ", n cit_n "p = ", p back_aranjEnd Sub

8. “Problema celor n dame” utilizănd metoda backtracking

Private Sub CommandButton15_Click() cit_n "n = ", n backEnd Sub

9. Generarea combinărilor (de n luate câte m) utilizănd metoda backtracking

Private Sub CommandButton18_Click() cit_n "n = ", n cit_n "p = ", p back_combEnd Sub

10. Generarea partiţiilor unei mulţimi utilizănd metoda backtracking

Private Sub CommandButton19_Click() cit_n "n=", n back_partitiiEnd Sub

160

Page 159: Algoritmi

11. Căutarea binară utilizând 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 IfEnd Sub

12. Realizarea unei subrutine pentru sortarea rapidă “Quicksort”

Sub sort(p As Integer, q As Integer, a As vector) Dim m As Integer If a.v(p) > a.v(q) Then m = a.v(p) a.v(p) = a.v(q) a.v(q) = m End IfEnd Sub

13. Sortarea “Merge-Sort” utilizând metoda “Divide et impera”

161

Page 160: Algoritmi

Sub interc(p As Integer, q As Integer, m As Integer, a As vector) Dim b As vector, i, j, k As Integer i = p j = m + 1 k = 1 While (i <= m) And (j <= q) If a.v(i) <= a.v(j) Then b.v(k) = a.v(i) 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 NextEnd Sub

14. Sortarea rapidă utilizând metoda “Divide et impera”

Sub divimp(p As Integer, q As Integer, a As vector) Dim m As Integer If (q - p) <= 1 Then sort p, q, a Else m = Int((p + q) / 2) divimp p, m, a divimp m + 1, q, a interc p, q, m, a End If

162

Page 161: Algoritmi

End Sub

15. Problema colorării hărţilor utilizând metoda backtracking

Private Sub CommandButton20_Click() Dim mat As matrice cit_n " n = ", n cit_mat "a", n, n, mat tipar_mat "a", n, n, mat For i = 1 To n For j = 1 To n mat.m(j, i) = mat.m(i, j) Next Next back_colEnd Sub

16. Interclasarea a 2 şiruri ordonate crescător

Private Sub CommandButton3_Click() Dim n As Integer, x As Integer, a As vector, m As Integer, b As vector, k As Integer, c 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 cit_n "m = ", m cit_date "a", m, b tipar "sirul dat este : ", m, b divimp 1, m, b 'MsgBox "Sirul a sortat este" tipar "Sirul b sortat este", m, b i = 1 j = 1 k = 0 While i <= n And j <= m If a.v(i) < b.v(j) Then k = k + 1 c.v(k) = a.v(i) i = i + 1 Else If a.v(i) = b.v(j) Then k = k + 1

163

Page 162: Algoritmi

c.v(k) = a.v(i) i = i + 1 j = j + 1 Else k = k + 1 c.v(k) = b.v(j) j = j + 1 End If End If Wend If i <= n Then For l = i To n k = k + 1 c.v(k) = a.v(l) Next End If If j <= m Then For l = j To m k = k + 1 c.v(k) = b.v(l) Next End If tipar "A U B = ", k, cEnd Sub

17. Sortarea Shell-Sort utilizând metoda Greedy

Private Sub CommandButton4_Click() Dim n As Integer, k As Integer, a As vector cit_n "n = ", n cit_date "a", n, a tipar "sirul dat este : ", n, a k = n Do k = k / 2 Do b = 1 For i = 1 To n - k If a.v(i) > a.v(i + k) Then x = a.v(i) a.v(i) = a.v(i + k) a.v(i + k) = x b = 0 End If Next Loop Until Not (b = 0) Loop Until Not (k <> 1)

164

Page 163: Algoritmi

'MsgBox "Sirul a sortat este" tipar "Sirul a sortat este", n, aEnd Sub

18. Citirea si scrierea unei matrici pe ecran

Private Sub CommandButton5_Click() Dim n As Integer, m As Integer, a As matrice, b As matrice, p As Integer, c As matrice cit_n "n = ", n cit_n "m = ", m cit_mat "a", n, m, a tipar_mat "a", n, m, aEnd Sub

19. Citirea unei matrici de pe dispozitivul de intrare

Sub cit_mat(mes As String, n As Integer, m As Integer, a As matrice) For i = 1 To n For j = 1 To m a.m(i, j) = InputBox(mes + "(" + Str$(i) + "," + Str$(j) + ")=", y) Next NextEnd Sub

20. Scrierea unei matrici pe ecran

Sub tipar_mat(mes As String, n As Integer, m As Integer, a As matrice) sir = mes + Chr(10) For i = 1 To n For j = 1 To m sir = sir + Str$(a.m(i, j)) + " " Next sir = sir + Chr(10) Next MsgBox sirEnd Sub

21. Produsul a două matrici

Private Sub CommandButton6_Click() Dim n As Integer, m As Integer, a As matrice, b As matrice, p As Integer, c As matrice

165

Page 164: Algoritmi

cit_n "n = ", n cit_n "m = ", m cit_mat "a", n, m, a tipar_mat "a", n, m, a cit_n "p = ", p 'cit_n "m = ", m cit_mat "b", m, p, b tipar_mat "m", m, p, b prod_mat n, m, p, a, b, c tipar_mat "axb=", n, p, cEnd Sub

Sub prod_mat(n As Integer, m As Integer, p As Integer, a As matrice, b As matrice, c As matrice) For i = 1 To n For j = 1 To p c.m(i, j) = 0 For k = 1 To m c.m(i, j) = c.m(i, j) + a.m(i, k) * b.m(k, j) Next Next NextEnd Sub

22. Programul principal pentru adunarea a două matrici

Private Sub CommandButton7_Click() Dim n As Integer, m As Integer, a As matrice, b As matrice, p As Integer, c As matrice cit_n "n = ", n cit_n "m = ", m cit_mat "a", n, m, a tipar_mat "a", n, m, a 'cit_n "p = ", p 'cit_n "m = ", m cit_mat "b", n, m, b tipar_mat "b", n, m, b ad_mat n, m, a, b, c tipar_mat "a+b=", n, m, cEnd Sub

23. Subrutina pentru adunarea a două matrici

Sub ad_mat(n As Integer, m As Integer, a As matrice, b As matrice,c As matrice) For i = 1 To n For j = 1 To m

166

Page 165: Algoritmi

c.m(i, j) = a.m(i, j) + b.m(i, j) Next NextEnd Sub

24. Programul principal pentru scăderea a două matrici

Private Sub CommandButton8_Click() Dim n As Integer, m As Integer, a As matrice, b As matrice, p As Integer, c As matrice cit_n "n = ", n cit_n "m = ", m cit_mat "a", n, m, a tipar_mat "a", n, m, a 'cit_n "p = ", p 'cit_n "m = ", m cit_mat "b", n, m, b tipar_mat "b", n, m, b scad_mat n, m, a, b, c tipar_mat "a-b=", n, m, cEnd Sub

25. Subrutina pentru adunarea a două matrici

Sub scad_mat(n As Integer, m As Integer, a As matrice, b As matrice, c As matrice) For i = 1 To n For j = 1 To m c.m(i, j) = a.m(i, j) - b.m(i, j) Next NextEnd Sub

26. Programul principal pentru ridicarea unei matrici la o putere p

Private Sub CommandButton9_Click() Dim n As Integer, m As Integer, a As matrice, b As matrice, p As Integer, c As matrice, k As Integer 'Sub scad_mat(n As Integer, m As Integer, a As matrice, b As matrice, c As matrice) 'const t as vector ={0,31,28,31,30,31,30,31,30,31,30,31,30} cit_n "n = ", n 'cit_n "m = ", m cit_mat "a", n, n, a tipar_mat "a", n, n, a cit_n "putere = ", k

167

Page 166: Algoritmi

'cit_n "m = ", m 'cit_mat "b", n, m, b 'tipar_mat "b", n, m, b putere_mat n, a, k, c tipar_mat "a^p=", n, n, cEnd Sub

27. Subprogramul pentru ridicarea unei matrici la o putere p

Sub putere_mat(n As Integer, a As matrice, k As Integer, c As matrice) Dim b As matrice, c1 As matrice For i = 1 To n For j = 1 To n 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

168

Page 167: Algoritmi

NextEnd Sub

28. Subrutina de iniţializare a stivei pentru metoda backtracking

Sub init(k As Integer, st As stiva) st.ss(k) = 0End Sub

29. Subrutina successor pentru “problema celor n dame”

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 IfEnd Sub

30. Subrutina successor pentru generarea combinărilor

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 IfEnd Sub

31. Subrutina succesor pentru problema “produsului cartezian a n mulţimi” utilizând 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 IfEnd Sub

32. Subrutina successor pentru colorarea hărţilor

Sub succesor_col(am_suc As Boolean, st As stiva, k As Integer)

169

Page 168: Algoritmi

If st.ss(k) < 4 Then am_suc = True st.ss(k) = st.ss(k) + 1 Else am_suc = False End IfEnd Sub

33. Subrutina valid pentru “problema celor n dame”

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 NextEnd Sub

34. Subrutina valid pentru colorarea hărţilor

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 NextEnd 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 IfEnd Sub

170

Page 169: Algoritmi

35. Subrutina valid pentru “produs cartezian a n mulţimi”

Sub valid_prod(ev As Boolean, st As stiva, k As Integer) ev = TrueEnd Sub

36. Subrutina soluţie pentru generarea permutărilor

Function solutie(k As Integer) As Boolean If k = n Then solutie = True Else solutie = False End IfEnd Function

37. Subrutina soluţie pentru generarea aranjamentelor sau combinărilor

Function solutie1(k As Integer) As Boolean If k = p Then solutie1 = True Else solutie1 = False End IfEnd Function

38. Subrutina tipărire pentru “problema celor n dame”

Sub tiparr() Dim i As Integer, b As String b = " " For i = 1 To n b = b + "(" + Str$(i) + "," + Str$(st.ss(i)) + ")," Next MsgBox bEnd Sub

39. Subrutina tipărire pentru “colorarea hărţilor”

Sub tipar_col() Dim i As Integer, b As String b = " " For i = 1 To nb = b + "Tara = " + Str$(i) + "; culoarea " + Str$(st.ss(i)) + " " Next MsgBox b

171

Page 170: Algoritmi

End Sub

40. Subrutina back pentru “problema celor n dame”

Sub back() Dim k As Integer k = 1 init k, st 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 tiparr Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd Sub

41. Programul principal pentru “problema celor n dame”

Sub Button2_Click() n = InputBox("n=", ib_title) backEnd Sub

42. Subrutina back pentru “generarea permutărilor”

Sub back_perm() Dim k As Integer k = 1 init k, st While k > 0 Do succesor am_suc, st, k If am_suc = True Then valid1 ev, st, k End If

172

Page 171: Algoritmi

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 WendEnd Sub

43. Subrutina back pentru “generarea aranjamentelor”

Sub back_aranj() Dim k As Integer k = 1 init k, st While k > 0 Do succesor am_suc, st, k If am_suc = True Then valid1 ev, st, k End If Loop Until (Not am_suc) Or (am_suc And ev) If am_suc Then If solutie1(k) Then tipar_rr Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd Sub

44. Subrutina valid pentru metoda backtracking

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

173

Page 172: Algoritmi

End If NextEnd Sub

45. Subrutina tipar pentru metoda backtracking

Sub tipar_r() Dim i As Integer, b As String b = " " For i = 1 To n b = b + Str$(st.ss(i)) + "," Next MsgBox bEnd Sub

46. Subrutina tipar pentru metoda backtracking

Sub tipar_rr() Dim i As Integer, b As String b = " " For i = 1 To p b = b + Str$(st.ss(i)) + "," Next MsgBox bEnd Sub

47. Subrutina back pentru “generarea combinărilor”

Sub back_comb() Dim k As Integer k = 1 init k, st While k > 0 Do succesor_c am_suc, st, k If am_suc = True Then valid_c ev, st, k End If Loop Until (Not am_suc) Or (am_suc And ev) If am_suc Then If solutie1(k) Then tipar_rr Else k = k + 1 init k, st End If Else

174

Page 173: Algoritmi

k = k - 1 End If WendEnd Sub

48. Subrutina back pentru “generarea produsului cartezian a n multimi”

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 WendEnd Sub

49. Subrutina back pentru “generarea partiţiilor unei mulţimi”

Sub back_partitii() Dim k As Integer k = 1 init k, st While k > 0 Do succesor_part 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_part

175

Page 174: Algoritmi

Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd Sub

50. Subrutina tiparire pentru problema “generare partiţii” a unei mulţimi

Sub tipar_part() Dim i As Integer, max As Integer, j As Integer, sir As String sir = "" max = st.ss(1) For i = 2 To n If max < st.ss(i) Then max = st.ss(i) End If Next sir = " PARTITII " For j = 1 To max For i = 1 To n If st.ss(i) = j Then sir = sir + Str$(i) + " " End If Next sir = sir + Chr(10) Next MsgBox sirEnd Sub

51. Subrutina succesor pentru problema “generare partiţii” a unei mulţimi

Sub succesor_part(am_suc As Boolean, st As stiva, k As Integer) Dim i As Integer, max As Integer If k = 1 Then max = 1 Else max = st.ss(1) For i = 2 To k - 1 If max < st.ss(i) Then max = st.ss(i) End If

176

Page 175: Algoritmi

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 IfEnd Sub

52. Subrutina back pentru “colorarea hărţilor”

Sub back_col() Dim k As Integer k = 1 init k, st While k > 0 Do succesor_col am_suc, st, k If am_suc = True Then valid_col ev, st, k End If Loop Until (Not am_suc) Or (am_suc And ev) If am_suc Then If solutie(k) Then tipar_col Else k = k + 1 init k, st End If Else k = k - 1 End If WendEnd SubPublic s As String

53. Funcţia pentru a verifica dacă un număr 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

177

Page 176: Algoritmi

Next prim = bEnd Function

54. Programul principal pentru inversarea unui număr natural n

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 inversateste: " + sirEnd 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 IfEnd Sub

56. Sortarea unui sir cu n componente utilizând metoda bulelor

Private Sub Buton11_Click()178

Page 177: Algoritmi

Dim n As Integer, a As vector cit_n "n = ", n cit_date "a", n, a tipar "vectorul initial a este ", n, a bule n, a tipar "vectorul a sortat este : ", n, aEnd Sub

57. Subrutina pentru sortarea prin metoda bulelor

Sub bule(n As Integer, a As vector) Do k = 0 For i = 1 To n - 1 If a.v(i) > a.v(i + 1) Then x = a.v(i) a.v(i) = a.v(i + 1) a.v(i + 1) = x k = 1 End If Next Loop Until k = 0End Sub

58. Sortarea unui sir cu n componente utilizând metoda selecţiei directe

Private Sub Buton12_Click() Dim n As Integer, a As vector cit_n "n = ", n cit_date "a", n, a tipar "vectorul initial a este ", n, a selectie n, a tipar "vectorul a sortat este : ", n, aEnd Sub

59. Subrutina pentru sortarea prin metoda selecţiei directe

Sub selectie(n As Integer, a As vector) For i = 1 To n - 1 min = a.v(i) k = i For j = i + 1 To n If min > a.v(j) Then min = a.v(j) k = j End If

179

Page 178: Algoritmi

Next If k <> i Then x = a.v(i) a.v(i) = a.v(k) a.v(k) = x End If NextEnd Sub

60. Sortarea unui sir cu n componente utilizând metoda prin numărare

Private Sub Buton14_Click() Dim n As Integer, a As vector cit_n "n = ", n cit_date "a", n, a tipar "vectorul initial a este ", n, a numarare n, a tipar "vectorul a sortat este : ", n, aEnd Sub

61. Suma cifrelor unui număr natural dat n

Sub buton2_Click() Dim n As Integer, s As Long, n1 As Integer Do n = InputBox(" n = ", y) Loop Until n > 0 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

62. Verificarea unui numar natural n daca este prim sau nu

Sub buton3_Click() Dim n As Integer, s As Long, n1 As Integer Do n = InputBox(" n = ", y) Loop Until n > 0 n1 = n b = True For i = 2 To Int(Sqr(n))

180

Page 179: Algoritmi

If n Mod i = 0 Then b = False i = Int(Sqr(n)) End If Next If b = True Then MsgBox "numarul n = " + Str$(n) + " este prim" Else MsgBox "numarul n = " + Str$(n) + " nu este prim" End IfEnd Sub

63. Determinarea numerelor prime mai mici sau egale cu n utilizând 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 : " + sirEnd Sub

64. Ciurul lui Eratostene

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))

181

Page 180: Algoritmi

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 : " + sirEnd Sub

65. Descompunerea unui numar in factori primi

Sub buton6_Click() Dim n As Integer, a As vector, sir As String, n1 As Integer Do n = InputBox(" n = ", y) Loop Until n > 0 i = 2 n1 = n l = 0 sir = "" Do fm = 0 While n Mod i = 0 fm = fm + 1 l = 1 n = Int(n / i) Wend If fm <> 0 Then sir = sir + Str$(i) + "^" + Str$(fm) + "*" End If i = i + 1 Loop Until n = 1 If l = 0 Then sir = Str$(n) + "^1" End If MsgBox Str$(n1) + "=" + sirEnd Sub

66. Scrierea unui număr ca suma a două cuburi182

Page 181: Algoritmi

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 NextEnd Sub

67. CMMDC a două numere utilizând recursivitatea

Sub buton8_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 = euclid(a, b) If c = 1 Then MsgBox " nr. sunt prime intre ele (" + Str$(a1) + "," + Str$(b1)+ ")" Else

183

Page 182: Algoritmi

MsgBox "Cmmdc (" + Str$(a1) + "," + Str$(b1) + ")=" + Str$(euclid(a, b)) End IfEnd Sub

68. Funcţia euclid

Function euclid(a As Integer, b As Integer) As Integer Dim r As Integer Do r = a Mod b MsgBox r a = b b = r Loop Until Not (r = 0 And r = 1) If r = 1 Then euclid = 1 Else euclid = a End IfEnd Function

69. CMMDC a două numere utilizând scăderi repetate

Private Sub Buton9_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 = euclid1(a, b) If c = 1 Then MsgBox " nr. sunt prime intre ele (" + Str$(a1) + "," + Str$(b1)+ ")" Else MsgBox "Cmmdc (" + Str$(a1) + "," + Str$(b1) + ")=" + Str$(euclid1(a, b)) End IfEnd Sub

70. Funcţia Euclid utilizând scăderi repetate

Function euclid1(a As Integer, b As Integer) As Integer If a > b Then euclid1 = euclid1(a - b, b)

184

Page 183: Algoritmi

Else If a < b Then euclid1 = euclid1(a, b - a) Else euclid1 = a End If End IfEnd Function

71. Funcţia Euclid utilizând scăderi repetate

Function euclid2(a As Integer, b As Integer) As Integer If b = 0 Then euclid2 = a Else euclid2 = euclid2(b, a Mod b) End IfEnd Function

72. x ^ y utilizând un număr minim de înmulţiri

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 tEnd Sub

73. Verifică dacă un număr natural este palindrome sau nu

Sub Button16_Click() Dim n As Long, m As Long Do n = InputBox("n=", ib_title) Loop Until (n > 0) m = n

185

Page 184: Algoritmi

If palindrom(n) = True Then MsgBox "n=" + Str$(m) + " este plaindrom" Else MsgBox "n=" + Str$(m) + " nu este plaindrom" End IfEnd Sub

74. Baza la exponent

Sub Button17_Click() Dim x As Double, y As Byte, z As Double, t As Byte Do x = InputBox("baza=", ib_title) y = InputBox("exponent=", ib_title) Loop Until (x > 0) And (y > 0) z = putere(x, y, t) MsgBox Str$(z) + " " + Str$(t - 1)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, aEnd Sub

76. Minimul dintr-un şir de numere utilizând divide et impera

Sub Button19_Click() Dim n As Integer, a As vector cit_n "n=", n cit_date "a", n, a 'MsgBox "Sirul a este" tipar "sirul dat este ", n, a MsgBox "minimul in Sirul a este" + Str$(minim(1, n))End Sub

77. Turnurile din Hanoi

Sub Button20_Click()186

Page 185: Algoritmi

Dim n As Integer, a As sir, b As sir, c As sir d = "" a.s = "A" b.s = "B" c.s = "C" n = InputBox("n=", ib_title) hanoi n, a, b, c MsgBox dEnd Sub

78. Subrutina Hanoi

Sub hanoi(n As Integer, a As sir, b As sir, c As sir) If n = 1 Then d = d + "(" + a.s + "->" + b.s + ")," Else hanoi n - 1, a, c, b d = d + "(" + a.s + "->" + b.s + ")," hanoi n - 1, c, b, a End IfEnd Sub

79. Subrutina back pentru permutări

Sub back_perm() Dim k As Integer k = 1 init k, st While k > 0 Do succesor am_suc, st, k If am_suc = True Then valid1 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 WendEnd Sub

187

Page 186: Algoritmi

80. Calculul sumei 1-1,1-1-1,………….,1-1-1-1-1-1-1…….-1

Private Sub Buttton3_Click() Dim n As Integer, ss As String cit_n "n = ", n ss = "" i = 0 j = 1 While (i < n) ss = ss + " 1" i = i + 1 k = 1 While k <= j And i < n ss = ss + " -1" i = i + 1 k = k + 1 Wend j = j + 1 Wend MsgBox ssEnd Sub

188

Page 187: Algoritmi

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”, ComputerScience 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”, EdituraTehnica, Bucuresti, 1976.[8.] Lippman, S. B. “C++ Primer”, Addison-Wesley, Reading, 1989.[9.] Livovschi, L., Georgescu, H. “Sinteza si analiza algoritmilor”, EdituraStiintifica 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 PublishingCompany, 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/

189