o8d1dl
1.1 Algoritm, program, programare
Aparitia primelor calculatoare electronice a constituit un salt urias in
directia automatizarii activitatii umane. Nu exista astazi domeniu de activitate
in care calculatorul sa nu isi arate utilitatea.
Calculatoarele pot fi folosite pentru a rezolva probleme, numai daca pentru
rezolvarea acestora se concep programe corespunzatoare de rezolvare. Termenul
de program (programare) a suferit schimbari in scurta istorie a informaticii.
Prin anii '60 problemele rezolvate cu ajutorul calculatorului erau simple si
se gaseau algoritmi nu prea complicati pentru rezolvarea lor. Prin program se
intelegea rezultatul scrierii unui algoritm intr?un limbaj de programare.
Din cauza cresterii complexitatii problemelor, astazi pentru rezolvarea unei
probleme adesea vom concepe un sistem de mai multe programe.
Dar ce este un algoritm? O definitie matematica, riguroasa, este greu de dat,
chiar imposibila fara a introduce si alte notiuni. Vom incerca in
continuare o descriere a ceea ce se intelege prin algoritm.
Ne vom familiariza cu aceasta notiune prezentand mai multe exemple de
algoritmi si observand ce au ei in comun. Cel mai vechi exemplu
este algoritmul lui Euclid, algoritm care determina cel mai mare divizor comun
a doua numere naturale. Evident, vom prezenta mai multi algoritmi, cei mai multi
fiind legati de probleme accesibile absolventilor de liceu.
Vom constata ca un algoritm este un text finit, o secventa finita de propozitii
ale unui limbaj. Din cauza ca este inventat special in acest scop, un
astfel de limbaj este numit limbaj de descriere a algoritmilor. Fiecare propozitie
a limbajului precizeaza o anumita regula de calcul, asa cum se va observa atunci
cand vom prezenta limbajul Pseudocod.
Oprindu-ne la semnificatia algoritmului, la efectul executiei lui, vom observa
ca fiecare algoritm defineste o functie matematica. De asemenea, din toate sectiunile
urmatoare va reiesi foarte clar ca un algoritm este scris pentru rezolvarea
unei probleme. Din mai multe exemple se va observa insa ca, pentru rezolvarea
aceleasi probleme, exista mai multi algoritmi.
Pentru fiecare problema P exista date presupuse cunoscute (date initiale pentru
algoritmul corespunzator, A) si rezultate care se cer a fi gasite (date finale).
Evident, problema s-ar putea sa nu aiba sens pentru orice date initiale. Vom
spune ca datele pentru care problema P are sens fac parte din domeniul D al
algoritmului A. Rezultatele obtinute fac parte dintr-un domeniu R, astfel ca
executand algoritmul A cu datele de intrare xID vom obtine rezultatele
rIR. Vom spune ca A(x)=r si astfel algoritmul A defineste o functie
A : D ---> R .
Algoritmii au urmatoarele caracteristici: generalitate, finitudine si unicitate.
Prin generalitate se intelege faptul ca un algoritm este aplicabil pentru
orice date initiale xID. Deci un algoritm A nu rezolva problema P cu niste
date de intrare, ci o rezolva in general, oricare ar fi aceste date. Astfel,
algoritmul de rezolvare a unui sistem liniar de n ecuatii cu n necunoscute prin
metoda lui Gauss, rezolva orice sistem liniar si nu un singur sistem concret.
Prin finitudine se intelege ca textul algoritmului este finit, compus
dintr-un numar finit de propozitii. Mai mult, numarul transformarilor ce trebuie
aplicate unei informatii admisibile xID pentru a obtine rezultatul final
corespunzator este finit.
Prin unicitate se intelege ca toate transformarile prin care trece informatia
initiala pentru a obtine rezultatul rIR sunt bine determinate de regulile
algoritmului. Aceasta inseamna ca fiecare pas din executia algoritmului
da rezultate bine determinate si precizeaza in mod unic pasul urmator.
Altfel spus, ori de cate ori am executa algoritmul, pornind de la aceeasi
informatie admisibila xID, transformarile prin care se trece si rezultatele
obtinute sunt aceleasi.
In descrierea algoritmilor se folosesc mai multe limbaje de descriere,
dintre care cele mai des folosite sunt:
? limbajul schemelor logice;
? limbajul Pseudocod.
In continuare vom folosi pentru descrierea algoritmilor limbajul Pseudocod
care va fi definit in cele ce urmeaza. In ultima vreme schemele
logice sunt tot mai putin folosite in descrierea algoritmilor si nu sunt
deloc potrivite in cazul problemelor complexe. Prezentam insa si
schemele logice, care se mai folosesc in manualele de liceu, intrucat
cu ajutorul lor vom preciza in continuare semantica propozitiilor Pseudocod.
1.2 Scheme logice
Schema logica este un mijloc de descriere a algoritmilor prin reprezentare
grafica. Regulile de calcul ale algoritmului sunt descrise prin blocuri (figuri
geometrice) reprezentand operatiile (pasii) algoritmului, iar ordinea
lor de aplicare (succesiunea operatiilor) este indicata prin sageti. Fiecarui
tip de operatie ii este consacrata o figura geometrica (un bloc tip) in
interiorul careia se va inscrie operatia din pasul respectiv.
Prin executia unui algoritm descris printr-o schema logica se intelege
efectuarea tuturor operatiilor precizate prin blocurile schemei logice, in
ordinea indicata de sageti.
In descrierea unui algoritm, deci si intr-o schema logica, intervin
variabile care marcheaza atat datele cunoscute initial, cat si rezultatele
dorite, precum si alte rezultate intermediare necesare in rezolvarea problemei.
Intrucat variabila joaca un rol central in programare este
bine sa definim acest concept. Variabila defineste o marime care isi poate
schimba valoarea in timp. Ea are un nume si, eventual, o valoare. Este
posibil ca variabila inca sa nu fi primit valoare, situatie in care
vom spune ca ea este neinitializata. Valorile pe care le poate lua variabila
apartin unei multimi D pe care o vom numi domeniul variabilei. In concluzie
vom intelege prin variabila tripletul
(nume, domeniul D, valoare) unde valoare apartine multimii D È AnedefinitS.
Blocurile delimitatoare Start si Stop (Fig.1.2.1.a si 1.2.1.b) vor marca inceputul
respectiv sfarsitul unui algoritm dat printr-o schema logica. Descrierea
unui algoritm prin schema logica va incepe cu un singur bloc Start si
se va termina cu cel putin un bloc Stop.
Blocurile de intrare/iesire Citeste si Tipareste (Fig. 1.2.1.c si d) indica
introducerea unor Date de intrare respectiv extragerea unor Rezultate finale.
Ele permit precizarea datelor initiale cunoscute in problema si tiparirea
rezultatelor cerute de problema. Blocul Citeste initializeaza variabilele din
lista de intrare cu valori corespunzatoare, iar blocul Tipareste va preciza
rezultatele obtinute (la executia pe calculator cere afisarea pe ecran a valorilor
expresiilor din lista de iesire).
Blocurile de atribuire (calcul) se utilizeaza in descrierea operatiilor
de atribuire (:=). Printr-o astfel de operatie, unei variabile var i se atribuie
valoarea calculata a unei expresii expr (Fig.1.2.1.e).
Fig.1.2.1. Blocurile schemelor logice
Blocurile de decizie marcheaza punctele de ramificatie ale algoritmului in
etapa de decizie. Ramificarea poate fi dubla (blocul logic, Fig.1.2.1.f) sau
tripla (blocul aritmetic, Fig. 1.2.1.g). Blocul de decizie logic indica ramura
pe care se va continua executia algoritmului in functie de indeplinirea
(ramura Da) sau neindeplinirea (ramura Nu) unei conditii. Conditia care
se va inscrie in blocul de decizie logic va fi o expresie logica
a carei valoare poate fi una dintre valorile "adevarat" sau "fals".
Blocul de decizie aritmetic va hotari ramura de continuare a algoritmului
in functie de semnul valorii expresiei aritmetice inscrise in
acest bloc, care poate fi negativa, nula sau pozitiva.
Blocurile de conectare marcheaza intreruperile sagetilor de legatura dintre
blocuri, daca din diverse motive s-au efectuat astfel de intreruperi (Fig.1.2.1.h).
Pentru exemplificare vom da in continuare doua scheme logice, corespunzatoare
unor algoritmi pentru rezolvarea problemelor P1.2.1 si P1.2.2.
P1.2.1. Sa se rezolve ecuatia de grad doi aX2+bX+c=0 (a,b,cIR _i a¹0).
Metoda de rezolvare a ecuatiei de gradul doi este cunoscuta. Ecuatia poate avea
radacini reale, respectiv complexe, situatie recunoscuta dupa semnul discriminantului
d = b2 - 4ac.
Algoritmul de rezolvare a problemei va citi mai intai datele problemei,
marcate prin variabilele a, b si c. Va calcula apoi discriminantul d si va continua
in functie de valoarea lui d, asa cum se poate vedea in fig.1.2.2.
Fig.1.2.2. Algoritm pentru rezolvarea ecuatiei de gradul doi.
P1.2.2. Sa se calculeze suma elementelor pozitive ale unui sir de numere reale
dat.
Schema logica (data in Fig.1.2.3) va contine imediat dupa blocul START
un bloc de citire, care precizeaza datele cunoscute in problema, apoi
o parte care calculeaza suma ceruta si un bloc de tiparire a sumei gasite, inaintea
blocului STOP. Partea care calculeaza suma S ceruta are un bloc pentru initializarea
cu 0 a acestei sume, apoi blocuri pentru parcurgerea numerelor: x1, x2…xn
si adunarea celor pozitive la suma S. Pentru aceasta parcurgere se foloseste
o variabila contor i, care este initializata cu 1 si creste mereu cu 1 pentru
a atinge valoarea n, indicele ultimului numar dat.
Fig.1.2.3. Algoritm pentru calculul unei sume.
Schemele logice dau o reprezentare grafica a algoritmilor cu ajutorul unor
blocuri de calcul. Executia urmeaza sensul indicat de sageata, putand
avea loc reveniri in orice punct din schema logica. Din acest motiv se
poate obtine o schema logica incalcita, greu de urmarit. Rezulta
importanta compunerii unor scheme logice structurate (D-scheme, dupa Djikstra),
care sa contina numai anumite structuri standard de calcul si in care
drumurile de la START la STOP sa fie usor de urmarit.
1.3. Limbajul PSEUDOCOD
Limbajul Pseudocod este un limbaj inventat in scopul proiectarii algoritmilor
si este format din propozitii asemanatoare propozitiilor limbii romane,
care corespund structurilor de calcul folosite in construirea algoritmilor.
Acesta va fi limbajul folosit de noi in proiectarea algoritmilor si va
fi definit in cele ce urmeaza. Tinand seama ca obtinerea unui algoritm
pentru rezolvarea unei probleme nu este intotdeauna o sarcina simpla,
ca in acest scop sunt folosite anumite metode pe care le vom descrie in
capitolele urmatoare, in etapele intermediare din obtinerea algoritmului
vom folosi propozitii curente din limba romana. Acestea sunt considerate
elemente nefinisate din algoritm, asupra carora trebuie sa se revina si le vom
numi propozitii nestandard. Deci limbajul Pseudocod are doua tipuri de propozitii:
propozitii standard, care vor fi prezentate fiecare cu sintaxa si semnificatia
(semantica) ei si propozitii nestandard. Asa cum se va arata mai tarziu,
propozitiile nestandard sunt texte care descriu parti ale algoritmului inca
incomplet elaborate, nefinisate, asupra carora urmeaza sa se revina.
Pe langa aceste propozitii standard si nestandard, in textul algoritmului
vom mai introduce propozitii explicative, numite comentarii. Pentru a le distinge
de celelalte propozitii, comentariile vor fi inchise intre acolade.
Rolul lor va fi explicat putin mai tarziu.
Propozitiile standard ale limbajului Pseudocod folosite in aceasta lucrare,
corespund structurilor de calcul prezentate in figura 1.3.1 si vor fi
prezentate in continuare. Fiecare propozitie standard incepe cu
un cuvant cheie, asa cum se va vedea in cele ce urmeaza. Pentru
a deosebi aceste cuvinte de celelalte denumiri, construite de programator, in
acest capitol vom scrie cuvintele cheie cu litere mari. Mentionam ca si propozitiile
simple se termina cu caracterul ';' in timp ce propozitiile compuse, deci
cele in interiorul carora se afla alte propozitii, au un marcaj de sfarsit
propriu. De asemenea, mentionam ca propozitiile limbajului Pseudocod vor fi
luate in seama in ordinea intalnirii lor in text,
asemenea oricarui text al limbii romane.
Prin executia unui algoritm descris in Pseudocod se intelege efectuarea
operatiilor precizate de propozitiile algoritmului, in ordinea citirii
lor.
In figura 1.3.1, prin A, B s-au notat subscheme logice, adica secvente
de oricate structuri construite conform celor trei reguli mentionate in
continuare.
Structura secventiala (fig.1.3.1.a) este redata prin concatenarea propozitiilor,
simple sau compuse, ale limbajului Pseudocod, care vor fi executate in
ordinea intalnirii lor in text. Propozitiile simple din limbajul
Pseudocod sunt CITESTE, TIPARESTE, FIE si apelul de subprogram. Propozitiile
compuse corespund structurilor alternative si repetitive.
Structura alternativa (fig.1.3.1.b) este redata in Pseudocod prin propozitia
DACA, prezentata in sectiunea 1.3.2, iar structura repetitiva din fig.1.3.1.c
este redata in Pseudocod prin propozitia CAT TIMP, prezentata in
sectiunea 1.3.3.
Bohm si Jacopini aBohm66i au demonstrat ca orice algoritm poate fi descris folosind
numai aceste trei structuri de calcul.
Propozitiile DATE si REZULTATE sunt folosite in faza de specificare a
problemelor, adica enuntarea riguroasa a acestora.
4 5 6 a) structura b) structura c) structura secventiala alternativa repetitiva Figura 1.3.1. Structurile elementare de calcul
Propozitia DATE se foloseste pentru precizarea datelor initiale, deci a datelor
considerate cunoscute in problema (numite si date de intrare) si are sintaxa:
DATE lista ; unde lista contine toate numele variabilelor a caror valoare initiala este cunoscuta.
In general, prin lista se intelege o succesiune de elemente de acelasi
fel despartite prin virgula. Deci in propozitia DATE, in dreapta
acestui cuvant se vor scrie acele variabile care marcheaza marimile cunoscute
in problema.
Pentru precizarea rezultatelor dorite se foloseste propozitia standard
REZULTATE lista ;
in constructia "lista" ce urmeaza dupa cuvantul REZULTATE
fiind trecute numele variabilelor care marcheaza (contin) rezultatele cerute
in problema.
Acum putem preciza mai exact ce intelegem prin cunoasterea completa a
problemei de rezolvat. Evident, o problema este cunoscuta atunci cand
se stie care sunt datele cunoscute in problema si ce rezultate trebuiesc
obtinute. Deci pentru cunoasterea unei probleme este necesara precizarea variabilelor
care marcheaza date considerate cunoscute in problema, care va fi reflectata
printr?o propozitie DATE si cunoasterea exacta a cerintelor problemei, care
se va reflecta prin propozitii REZULTATE. Variabilele prezente in aceste
propozitii au anumite semnificatii, presupuse cunoscute. Cunoasterea acestora,
scrierea lor explicita, formeaza ceea ce vom numi in continuare specificarea
problemei. Specificarea unei probleme este o activitate foarte importanta dar
nu si simpla.
De exemplu, pentru rezolvarea ecuatiei de gradul al doilea, specificarea problemei,
scrisa de un incepator, poate fi:
DATE a,b,c; A Coeficientii ecuatiei S
REZULTATE x1,x2; A Radacinile ecuatiei S
Aceasta specificatie este insa incompleta daca ecuatia nu are radacini
reale. In cazul in care radacinile sunt complexe putem nota prin
x1, x2 partea reala respectiv partea imaginara a radacinilor. Sau pur si simplu,
nu ne intereseaza valoarea radacinilor in acest caz, ci doar faptul ca
ecuatia nu are radacini reale. Cu alte cuvinte avem nevoie de un mesaj care
sa ne indice aceasta situatie (vezi schema logica 1.2.2), sau de un indicator,
fie el ind. Acest indicator va lua valoarea 1 daca radacinile sunt reale si
valoarea 0 in caz contrar. Deci specificatia corecta a problemei va fi
DATE a,b,c; A Coeficientii ecuatiei S
REZULTATE ind, AUn indicator: 1=radacini reale, 0=complexeS x1,x2; A Radacinile ecuatiei, in cazul ind=1,S
Arespectiv partea reala si cea S
Aimaginara in cazul ind=0S
Evident ca specificarea problemei este o etapa importanta pentru gasirea unei
metode de rezolvare si apoi in proiectarea algoritmului corespunzator.
Nu se poate rezolva o problema daca aceasta nu este bine cunoscuta, adica nu
avem scrisa specificarea problemei. Cunoaste complet problema este prima regula
ce trebuie respectata pentru a obtine cat mai repede un algoritm corect
pentru rezolvarea ei.