Sunteți pe pagina 1din 13

3.3.

LIMBAJUL ALGORITMIC

Limbajul algoritmic realizează o scriere mai apropiată de cea liniară. Permite folosirea unor
instrucţiuni care conţin condensat câteva subscheme utilizate frecvent şi câteva reguli simple
de aliniere a textului scris.
Având un algoritm descris într-un astfel de limbaj, translatarea în limbaj de programare nu
presupune decât o concentrare asupra aspectelor sintactice.
Limbajul algoritmic (pseudocodul) realizează specificarea algoritmilor prin două tipuri de
enunţuri: nestandard şi standard:
- enunţurile nestandard sunt fraze în limbaj natural, care pot fi utilizate de programator în
schiţarea formelor iniţiale ale algoritmilor; ele pot fi înlocuite treptat cu enunţuri standard; sunt
marcate cu simbolul “*”;
- enunţurile standard exprimă operaţii cu corespondenţe directe în limbajele de programare.

Observaţie
Limbajul algoritmic nu este conceput cu un limbaj de programare; are puţine reguli sintactice,
lăsând o mare libertate de exprimare a acţiunilor algoritmilor.
Principalele elemente ale limbajului algoritmic sunt:
A. Cuvintele cheie – sunt cuvinte care identifică o instrucţiune; vor fi evidenţiate în scris.
B. Datele – pot fi clasificate conform mai multor criterii:
- după tip: date numerice (întregi şi reale); logice (booleene) şi şir de caractere;
- după natură: constante şi variabile;
 constantele întregi şi reale sunt numere întregi şi reale reprezentante în
modul uzual;
 constantele booleene (logice) sunt adevăr (T) şi fals (F);
 variabilelor li se asociază un nume, care este un şir de caractere alfanumerice
(litere sau cifre), începând cu o literă;
Organizarea datelor. Admitem următoarele moduri de organizare a datelor: tablouri (vectori şi
matrice), lista, stiva, coada, articolul şi fişierul.
Operaţiile cu date. Depind de natura şi organizarea datelor:
 Asupra datelor numerice se folosesc operaţiile uzuale: adunarea, scăderea, înmulţirea,
împărţirea şi exponenţierea, comparaţiile uzuale (<, >, ≤, , =, ) rezultatul fiind
exprimat printr-o valoare logică.
 Asupra datelor logice se pot efectua operaţiile de disjuncţie (OR), conjuncţie (AND) şi
negaţie (NOT).
 Asupra şirurilor de caractere se pot efectua concatenarea şi comparaţiile (bazate pe
ordinea lexicografică).

C. Instrucţiunile limbajului algoritmic:


C1. Instrucţiuni de citire/scriere
read lista variabile
write lista variabile

1
Citirea constă din transferul de valori de pe mediul de intrare în locaţiile de memorie ale
calculatorului. Scrierea constă din transferul conţinutului (valorii) unor variabile de memorie pe
mediul de ieşire. Pentru citirea/scrierea unor variabile de tip tablou vom utiliza forma:
   
v i , i  1, n sau aij , i  1, m , j  1, n

C2. Instrucţiunea de atribuire


variabilă: = expresie

Se evaluează expresia şi rezultatul este atribuit variabilei.

C3. Instrucţiunea de oprire


stop

Marchează sfârşitul algoritmului.

C4. Instrucţiunea de ramificare


if condiţie
then secvenţa 1
else secvenţa 2
endif

Observaţie. Prin secvenţă înţelegem un grup de instrucţiuni scrise liniar (una după alta).

C5. Instrucţiunea repetitivă condiţionată anterior (ciclul cu test iniţial)


while condiţie
do secvenţa
endwhile

Se execută secvenţa atât timp cât condiţia este adevărată; dacă rezultatul evaluării este fals
execuţia se termină.

C6. Instrucţiunea repetitivă condiţionată posterior (ciclul cu test final)


repeat
secvenţa
until condiţie

Se execută secvenţa (cel puţin o dată) până condiţia devine adevărată.

C7. Instrucţiunea repetitivă cu un număr fixat de paşi (ciclul cu contor)


for contor = val. iniţială, val. finală, pas
do secvenţa
endfor
Observaţie.

2
Instrucţiunea “ciclul cu contor” o considerăm echivalentă cu:
contor := val. iniţială
while (contor – val. finală) x pas ≤ 0
do secvenţa
contor := contor + pas
endwhile

C8. Instrucţiunea de ramificare multiplă


case expresie of
C1 : secvenţa 1
………………
Cn : secvenţa n
else secvenţa n + 1
endcase

Se evaluează expresia: dacă valoarea expresiei este egală cu C1, C2,..., Cn se execută secvenţa
corespunzătoare: secvenţa 1, secvenţa 2,..., secvenţa n. În caz contrar se execută secvenţa n +
1.

C9. Instrucţiunea de ramificare redusă


if condiţie
then secvenţa
endif

C10. Instrucţiunea de ieşire dintr-un ciclu


exit

Determină trecerea la execuţia primei instrucţiuni care urmează celui mai interior ciclu while,
repeat, for care o conţine, ieşindu-se deci din acel ciclu.

C11. Comentariul are forma:


/* şir de caractere */

Comentariul poate apărea oriunde în program, având rolul de a mări claritatea algoritmului,
oferind explicaţii suplimentare.

3
Exemple.
1. Algoritmul de calcul al factorialului N!  1  2  3 N se poate prezenta în limbaj algoritmic
astfel:

read N read N read N


F : 1 F : 1; i : 1 F : 1; i : 1
for i  1,N ,1 while N  i repeat
do F : F  i do F : F  i F : F  i
endfor i : i 1 i : i 1
write F endwhile until N  i
stop write F write F
stop stop

2. Diferenţa a două mulţimi date sub formă de vectori (trecerea în limbaj algoritmic a schemei
logice structurate prezentate în paragraful 3.2).

/* diferenţa a două mulţimi */


 
read m , n , ai , i  1, m , b j , j  1, n 
l : 0 ; i : 1
repeat
j : 1 ; logic: =T
repeat
if ai  b j
then logic: = F
else j : j  1
endif
until  j  n or (not logic)
if logic
then l :  l  1
cl :  ai
endif
i : i 1
until i  m
if l  0
then write „diferenţa este vidă”

else write c j , j  1, l 
endif
stop.

4
Proceduri şi funcţii
Un program poate face apel la unul sau mai multe subprograme.
Structura programului complet conţine:
 un program principal, care nu poate fi apelat ca subprogram;
 structură de subprograme.
Dacă programul P face apel la un alt program P1, atunci P se numeşte program apelant, iar P1 se
numeşte program apelat (subprogram).
Subprogramele reprezintă secvenţe de operaţii executate în scopul rezolvării unei probleme.
Aceeaşi secţiune din algoritm în mai multe puncte ale acestuia, poate fi scrisă o singură dată şi
apelată ori de câte ori este nevoie.
Utilizarea procedurilor şi funcţiilor conduce la creşterea eficienţei activităţii de programare:
părţi ale algoritmului pot fi proiectate, codificate şi testate independent una faţă de alta. Se pot
construi mult mai uşor algoritmi cu complexitate mare, utilizând module deja proiectate şi
verificate. Aceasta sporeşte fiabilitatea programelor şi reduce efortul de programare.

1. Utilizarea procedurilor:
procedure nume (parametrii formali)
secvenţa
return
apelul procedurii:
call nume (parametrii efectivi)
Instrucţiunea return determină întoarcerea în programul apelant, la instrucţiunea imediat
următoare instrucţiunii call care a produs apelarea.

2. Utilizarea funcţiilor:
function nume (parametrii formali)
secvenţa
end
apelul funcţiei:
nume (parametrii efectivi)

Exemplu.
n!
Calculul formulei C nk  se poate realiza utilizând o funcţie pentru calculul
k!  n  k !
factorialului.

function FACT(M)
FACT : 1
for i  1, M ,1
do FACT : FACT  i
endfor
end

5
Programul principal este:
read n, k
comb : FACT n / FACT k  / FACT n  k 
write comb
stop

Comentarii
 O procedură poate avea un număr oarecare de parametri, eventual zero; de exemplu, o
procedură care realizează tipărirea unui mesaj fix nu are parametri.
 Între parametrii formali şi cei efectivi se realizează o corespondenţă biunivocă;
corespondenţa este poziţională. Parametrii actuali şi cei efectivi trebuie să corespundă
cu număr şi tip.
 Pentru funcţii, lista de parametri formali conţine doar parametrii de intrare; singurul
parametru de ieşire este chiar numele funcţiei, care este utilizat în secvenţa de operaţii
a funcţiei ca orice parametru; apelul unei funcţii este considerat ca un operand al
expresiilor.

Iterativitate şi recursivitate
Iterativitatea este procesul prin care rezultatul este obţinut ca urmare a execuţiei repetate a
unui set de operaţii, de fiecare dată cu alte valori de intrare. Numărul de iteraţii poate fi
cunoscut sau necunoscut, dar determinabil pe parcursul execuţiei. Indiferent de situaţie,
numărul de iteraţii trebuie să fie finit.

Recursivitatea este procesul iterativ prin care valoarea unei funcţii se determină pe baza uneia
sau mai multora dintre propriile ei valori anterioare, prin autoapelarea funcţiei. Valorile unei
funcţii recursive se calculează din aproape în aproape, pe baza valorilor cunoscute ale funcţiei
pentru anumite argumente iniţiale.

Puterea de exprimare a recursivităţii constă în posibilitatea definirii unui număr infinit de


calcule printr-un program recursiv, chiar dacă acesta nu conţine repetări explicite.
Instrumentul necesar şi suficient folosit la exprimarea calculelor recursive este subprogramul
(procedură sau funcţie) deoarece acest mecanism permite identificarea printr-un nume a mai
multor instrucţiuni.

Subprogramul P este direct recursiv dacă se autoapelează, adică P conţine o referire explicită la
el însuşi.
Subprogramul P este indirect recursiv dacă P conţine un apel la subprogramul Q, care conţine o
referire directă sau indirectă la P.

Pentru a nu conduce la calcule infinite, un subprogram recursiv trebuie să conţină o condiţie


(pe care o vom numi COND) care la un moment dat devine falsă, caz în care se încheie apelarea
recursivă.

6
Schema unei proceduri recursive poate fi descrisă astfel:
procedure P
if COND then ………
call P
………
endif
return

Tehnica cea mai des utilizată pentru a termina apelul recursiv constă în definirea unei funcţii
f x  , astfel încât f x   0 determină terminarea apelului recursiv.
Dacă se poate demonstra că la fiecare apel valoarea funcţiei scade, atunci rezultă caracterul
finit al calculului descris de procedura respectivă.

Pentru a exemplifica procedeul considerăm funcţia factorial descrisă de:

 1, dacă n  0
fact n   (3.4)
n  fact n - 1, dacă n  1

Calculul pentru n  4 decurge astfel:

fact 4   4  fact 3   4  3  fact 2   4  3  2  fact 1 


 4  3  2  1  fact 0   4  3  2  1  1  4  3  2  1 
 4  3  2   4  6  24.

Funcţia recursivă va fi:


function FACT n
if n  o then FACT : 1
else FACT : n  FACT n  1
endif
end

Observaţie. În acest caz apelarea recursivă se încheie în momentul în care condiţia n=0 devine
adevărată.

Un algoritm recursiv poate fi înlocuit cu unul iterativ echivalent cu el. În general forma iterativă
a unei funcţii este mai eficientă decât cea recursivă în ceea ce priveşte timpul de execuţie şi
memoria ocupată. Pentru probleme de complexitate redusă este preferată varianta iterativă.
Forma recursivă este preferată în cazurile în care transformarea recursivităţii în interaţie cere
un efort de programare deosebit, algoritmul pierzându-şi claritatea exprimării, testarea şi
întreţinerea devenind astfel foarte dificile.

7
Aplicaţie
Se consideră polinomul Px   a0  x n  a1  x n1    an1  x  an cu ai  R , i  0 , n .
Să se calculeze valoarea polinomului pentru o valoare x 0 dată.

Polinomul se poate scrie:


Px   a0  x  a1   x    an1   x  an şi rezultă relaţiile de recurenţă:
P  a0 (3.5)
P  P  x0  ai , pentru i  1, 2 ,  , n

Algoritmul iterativ de calcul a valorii polinomului este:


 
read n , ai , i  0 , n , x0
P : a0 ; k : 1
while k  n
do P : P  x0  ak
k : k  1
endwhile
write x 0 ,P
stop

În variantă recursivă pentru determinarea valorii polinomului P x  de grad n trebuie calculată


funcţia pol n definită recursiv astfel:
 a0 , dacă n  0
pol n   (3.6)
pol n  1  x 0  an , dacă n  1
Algoritmul recursiv de calcul a valorii polinomului este:
function POL n
if n  0 then POL : a0
else POL : POL n  1  x0  an
endif
end

Programul principal care apelează funcţia recursivă POL n este:


 
read n , ai , i  0 , n , x 0
P : POL n
write x 0 , P
stop

8
Analiza algoritmilor
Pentru rezolvarea unei probleme se pot construi mai mulţi algoritmi, care pot avea o
comportare diferită, chiar dacă setul de date de intrare este acelaşi. Comportarea se referă la
timpul de execuţie şi la memoria necesară.
Analiza unui algoritm înseamnă determinarea resurselor necesare pentru funcţionare (timp de
execuţie şi necesar de memorie).
Datorită evoluţiei sistemelor de calcul, problema memoriei a devenit secundară. Timpul necesar
execuţiei unui algoritm este în general dependent de numărul datelor de intrare.
Pentru fiecare algoritm se poate estima în funcţie de diverse date de intrare (numărul de
parametrii, mărimea tablourilor folosite etc.) un ordin de mărime al timpului în care se
realizează comparările cuprinse în acel algoritm. Se consideră că fiecare operaţie de comparare
se execută într-un segment de timp care se ia ca unitate.

Definiţia 3.3. Fie f : N  R * o funcţie arbitrară. Ordinul lui f n este mulţimea funcţiilor g n
mărginite superior de un multiplu pozitiv al lui f n , adică:
 
O f n  g : N  R *  c  R  ,  n  N , gn  c  f n (3.7)

În această idee, dacă o implementare a unui algoritm, nu ia mai mult de g n secunde pentru a
rezolva o problemă cu n comparaţii (sau operaţii aritmetice), atunci orice altă implementare a
aceluiaşi algoritm ia un timp de ordinul lui g n secunde. Spunem că un astfel de algoritm ia un
timp de ordinul lui f n pentru orice funcţie f : N  R * astfel ca g n O f n .
În general însă se încearcă exprimarea ordinului unui algoritm ca timpul de rulare al celei mai
simple funcţii f astfel ca g n O f n .
Să presupunem că printr-o analiză riguroasă s-a ajuns pentru timpul de execuţie al unui
algoritm la expresia:
T n  an 2  bn  c
De la un n suficient de mare termenul preponderent devine an 2 , ceilalţi având o contribuţie
nesemnificativă la valoarea exactă pentru T n şi deci vor fi neglijaţi. De asemenea, factorul
constant din expresia termenului principal va fi ignorat, pentru că şi el devine nesemnificativ în
caracterizarea ordinului de mărime. Pentru rezultatul obţinut prin aceste simplificări vom nota:
 
T n  O n 2
Practica a impus ca acceptabili numai algoritmii cu comportare în timp polinomială, adică
 
pentru care k  0 cu T n  O nk .
Algoritmii cu comportare în timp exponenţială sunt consideraţi inacceptabili. Pentru o întreagă
clasă de astfel de algoritmi nu se cunoaşte dacă pot fi reduşi la algoritmi cu eficacitate
polinomială. Acestea reprezintă probleme deschise de cercetare în teoria algoritmilor.

În tabelul 3.1, se prezintă variaţia timpului de execuţie în cazul comportării polinomiale şi


exponenţiale pentru mai multe valori ale lui n, în ipoteza că durata de timp pentru o operaţie
de comparare este secunda.

9
Tabelul 3.1.
O n n =10 n = 20 n = 30 N = 40 n = 50 n = 60
n 0.00001 0.00002 0.00003 0.00004 0.00005 0.00006
secunde secunde secunde secunde secunde secunde
n2 0.0001 0.0004 0.0009 0.0016 0.0025 0.0036
secunde secunde secunde secunde secunde secunde
3 0.001 0.008 0.027 0.064 0.125 0.216
n
secunde secunde secunde secunde secunde secunde
n5 0.1 3.2 24.3 1.7 5.2 13.0
secunde secunde secunde minute minute minute
n 0.001 1.0 17.9 12.7 35.7 366
2
secunde secundă minute zile ani secole
n
3 0.059 58 6.5 38.55 2  10 8 1.3  10 13
secunde minute ani secole secole secole
(preluare din Baum D., Algorithmen und Datenstrukturen, Vorlesung, Universität Trier)

În general se consideră că un algoritm este mai eficient decât altul dacă are un ordin de mărime
pentru timpul de execuţie mai mic.

Exemplu. Determinarea minimului şi maximului elementelor unui vector.


Se dă vectorul A  a1 ,a2 , ,an  şi se pune problema determinării valorilor:
m  mina1 ,a2 , ,an  şi M  max a1 ,a2 , ,an  .

Metoda 1
O metodă evidentă este de a determina în paralel cele două valori. Algoritmul este:

/* algoritmul MINMAX1*/
 
read n , ai , i  1, n
m : a1 ; M : a1
for i  2 , n
do if ai  m
then m : ai
endif
if ai  M
then M : ai
endif
endfor
write m , M
stop
Numărul de comparaţii efectuate între elementele vectorului A şi valorile m şi M este 2n  1 .
Metoda 2

10
Algoritmul prezentat anterior poate fi îmbunătăţit dacă se efectuează comparaţia ai  M
numai în cazul în care condiţia ai  m este falsă.
Algoritmul este următorul:

/* algoritmul MINMAX2 */
 
read n , ai , i  1, n
m : a1 ; M : a1
for i  2 , n
do if ai  m
then m : ai
else if ai  M
then M : ai
endif
endif
endfor
write m , M
stop

Cazul cel mai defavorabil apare atunci când elementele vectorului sunt în ordine crescătoare,
numărul de comparaţii fiind 2n  1 . Se poate arăta că numărul mediu de comparaţii în acest
3n
caz este  1. .
2

Metoda 3
În funcţie de paritatea lui n, se începe cu una din următoarele iniţializări:
 dacă n par  m :  min a1 ,a2  M : max a1 ,a2 
 dacă n impar  m : a1 M : a1
Se consideră perechi succesive de elemente de forma:
ak ,ak 1  cu k = 3, 5, …, n-1 pentru n par;
şi k = 2, 4, …, n-1 pentru n impar.
Se compară m cu min ak ,ak1  şi M cu max ak ,ak1  actualizându-se eventual valorile
variabilelor m şi M.

11
Algoritmul pentru această metodă este:

/* algoritmul MINMAX3 */
 
read n , ai , i  1, n
if n mod 2 = 0
then if a1  a2
then m : a1 ; M : a2
else m : a2 ; M : a1
endif
k : 3
else m : a1 ; M : a1 ; k : 2
endif
while k  n  1
do if ak  ak 1
then if ak  m
then m : ak
endif
if ak 1  M
then M : ak1
endif
else if ak1  m
then m : ak 1
endif
if ak  M
then M : ak
endif
endif
k : k  2
endwhile
write m,M
stop

Dacă n  2  s se efectuează o comparaţie la iniţializare şi 3  s  1 comparaţii în continuare


3  n n , dacă x  n
3s  2     2 comparaţii, unde x   
 2  n  1 , dacă n  x  n  1
Dacă n  2  s  1 nu se efectuează nici o comparaţie la iniţializare şi 3  s  1 comparaţii în rest,
3  n
deci  3  s  3    2 comparaţii.
 2 

12
Propoziţia 3.1. Orice algoritm care determină cel mai mic şi cel mai mare element al unei
mulţimi neordonate cu n elemente necesită cel puţin
3  n
 2   2 comparaţii.
Concluzie. Algoritmul prezentat în Metoda 3 este optim.

Verificarea corectitudinii algoritmilor


Algoritmul trebuie să furnizeze rezultate corecte pentru orice set de date. Testarea unui
algoritm pentru toate seturile de date nu este în general posibilă. Datorită caracterului
secvenţial al calculelor efectuate, un algoritm corect se încheie după parcurgerea unui număr
finit de paşi. În caz contrar se spune că algoritmul ciclează.

Verificarea corectitudinii unui algoritm cuprinde două probleme:


a) problema corectitudinii parţiale constă în verificarea corectitudinii rezultatelor, în
ipoteza că algoritmul se încheie într-un număr finit de paşi;
b) verificarea terminării într-un număr finit de paşi.
Cele două aspecte: a) şi b) formează problema corectitudinii totale.

13

S-ar putea să vă placă și