Documente Academic
Documente Profesional
Documente Cultură
1
12. ŞIRURI DE CARACTERE........................................................................................... 111
EXEMPLE .................................................................................................................................... 116
SUGESTII TEME DE LABORATOR ....................................................................................... 118
13. STRUCTURI ................................................................................................................. 119
EXEMPLE .................................................................................................................................... 120
SUGESTII TEME DE LABORATOR ....................................................................................... 123
14. LISTE ............................................................................................................................ 124
EXEMPLE .................................................................................................................................... 125
SUGESTII TEME DE LABORATOR ....................................................................................... 129
15. RECURSIVITATE ........................................................................................................ 130
EXEMPLE .................................................................................................................................... 131
SUGESTII TEME DE LABORATOR ....................................................................................... 134
16. METODA DIVIDE ET IMPERA................................................................................. 135
EXEMPLE .................................................................................................................................... 136
SUGESTII TEME DE LABORATOR ....................................................................................... 139
17. METODA BACKTRACKING ...................................................................................... 140
EXEMPLE .................................................................................................................................... 141
SUGESTII TEME DE LABORATOR ....................................................................................... 143
18. GRAFURI ŞI ARBORI................................................................................................. 144
18.1 GRAFURI NEORIENTATE ............................................................................................... 144
EXEMPLE .................................................................................................................................... 146
18.2 GRAFURI ORIENTATE..................................................................................................... 159
SUGESTII TEME DE LABORATOR ....................................................................................... 164
19. MINICULEGERE DE PROBLEME ........................................................................... 165
2
1. BAZE ŞI SISTEME DE NUMERAŢIE
Deci, orice număr natural poate fi scris ca o sumă de produse, fiecare termen al sumei
fiind produsul dintre o cifră (de la 0 până la 9, aşa cu am învăţat în clasele primare …) şi o
putere a lui 10.
Puterile lui 10 descresc de la stânga spre dreapta, cea mai mare putere fiind
determinată de mărimea numărului respectiv.
Pentru un numar N oarecare vom avea, deci :
N = a n ×10n + a n-1 ×10 n-1 + a n-2 ×10n-2 + … + a 2 ×102 + a 1 ×101 + a 0 ×100, (1)
era reprezentat ca 209319 (renunţând la scrierea puterilor lui 10, care înmulţesc de fapt
fiecare cifră), numărul
3
va fi scris ca
prin aceasta neîntelegând înmulţirea între coeficienţii a i ci, aşa cum am arătat, interpretarea
din formula (1).
Alegerea a 10 simboluri (cifrele de la 0 până la 9) nu este însă nici singura posibilitate si nici
cea mai bună în orice ocazie. Antropologii sunt unanimi în a considera că această alegere se
datorează în exclusivitate faptului că avem 10 degete la mâini.
Mai mult, ea nu afost nici totdeauna nici pretutindeni valabilă.
Se ştie cu certitudine că au existat reprezentări folosind :
- 20 de simboluri – urmările se pot vedea în limba franceză, unde numărul 99 se
citeşte quatre vingt dix neuf, adică, mot-a-mot de 4 ori douăzeci şi nouăsprezece
- 12 simboluri – se regăsşte în sistemul de unităţi anglo-saxon (cu divizarea
unităţilor de măsură în 12 părţi egale), sau în termenul românesc duzină, care, în
mod riguros înseamnă o cantitate de 12 obiecte
- 60 de simboluri – utilizat în Egiptul antic
( 1×76 + 5×75 + 3×74 + 1×73 + 1×72 + 5×71 + 5×70 = 1×117649 + 5×16807 + 3×2401 + 1×343
+ 1×49 + 5×7 + 5×1 = 117649+84035+7203+343+49+35+5 = 209319 ).
Pentru a putea deci să reprezentăm orice număr conform principiului enunţat, avem
nevoie de două mulţimi :
- o mulţime A de denumiri (numele cifrelor, de exemplu « zero », « unu », etc.)
- o mulţime B de simboluri (reprezentarea grafică a cifrelor din mulţimea A ; de
exemplu « 1 », « 2 », etc)
Între cele două mulţimi există o corespondentă biunivocă.
Numărul de elemente al mulţimii A (şi deci, în mod evident, şi al mulţimii B) poate fi
oarecare, dar cel puţin 2. Astfel, în sistemul zecimal (sistemul cu care suntem astăzi
obişnuiţi), acest număr este 10 (cifrele de la 0 până la 9). In exemplul anterior, acest număr
este 7 (cifrele de la 0 până la 6).
Putem utiliza şi un număr mai mare de simboluri (am văzut că vechii egipteni
foloseau 60 …). Va trebui ca în afara celor cu care suntem familiarizaţi (0, 1, 2, 3, 4, 5, 6, 7,
8, 9) să mai “inventăm” alte simboluri. În informatică s-a generalizat utilizarea literelor mari
ale alfabetului latin.
4
Semnificaţiile acestor simboluri vor fi :
Având la dispoziţie aceste 18 simboluri, putem exprima numărul natural 209319 din
sistemul zecimal ca o sumă de produse în care să apară puterile lui 18, iar coeficienţii să fie
din mulţimea celor 18 simboluri de mai sus :
1×184 + H×183 + G×182 + 0×181 + F×180 = 1×184 + 17×183 + 16×182 + 0×181 + 15×180 =
=1×104976 + 17×5832 + 16×324 + 0×18 + 15×1 = 209319
vom obţine
N = 1HGOF.
5
Orice număr natural se exprimă atunci ca o sumă de produse între nişte coeficienţi
(cifre cuprinse între 0 şi b-1 , unde b este baza de numeraţie) şi puterile corespunzătoare ale
bazei b :
În cuprinsul acestui capitol, vom utiliza reprezentările numerelor sub formele din
formulele (3) şi (5).
Exemplele pe care le-am prezentat anterior se sintetizează atunci în modul următor :
(este deci vorba de reprezentarea aceluiaşi număr în bazele de numeraţie 10, 7 şi 18).
6
A cifra A, corespunzând numărului natural 10 din sistemul zecimal
B cifra B, corespunzând numărului natural 11 din sistemul zecimal
C cifra C, corespunzând numărului natural 12 din sistemul zecimal
D cifra D, corespunzând numărului natural 13 din sistemul zecimal
E cifra E, corespunzând numărului natural 14 din sistemul zecimal
F cifra F, corespunzând numărului natural 15 din sistemul zecimal)
Este evident că prima problemă care se pune, pentru a putea utiliza numere scrise în
alte sisteme de numeraţie decât cel zecimal, este de a dispune de algoritmi pentru trecerea
acestor numere în baza dorita (şi în general dintr-o bază în alta).
Chiar dacă nu ştim (deocamdată) cum am ajuns la acest rezultat, suntem deja în
măsură să verificăm dacă este corect.
Întra-adevăr, conform definiţiilor date şi a formulei (3),
N = 23144234 (5) =
= 2×57 + 3×56 + 1×55 + 4×54 + 4×53 + 2×52 + 3×51 + 4×50 =
= 2×78125 + 3×15625 + 1×3125 + 4×625 + 4×125 + 2×25 + 3×5 + 4×1 =
= 156250+46875+3125+2500+500+50+15+4 =
= 209319
Cum putem însă să ajungem la această reprezentare în baza 5 (corectă, dupa cum
tocmai am demonstrat) ?
Conform definiţiei, numărul căutat este de forma :
care este scrierea polinomială a numărului nostru în baza 5 şi unde trebuie să determinăm
coeficienţii a i (să ajngem pe o cale oarecare la concluzia că a 7 =2, a 6 =3, a 5 =1, a 4 =4, a 3 =4,
a 2 =2, a 1 =3, a 0 =4).
Dacă împărţim numărul 209319 la 5 (baza în care vrem să trecem numărul), obţinem
Dacă în scrierea polinomială a numărului în baza 5 (care este acelaşi număr 209319,
altfel scris) scoatem factor comun pe 5, obţinem :
7
C 0 = a 7 ×56 + a 6 ×55 + a 5 ×54 + a 4 ×53 + a 3 ×52 + a 2 ×51 + a 1 ×50
şi restul R 0 = a 0 .
Cum am împărţit la 5 acelaşi număr (doar exprimat altfel), rezultă că C 0 =41863, şi,
ceea ce este mai important, a 0 =4. Am determinat deci coeficientul a 0 din exprimarea
numărului nostru în baza 5 ; el este chiar restul împărţirii lui 209319 la 5.
Împărţind pe C 0 =41853 la 5 obţinem un nou cât şi un nou rest :
C 0 : 5 = C 1 rest R 1 ,
şi R 1 =a 1 ,
de unde deducem că C 1 =8372 şi a 1 =3, adică am mai găsit o cifră din reprezentarea în baza 5
a numarului nostru : ea este restul împărţirii primului cât la 5 (baza în care dorim să trecem
numărul.
Continuând de aceeaşi manieră, obţinem succesiv :
C 3 : 5 = 334 : 5 = 66 şi rest 4
C 3 = a 7 ×53 + a 6 ×52 + a 5 ×51 + a 4 ×50 =
= 5(a 7 ×52 + a 6 ×51 + a 5 ×50)+ a 4 =
= 5 × C4 + a4
C 4 : 5 = 66 : 5 = 13 şi rest 1
8
C 4 = a 7 ×52 + a 6 ×51 + a 5 ×50 =
= 5(a 7 ×51 + a 6 ×50)+ a 5 =
= 5 × C5 + a5
ceea ce înseamnă că C 6 =2 şi a 6 =3
Observăm cu uşurinţă că, împărţind rând pe rând numărul nostru din baza 10 şi apoi
resturile succesive obţinute la 5 (noua bază în care dorim să exprimăm numărul dat), obţinem
resturile a 0 , a 1 , a 2 , … ,a 7 , adică tocmai cifrele constitutive ale numărului nostru în baza 5
(în ordinea inversă în care apar ele în exprimarea polinomială).
Din mersul de calcul, este evident că acest lucru rămâne valabil oricare ar fi baza b în
care dorim să exprimăm numărul nostru din baza 10, ceea ce poate fi demonstrat pornind de
la formula (3) :
N: b = C 0 rest R 0
cu C 0 = a n ×bn-1 + a n-1 ×bn-2 + … + a 2 ×b1 + a 1 ×b0 =
= b(a n ×bn-2 + a n-1 ×bn-3 + … + a 2 ×b0) + a 1
R 0 =a 0
C 0 : b = C 1 rest R 1
cu C 1 = a n ×bn-2 + a n-1 ×bn-3 + … + a 3 ×b1 + a 2 ×b0 =
= b(a n ×bn-3 + a n-1 ×bn-4 + … + a 3 ×b0) + a 2
R 1 =a 1
C 1 : b = C 2 rest R 2
cu C 1 = a n ×bn-3 + a n-1 ×bn-4 + … + a 4 ×b1 + a 3 ×b0 =
= b(a n ×bn-4 + a n-1 ×bn-5 + … + a 4 ×b0) + a 3
R 2 =a 2
….
9
Procedeul se repetă până când se obţine câtul 0 (semnalul de STOP); ultimul rest
obţinut va reprezenta prima cifră a numărului exprimat în baza b.
Aceasta reprezintă chiar algoritmul de trecere a unui număr natural din baza 10
într-o bază b oarecare :
• se împarte succesiv numărul din baza 10 (şi câturile succesive obţinute pe parcurs) la
valoarea b ( baza în care dorim să trecem numarul);
• se reţin resturile succesive obţinute pe parcurs;
• procedeul se opreşte după ce s-a obţinut un cât egal cu zero (reţinându-se deci şi restul
respectiv) ;
• resturile succesive obţinute constituie, în ordinea inversă, cifrele numărului exprimat
in noua bază.
În toate exemplele pe care le-am analizat anterior (de trecere a unui număr din baza 10
intr-o bază b oarecare), am verificat corectitudinea rezultatelor obţinute prin aplicarea
formulei polinomiale (3).
Aceasta constituie chiar metoda de trecere a unui număr natural dintr-o bază b
oarecare în baza 10 : aplicarea formulei polinomiale (3).
Având la dispoziţie o metodă de trecere a unui număr natural dintr-o bază oarecare în
baza 10 şi o metodă de trecere a unui număr natural din baza 10 într-o bază b oarecare, putem
trece cu uşurinţă un număr dintr-o bază b 1 într-o bază b 2 , prin aplicarea combinată a celor
două metode : se trece numărul din baza b 1 în baza 10, apoi se trece rezultatul în baza b 2 prin
metodele deja menţionate.
În cazul particular în care cele două baze b 1 şi b 2 sunt legate între ele printr-o relaţie
de forma b 2 = b 1 k, trecerea din baza b 1 în baza b 2 (şi reciproc) se poate face direct, fără a mai
fi nevoie să mai trecem prin intermediul bazei 10.
Să exprimăm acelaşi număr N în bazele b 2 şi b 1 ; conform formulei polinomiale (3),
vom avea :
unde :
α i ≤ b 2 -1, i = 1 … n
β j ≤ b 1 -1, j = 1 … m
10
şi, datorită faptului că b 2 = b 1 k, vom avea n ≤ m (acelaşi număr natural N are o reprezentare
mai “lungă” în baza mai mică – b 1 – decât în baza mai mare – b 2 ).
Putem să calculăm mai exact valoarea lui m (în raport cu n), raţionând în modul
următor :
Din exprimarea numărului N în baza b 2 (unde am notat cu n cel mai mare exponent al
bazei b 2 ) rezultă că N < b 2 n+1 (dacă n-ar fi aşa, în dezvoltarea polinomială ar trebui să apară şi
puterea n+1).
Ştiind că b 2 = b 1 k, putem deduce succesiv
Din exprimarea numărului N în baza b 1 (unde am notat cu m cel mai mare exponent
al bazei b 1 ) şi, ţinând cont de rezultatul anterior, rezultă că
m ≤ b 1 kn+k-1
ceea ce înseamnă că reprezentarea numărului N în baza b 1 va fi, de fapt :
Evident că, din acest punct de vedere, a trece numărul N din baza b 2 în baza b 1 revine
la a determina coeficienţii β j (j ≤ kn+k-1) atunci când cunoaştem coeficienţii α i (i ≤ n). Să ne
aducem aminte că dispunem deja de o metodă indirectă : aceea de a trece numărul din baza b 2
în baza 10, apoi din baza 10 în baza b 1 , dar aşa cum anunţam anterior, în cazul particular în
care ne găsim (b 2 = b 1 k), există şi o metodă directă, fără a mai fi necesară trecerea prin
intermediul bazei 10.
Pentru aceasta să observăm că toţi coeficienţii α i sunt cifre (simboluri) în
reprezentarea numărului în baza b 2 (α i ≤ b 2 -1), dar exprimarea lor în baza b 1 constituie
numere (formate din una sau mai multe cifre), din simplul motiv că în baza b 1 cifrele pot fi
cel mult egale cu b 1 -1 ≤ b 2 -1.
Să exprimăm pe oricare dintre coeficienţii α i în baza b 1 ; observăm, mai întâi, că
α i ≤ b 2 -1 = b 1 k-1 < b 1 k,
11
= (a 1,k-1 b 1 k-1 + a 1,k-2 b 1 k-2 + a 1,k-3 b 1 k-3 + … + a 1,2 b 1 2 + a 1,1 b 1 1 + a 1,0 b 1 0)b 1 kn +
= (a 0,k-1 b 1 k-1 + a 0,k-2 b 1 k-2 + a 0,k-3 b 1 k-3 + … + a 0,2 b 1 2 + a 0,1 b 1 1 + a 0,0 b 1 0)b 1 kn
Observăm două lucruri :
N = a n,k-1 a n,k-2 a n,k-3 …a n,2 a n,1 a n,0 a n-1,k-1 a n-1,k-2 a n-1,k-3 …a n-1,2 a n-1,1 a n-1,0 a n-2,k-1 a n-2,k-2 a n-2,k-3 …a n-2,2 a n-2,1 a n-
2,0 …
a 2,k-1 a 2,k-2 a 2,k-3 …a 2,2 a 2,1 a 2,0 a 1,k-1 a 1,k-2 a 1,k-3 …a 1,2 a 1,1 a 1,0 a 0,k-2 a 0,k-3 …a 0,2 a 0,1 a 0,0
Dar fiecare grupă de câte k cifre (coeficienţi) din această reprezentare constituie pur şi
simplu reprezentarea în baza b 1 a cifrelor α i ale numărului N în baza b 2 (ne aducem aminte
că am avut α i = a i,k-1 b 1 k-1 + a i,k-2 b 1 k-2 + a i,k-3 b 1 k-3 + … + a i,2 b 1 2 + a i,1 b 1 1 + a i,0 b 1 0 =
= a i,k-1 a i,k-2 a i,k-3 …a i,2 a i,1 a i,0 (b 1 ) ).
12
EXEMPLE
Exemplul 1
Am obţinut restul 0, deci procedeul se opreşte aici. Rezultă că numărul N=209319 (10)
se exprimă în baza 6 pe 7 cifre :
Verificare :
Exemplul 2
Am obţinut restul 0, deci procedeul se opreşte aici. Rezultă că numărul N=209319 (10)
se exprimă în baza 19 pe 5 cifre :
13
Verificare :
Exemplul 3
Am obţinut restul 0, deci procedeul se opreşte aici. Rezultă că numărul N=209319 (10)
se exprimă în baza 2 pe 18 cifre :
Verificare :
14
Exemplul 4
Verificare :
Exemplul 5
Am obţinut restul 0, deci procedeul se opreşte aici. Rezultă că numărul N=209319 (10)
se exprimă în baza 16 pe 5 cifre :
Verificare :
Aplicând acelaşi procedeu, poate fi trecut numărul dat în orice bază (b≥2).
15
Pentru un control al celor învăţate, vom da reprezentarea aceluiaşi număr (209319) în
toate bazele de numeraţie de la baza b=2 (sistemul binar, şi totodată cea mai mică bază
posibilă) până la baza b=20 :
N = 209319 (10) =
= 110011000110100111 (2) =
= 101122010120 (3) =
= 303012213 (4) =
= 23144234 (5) =
= 4253023 (6) =
= 1531155 (7) =
= 630647 (8) =
= 348116 (9) =
= 209319 (10) =
= 1332A0 (11) =
= A1173 (12) =
= 74C376 (13) =
= 563D5 (14) =
= 42049 (15) =
= 331A7 (16) =
= 28A47 (17) =
= 1HG0F (18) =
= 1B9FF (19) =
= 1635J (20)
16
Despărţim reprezentarea binară în grupe de câte 3 cifre (de la dreapta la stânga):
110 011 000 110 100 111
şi reprezentăm fiecare grupă pe câte o singură cifră în baza 8 (pentru că am făcut un tabel de
corespondenţe, putem să citim asta direct din tabel) ; obţinem succesiunea de cifre
630647
Se observă cu uşurinţă că am obţinut chiar reprezentarea numărului N în baza 8.
17
N = 209319 (10) = 331A7 (16)
înlocuim fircare cifră din baza 16 prin reprezentarea ei prin grupe de două cifre în baza 4
(binar) şi obţinem direct reprezentarea întregului număr N în baza 4.
Pentru ultima pereche de baze dintre cele pe care ni le-am propus, vom scrie mai întâi
corespondenţa dintre bazele 10, 3 şi 9 (cu alte cuvinte, vom număra în aceste baze) :
Baza 10 3 9
0 00 0
1 01 1
2 02 2
3 10 3
4 11 4
5 12 5
6 20 6
7 21 7
8 22 8
şi reprezentăm fiecare grupă pe câte o singură cifră în baza 9 (pentru că am făcut un tabel de
corespondenţe, putem să citim asta direct din tabel) ; obţinem succesiunea de cifre
348116
Se observă cu uşurinţă că am obţinut chiar reprezentarea numărului N în baza 9.
18
SUGESTII TEME DE LABORATOR
A. Scrieţi pe o foaie de hârtie data naşterii, în formatul aaaallzi, fǎrǎ pauze sau caractere
separatoare. Veţi folosi ca punct de plecare numǎrul de opt cifre astfel obţinut. (de
exemplu, pentru o persoanǎ nǎscutǎ la data de 17 decembrie 1976, vom obţine
numǎrul 19761217, iar pentru o persoanǎ nǎscutǎ la data de 9 mai 1980 vom obţine
numǎrul 19800508).
Treceţi numǎrul astfel obţinut (considerat cǎ este scris în baza 10) în toate bazele de la
2 pânǎ la 16, utilizând algoritmul cu împǎrţiri successive.
- 3333(8) în bazele 4, 12 şi 5
- ABC(16) în bazele 6, 7 şi 11
19
1.2 EFECTUAREA OPERAŢIILOR ÎN DIFERITE BAZE DE
NUMERAŢIE
Q = 209319,6875
Este uşor de generalizat acest lucru pentru reprezentarea unui număr fracţionar Q într-
o bază b oarecare :
Q = Q1 + Q2
unde
Având în vedere că pentru partea înteagă dispunem deja de metode de trecere dintr-o
bază în alta, rezultă că pentru a trece numărul Q dintr-o bază în alta mai avem nevoie doar de
metode prin care să trecem un număr fracţionar fără parte întreagă în baza dorită.
Pentru a trece un astfel de număr dintr-o bază b oarecare în baza 10, e sufficient să
aplicăm formula polinomială.
Exemplu
20
Conform formulei, vom avea
Pentru a trece numărul Q f din baza 10 într-o bază b oarecare, observăm următoarele :
adică, prin înmulţirea numărului de la care am pornit cu baza b, se obţine “trecerea” în faţa
virgulei a coeficientului a -1 (prima cifră din reprezentarea în baza b a numărului strict
fracţionar Q f .
Neglijând acum această cifră şi repetând înmulţirea cu b, pentru numărul Q f1 obţinut
la pasul anterior, vom obţine cea de-a doua cifră din reprezenterea numărului iniţial Q f – cifra
a -2 , şi aşa mai departe. Procedeul se opreşte când partea fracţionară devine zero, sau atunci
când considerăm că am obţinut numărul cu precizia dorită.
0,6875 × 2 = 1,375 a -1 =1
0,375 × 2 = 0,75 a -2 =0
0,75 × 2 = 1,5 a -3 =1
0,5 × 2 = 1 a -4 =1
şi, deoarece partea fracţionară a devenit egala cu zero, procedeul s-a încheiat ; am obţinut,
deci :
Verificare :
= 0,6875 (10)
21
Observaţie importantă 1 :
Dacă un număr fracţionar se poate reprezenta cu ajutorul unui număr finit de cifre
(matematic exprimat : fără perioadă) într-o bază b 1 , aceasta nu înseamnă că el se va putea
reprezenta tot cu ajutorul unui număr finit de cifre în orice altă bază b 2 .
Deci, proprietatea de periodicitate a unui număr depinde de baza în care este
exprimat.
Exemplul 1 :
Fie numărul 0,12 (3) . După cum se vede, el se reprezintă cu ajutorul a doar două cifre
după virgulă, în baza 3. Să-l trecem în baza 10. Conform formulei polinomiale :
0,12 (3) = 1 × 3-1 + 2 × 3-2 = 1 × 1/3 +2 × 1/9 = 5/9 = 0,55555... = 0,(5) (10)
Exemplul 2 :
Fie numărul 0,7 (10) . După cum se vede, el se reprezintă cu ajutorul unei singure cifre
după virgulă, în baza 10. Să-l trecem în baza 2. Conform metodei pe care o cunoaştem,
aceasta se face prin înmulţirea succesivă cu 2.
Observaţie importantă 2 :
Metodele de trecere reciprocă între două baze b 1 şi b 2 legate între ele printr-o relaţie
de tipul b 2 = b 1 k vor rămâne valabile, având grijă doar să despărţim în grupe de câte k cifre
de la virgulă spre stânga, pentru partea întreagă, respectiv spre dreapta, pentru partea
fracţionară. Dacă, pentru partea întreagă, ultima grupă de cifre pe care le-am despărţit (cea
mai din stânga) este formată din mai puţin de k cifre, se completează la stânga cu numărul
necesar de zerouri.
Dacă, pentru partea fracţionară, ultima grupă de cifre pe care le-am despărţit (cea mai din
dreapta) este formată din mai puţin de k cifre, se completează la dreapta cu numărul necesar
de zerouri.
22
Exemplu :
Să trecem numărul 209319,6875 (10) în baza 16, ţinând cont că avem deja
reprezentarea sa în baza 2. deoarece 16 = 24, înseamnă că k = 4, deci trebuie să despărţim
cifrele din reprezentarea în bază 2 în grupe de câte 4.
unde am completat grupa din stânga cu două zerouri (la stânga), ceea ce, evident, nu modifică
valoarea numărului.
23
face “împrumut” o unitate din cifra de rang urmǎtor i + 1. În cazul în care cifra din care se
face “împrumutul” este 0, se face împrumutul mai departe, din cifra de rang urmǎtor. în
tabelul 2 este prezentat un exemplu de scǎdere a numerelor în binar, octal şi hexazecimal.
înmulţire împǎrţire
1011× 11101|101
1101 101 |101
------------- ----
1011 0100
0000 000
1011 ------
1011 1001
10001111 101
-------
100
Notǎ:
Dacǎ în urma împǎrţirii a douǎ numere în binar rezultǎ un rest diferit de zero şi mai
mic decî împarţitorul, pentru obţinerea pǎrţii fracţionare se poate continua împǎrţirea
astfel: se adauga cifra 0 la rest si virgula la cât si se continuǎ prin împǎrţirea restului la
împarţitor, rezultatele fiind adǎugate la cât dupǎ virgulǎ
24
Reprezentarea binarǎ a numerelor negative
Codul direct
Numerele întregi se reprezintǎ prin valoare absolutǎ şi semn. în cazul codului direct,
pentru numerele negative bitul de semn este 1 iar ceilalţi n − 1 biţi servesc pentru
reprezentarea valorii absolute a numǎrului.
De exemplu, numǎrul N = −5 se poate reprezenta pe 8 biţi astfel: 10000101(2),
unde valoarea absolutǎ este 0000101(2) iar primul bit este bitul de semn.
Domeniul de reprezentare în cazul codului direct va fi:
Se poate observa cǎ existǎ douǎ reprezentǎri ale lui zero, respectiv 00000000 şi
10000000, iar numǎrul maxim şi numǎrul minim dintr-un domeniu au aceeaşi valoare
absolutǎ, respectiv 01111111 şi 11111111
Codul invers
11111010(2),
C1(N ) = 2n − 1 − V
25
unde n - numǎrul de biţi al reprezentǎrii
V - valoarea absolutǎ a numǎrului de reprezentat
Codul complementar
(inversare)
11111010 +
1
11111011
Din punct de vedere matematic, complementul faţǎ de 2 al unui numǎr negativ N este
C2(N ) = 2n − V
unde:
În cazul codului complementar bitul din stânga rǎmâne tot timpul bit de semn.
Avantajul reprezentǎrii în complement faţǎ de 2 este cǎ, adunînd un numǎr cu
complementul sǎu faţǎ de 2, rezultatul este 0 (ignorând depǎşirea) ceea ce nu este valabil în
cazul celorlalte reprezentǎri.
26
SUGESTII TEME DE LABORATOR
a) 1101001(2) + 1010111(2)
b) 1000100(2) + 1001111(2)
c) 1733(8) + 234(8)
d) 1022(8) + 7721(8)
e) AC 97(16) + 33ED(16)
f) 922A(16) + 4522(16)
g) 10110(2) − 1101(2)
h) 11101011(2) − 11101(2)
i) 7100(8) − 324(8)
j) 1021(8) − 261(8)
k) AA31(16) − 2F C(16)
l) F D124(16) − AF 3C(16)
a) 110100110(2) × 11001(2)
b) 100101101(2) × 10011(2)
c) 111010001(2) × 1110(2)
d) 110111101(2) × 101(2)
e) 10111(2) : 110(2)
f ) 10101(2) : 100(2)
g) 110011(2) : 1101(2)
h) 100010(2) : 101(2)
a) -314
b) -666
c) -2111
d) -34
e) -255
f) -256
g) -100
h) -1
27
2. REPREZENTAREA DATELOR ÎN
CALCULATOR
N = ± M⋅B ±E (3.1)
unde M este mantisa, B este baza sistemului de numeraţie, iar E este exponentul.
28
De obicei, în locul exponentului se reprezintă o valoare numită caracteristică, care
se obţine prin adunarea unui deplasament la exponent, astfel încât să rezulte întotdeauna
o valoare pozitivă. Deci, nu se rezervă un câmp separat pentru semnul exponentului.
Caracteristica C este deci exponentul deplasat:
C = E + deplasament
(3.2)
29
Pentru simplificarea operaţiilor cu numere în VM şi pentru creşterea preciziei
acestora, se utili- zează reprezentarea sub forma normalizată. Un număr în VM este
normalizat dacă bitul c.m.s. al manti- sei este 1. Din cele două reprezentări ale numărului
1,75 ilustrate anterior, prima este cea normalizată.
Deoarece bitul c.m.s. al unui număr normalizat în VM este întotdeauna 1, acest bit
nu este de obicei memorat, fiind un bit ascuns la dreapta virgulei binare. Aceasta permite
ca mantisa să aibă un bit semnificativ în plus. Astfel, câmpul de 23 de biţi este utilizat
pentru memorarea unei mantise de 24 de biţi cu valori între 0,5 şi 1,0.
Cu această reprezentare, Figura 2.1 indică gama numerelor care pot fi reprezentate
într-un cu- vânt de 32 de biţi.
Dacă se utilizează reprezentarea în C2, se pot reprezenta toate numerele întregi
între –231 şi 231–1, cu un total de 232 numere diferite. Pentru formatul prezentat,
se pot reprezenta numere în urmă- toarele domenii (Figura 3.1):
• Numere negative mai mici decât – (1–2-24) ⋅ 2127, apariţia acestora determinând
o depăşire su- perioară negativă
30
• Numere negative mai mari decât –0,5 ⋅ 2-128, care determină o depăşire inferioară
negativă
• Zero
• Numere pozitive mai mici decât 0,5 ⋅ 2-128, care determină o depăşire inferioară
pozitivă
• Numere pozitive mai mari decât (1–2-24) ⋅ 2127, care determină o depăşire
superioară pozitivă
31
cât şi domeniul numerelor, este de a utiliza un număr mai mare de biţi pentru
reprezentare. Cele mai multe calculatoare utilizează cel puţin două formate, în simplă
precizie (de exemplu pe 32 de biţi), şi dublă precizie (de exemplu pe 64 de biţi).
Baza implicită este 2. Formatul scurt şi cel lung sunt prezentate în continuare.
Bitul ascuns este utilizat şi la standardul IEEE 754, dar mantisa este reprezentată
într-un mod diferit. Ea constă dintr-un bit implicit cu valoarea 1 (partea întreagă), virgula
binară implicită, şi apoi cei 23, respectiv 52 de biţi ai fracţiei. Dacă toţi biţii fracţiei sunt
0, mantisa este 1,0; dacă toţi biţii fracţiei sunt 1, mantisa este cu puţin mai mică decât 2,0.
Deci:
M = 1,Fracţie
iar valoarea numărului în precizie simplă (NS), respectiv în precizie dublă (ND) este:
NS = (–1)s ⋅ M ⋅ 2C-127 (3.4)
ND = (–1)s ⋅ M ⋅ 2C-1023 (3.5)
32
Gama numerelor care pot fi reprezentate în precizie simplă este cuprinsă între
aproximativ 2,2×10-38 şi 3,4×1038, iar cea a numerelor reprezentate în precizie dublă este
cuprinsă între 2,2×10-308 şi 1,7×10308.
În cazul obţinerii unui rezultat cu o valoare mai mică decât numărul normalizat cel mai mic
posibil, în mod obişnuit rezultatul este setat la zero şi operaţiile se continuă, sau se
semnalează o eroa- re de depăşire inferioară. Nici una din aceste soluţii nu este
satisfăcătoare, astfel încât standardul per- mite utilizarea numerelor nenormalizate
(denormalizate), care au caracteristica 0, iar fracţia diferită de 0.
Pentru valoarea zero, atât caracteristica, cât şi fracţia, sunt egale cu 0. Există două
reprezentări pentru valoarea 0, în funcţie de bitul de semn: +0, respectiv -0. Bitul de la
stânga punctului binar este implicit 0 în loc de 1.
33
pentru infinit, constând din caracteristica formată din biţi de 1 (255 pentru formatul scurt),
şi o fracţie egală cu 0. Valoarea infinit se poate utiliza ca operand, de exemplu:
∞+n=∞
n/∞=0
n/0=∞
Astfel, utilizatorul poate decide dacă va trata depăşirea superioară ca o condiţie de eroare,
sau va con- tinua calculele cu valoarea infinit.
Exemple
Numărul –0,75 poate fi scris ca – 3/4 sau – 0,11 în binar. Notaţia ştiinţifică a
numărului este
-0,11×20, iar forma normalizată a acestei notaţii este – 1,1×2–1. Caracteristica va fi –1
+ 127 = 126 (7Eh). Reprezentarea numărului în precizie simplă este deci:
Bitul de semn este 1, câmpul rezervat caracteristicii conţine 81h = 129, iar câmpul
fracţiei conţine 1 × 2–2 = 0,25. Valoarea numărului este:
34
SUGESTII TEME DE LABORATOR
a) 41 8A 1E 94;
b) 41 36 66 6A;
c) BE CC CC CB;
d) BD AB 40 C0
35
3. ELEMENTE DE TEORIA CODURILOR
Considerând un cod format din biţii b0, b1, b2, b3, ponderile asociate acestora fiind
p0, p1, p2, respectiv p3, valoarea cifrei zecimale codificate este:
N = p0 b0 + p1 b1 + p2 b2 + p3 b3
36
Exemple
În cazul codului 8421, deoarece fiecare bit are ponderea numărării în binar (20, 21,
22, 23), iar cuvintele de cod reprezintă numerele succesive în sistemul binar natural, codul
se mai numeşte cod binar-zecimal natural (NBCD – Natural Binary Coded Decimal). În
mod obişnuit, acest cod se numeşte, impropriu, cod BCD.
În cazul codului 2421, numit şi cod Aiken (după numele prof. Howard Aiken,
care a realizat calculatorul MARK I), primele 5 cifre zecimale (0 – 4) au aceeaşi exprimare
ca şi în codul 8421. Cifra zecimală 5 poate fi exprimată fie prin 0101, fie prin 1011. Deci,
reprezentarea unor cifre zecimale nu este unică, această proprietate fiind valabilă şi pentru
alte coduri. Pentru codificare s-a ales reprezentarea 1011, deoarece codul pentru cifra 5 se
poate obţine atunci prin complementarea codului pentru cifra 4. Aceeaşi regulă se poate
aplica pentru obţinerea codului cifrei 6 din codul cifrei 3, a codului cifrei 7 din codul cifrei
2 etc.
Codurile care au această proprietate se numesc coduri autocomplementare. Un cod
este autocomplementar dacă cuvântul de cod al complementului faţă de 9 al cifrei N (deci
9 – N) se poate obţine din codul cifrei N, prin complementarea fiecăruia din cei 4 biţi. De
exemplu, codul 8421 nu este autocomplementar, iar codurile 2421 , 6423 , 8421 sunt
autocomplementare.
ondiţia necesară pentru ca un cod ponderat să fie autocomplementar este ca suma
ponderilor să fie egală cu 9. Autocomplementaritatea constituie un avantaj în construcţia
unui dispozitiv aritmetic care lucrează cu numere zecimale reprezentate în codul respectiv.
Observaţie. În forma internă, numerele sunt reprezentate şi prelucrate fie sub
formă binară, fie codificate printr-un cod binar-zecimal. Trebuie sesizată diferenţa
dintre conversia unui număr zecimal în echivalentul său binar şi codificarea binar-
zecimală a numărului zecimal. De exemplu:
12 10 = 1100 2 (4 biţi)
12 10 = (0001 0010) BCD (8 biţi)
Codurile neponderate pot avea un număr mai mare de 4 biţi. Codurile cele mai
uzuale sunt prezentate în Tabelul 2.
37
Codul Exces 3 este autocomplementar, şi derivă din codul 8421 (BCD) prin
adăugarea la fiecare cifră a valorii 3. Utilizând acest cod, se poate face distincţie între
lipsa unei informaţii înscrise într-un registru sau locaţie de memorie şi înscrierea valorii
zero (0000 reprezintă lipsa unei informaţii, iar zero este codificat prin 0011).
O altă metodă pentru detectarea erorilor constă în folosirea unor biţi suplimentari
de verificare. De exemplu, un cod de n biţi poate fi format din m biţi de date şi r biţi
redundanţi de verificare (n = m + r). Fiind date două cuvinte de cod, de exemplu 1000
1001 şi 1011 0001, se poate determina numărul biţilor care diferă. În acest caz, există 3
biţi care diferă. Pentru determinarea numărului de biţi care diferă, se efectuează suma
modulo 2 între cele două cuvinte de cod, şi se calculează numărul biţilor de 1 ai
rezultatului. Numărul poziţiilor în care două cuvinte de cod diferă reprezintă distanţa
Hamming. Dacă între două cuvinte de cod se află o distanţă Hamming d, sunt necesare d
erori de câte un singur bit pentru trecerea de la un cod la al doilea cod.
38
Un exemplu simplu de cod detector de erori este un cod care conţine un bit
suplimentar numit bit de paritate. Acest bit se poate alege astfel încât numărul total al
biţilor având valoarea 1 în exprimarea numărului să fie par, respectiv impar. Dacă se
utilizează paritatea pară, notând cu x 3 x 2 x 1 x 0 biţii cifrei zecimale şi cu p bitul de paritate,
valoarea bitului de paritate determină ca suma modulo 2 a valorii tuturor biţilor să fie 0:
x3 ⊕ x2 ⊕ x1 ⊕ x0 ⊕ p = 0
de unde rezultă
p = x3 ⊕ x2 ⊕ x1 ⊕ x0
Dacă se alege paritatea impară, trebuie ca:
x3 ⊕ x2 ⊕ x1 ⊕ x0 ⊕ p = 1
p = x3 ⊕ x2 ⊕ x1 ⊕ x0 ⊕ 1
Codurile 8421 cu paritate pară ale cifrelor zecimale sunt indicate în Tabelul 2.
Verificarea parităţii nu poate detecta apariţia erorilor duble, deoarece aceste erori
nu modifică suma modulo 2 a biţilor. Există coduri mai complexe, numite coduri
corectoare de erori, care permit şi corectarea unui bit eronat sau a mai multor biţi eronaţi.
Aceste coduri sunt utile atunci când retransmisia informaţiei nu este posibilă, sau necesită
un timp care nu ar fi acceptabil.
39
sistematică a fiecăruia din cei n biţi. Fiecare din cele 2m cuvinte valide necesită n+1
combinaţii de biţi dedicate pentru cuvântul respectiv. Deoarece numărul total al
combinaţiilor de biţi este 2n, trebuie ca (n+1)2m ≤ 2n. Utilizând relaţia n = m + r,
această cerinţă devine (m + r + 1) ≤ 2r. Fiind dat m, aceasta impune o limită inferioară
asupra numărului biţilor de verificare necesari pentru corectarea erorilor de un singur bit.
De exemplu, pentru un cuvânt de m = 4 biţi, numărul minim al biţilor de verificare este r
= 3, iar pentru un cuvânt de m = 8 biţi, numărul minim al biţilor de verificare este r = 4.
Aceste limite teoretice pot fi atinse utilizând o metodă datorată lui Richard
Hamming. Metoda poate fi utilizată pentru construirea codurilor corectoare de erori pentru
cuvinte de cod de orice dimen- siune. Într-un cod Hamming, se adaugă r biţi de paritate la
un cuvânt de m biţi, rezultând un nou cuvânt cu lungimea de m + r biţi. Biţii sunt
numerotaţi începând cu 1 (şi nu cu 0), bitul 1 fiind bitul c.m.s. Toţi biţii ai căror număr
este o putere a lui 2 sunt biţi de paritate, restul biţilor fiind utilizaţi pentru date. De
exemplu, în cazul unui cuvânt de 4 biţi, biţii 1, 2 şi 4 sunt biţi de paritate. În total, cuvântul
de cod va conţine 7 biţi (4 de date şi 3 de paritate). În exemplul prezentat se va utiliza în
mod arbitrar paritatea pară.
Fiecare bit de paritate verifică anumite poziţii de biţi. Aceste poziţii sunt ilustrate
în Figura 1, unde prin pătrate s-a indicat poziţia biţilor de paritate, iar prin cercuri s-a indicat
poziţia biţilor de informaţie.
Figura 2. Construcţia codului Hamming pentru valoarea binară 0101 prin adăugarea a
trei biţi de paritate.
În general, bitul b este verificat de acei biţi b1, b2, …, bj astfel încât b1 + b2 + …
+ bj = b. De exemplu, bitul 5 este verificat de biţii 1 şi 4 deoarece 1 + 4 = 5. Bitul 6 este
verificat de biţii 2 şi 4 deoarece 2 + 4 = 6.
Dacă transmiterea cifrei zecimale se realizează corect, toţi biţii de paritate verifică
în mod corect paritatea. Dacă apare o eroare de transmisie, fie la un bit de paritate, fie la un
40
bit de informaţie, acest cod poate corecta o singură eroare. De exemplu, presupunem că a
apărut o eroare la transmiterea bitului de informaţie din poziţia 6. Codul recepţionat va fi
0100111 în loc de 0100101. Se verifică biţii de paritate, cu următoarele rezultate:
Bitul incorect trebuie să fie unul din biţii testaţi de bitul 2 de paritate (2, 3, 6 sau 7).
Deoarece bitul 4 de paritate este incorect, unul din biţii 4, 5, 6 sau 7 este incorect. Bitul
eronat este unul din cei care se află în ambele liste, deci poate fi bitul 6 sau 7. Bitul 1 de
paritate fiind corect, rezultă că şi bitul
7 este corect. Bitul eronat este deci bitul 6, şi valoarea acestui bit trebuie inversată. În acest
fel, eroarea poate fi corectată.
Codul ASCII
Un cod alfanumeric foarte des utilizat este codul ASCII (American Standard Code
for Information Interchange), care codifică literele mari şi mici ale alfabetului englez,
cifrele zecimale, semnele de punctuaţie şi alte caractere speciale. Codul ASCII utilizează 7
biţi pentru a codifica 128 de caractere. Din cele 128 de caractere, 94 sunt caractere care pot
fi tipărite, iar 34 sunt caractere utilizate pentru diferite funcţii de control.
41
Tabelul 3 Codurile ASCII
b6b5b4
b6b5b4
42
UCS (Universal Character Set). Versiunea curentă a standardului 10646 este 3.0. UCS
a fost primul set de caractere standardizat elaborat cu scopul de a include în final toate
caracterele utilizate în toate limbile scrise din lume, ca şi alte simboluri, cum sunt cele
matematice. UCS este utilizat atât pentru reprezentarea internă a datelor în sistemele de
calcul, cât şi pentru comunicaţiile de date.
În versiunile uzuale ale UCS, codul unui caracter este reprezentat prin patru cifre
hexazecimale; aceasta corespunde formei pe doi octeţi, denumită UCS-2. A fost definită de
asemenea şi o formă pe 4 octeţi, UCS-4, pentru a garanta faptul că spaţiul de codificare va
fi suficient şi în viitor. În forma pe 2 octeţi a UCS, cele 65536 coduri sunt împărţite în 256
de linii de câte 256 de celule fiecare. Primul octet al codului unui caracter indică numărul
liniei, iar al doilea numărul celulei. Primele 128 de caractere din linia 0 reprezintă
caracterele ASCII. Întreaga linie 0 conţine aceleaşi caractere ca şi cele definite de
standardul ISO/IEC 8859-1. Octetul reprezentând codul unui caracter ASCII sau ISO/IEC
8859-1 poate fi transformat simplu în reprezentarea sa UCS prin adăugarea cifrelor 00 în
faţa acestuia. UCS cuprinde aceleaşi caractere de control ca şi cele din setul ASCII.
În forma pe 4 octeţi, pot fi reprezentate peste 2 miliarde de caractere diferite.
Primul bit al primului octet trebuie să fie 0, astfel încât se utilizează numai 31 de biţi din
cei 32. Acest spaţiu de codificare este împărţit în 128 de grupe, fiecare conţinând 256 de
planuri. Primul octet de cod indică numărul grupei, iar al doilea numărul planului. Al
treilea şi al patrulea octet indică numărul liniei, respectiv al celulei. Caracterele care pot fi
reprezentate în forma UCS-2 aparţin planului 0 din grupa 0, care este numit plan
multilingv de bază sau BMP (Basic Multilingual Plane). Încă nu au fost alocate caractere
poziţiilor în afara planului BMP, iar în practică se utilizează numai forma pe doi octeţi.
43
Există patru zone principale pentru asignarea codurilor UCS-2. Zona A conţine
caractere alfabetice şi simboluri, având primele două cifre hexazecimale ale codului
cuprinse între 00h şi 4Dh. De exemplu, această zonă cuprinde seturile Basic Latin,
Latin-1, Latin Extended-A, Latin Extended-B; alfabetele grec, chirilic, armean, ebraic,
arab, bengali, tamil, georgian; diferite simboluri, săgeţi, operatori matematici, forme
geometrice etc. Literele ă, Ă, ş, Ş, ţ, Ţ din limba română fac parte din setul Latin
Extended-A, în timp ce literele â, Â, î, Î fac parte din setul Latin-1. De menţionat că toate
aceste caractere fac parte din setul de caractere ISO/IEC 8859-2, care cuprinde caracterele
speciale necesare pentru limbile ţărilor din Europa Centrală şi de Est. În Tabelul 5. se
prezintă codurile ISO/IEC 10646 ale caracterelor speciale utilizate în limba română.
Tabelul 5. Codurile ISO/IEC 10646 ale caracterelor speciale din limba română
Zona I conţine caractere ideografice, având primele două cifre hexazecimale ale
codului cuprinse între 4Eh şi 9Fh. Un caracter ideografic reprezintă un cuvânt sau o unitate
gramaticală inseparabilă. Există un mare număr de caractere ideografice în diferite limbi.
Ca un principiu general de codificare, pentru ca un nou caracter să fie inclus în setul UCS,
acest caracter trebuie să difere de toate caracterele deja incluse, atât în semnificaţie, cât şi
ca formă. Formele grafice alternative ale caracterelor existente (variante de fonturi,
hieroglife) nu au deci coduri distincte. În limbile chineză, japoneză şi coreeană există un
mare număr de caractere ideografice care au aceeaşi origine istorică şi doar diferenţe
minore ca formă în cele trei limbi. Acestor variante naţionale ale aceluiaşi caracter
ideografic li s-a atribuit un cod UCS unificat, o soluţie cunoscută sub numele de unificare
CJK. Zona I conţine asemenea caractere unificate.
Zona O, având primele două cifre hexazecimale ale codului cuprinse între A0h şi
DFh, este rezervată pentru versiunile ulterioare ale standardului. Zona R este o zonă pentru
utilizare restrânsă, codurile având primele două cifre hexazecimale cuprinse între E0h şi
FFh . Această zonă este împărţită la rândul ei în zona de utilizare privată, zona de
compatibilitate a caracterelor ideografice CJK şi zona caracterelor speciale. Zona de
utilizare privată este disponibilă pentru utilizatorii care necesită caractere speciale pentru
programele lor de aplicaţii. De exemple, icoanele utilizate în meniuri pot fi specificate prin
coduri de caractere în această zonă. Există posibilitatea utilizării unui număr de 6400 de
caractere private, cu coduri având primele două cifre hexazecimale cuprinse între E0h şi
F8h. Această zonă nu va fi ocupată de versiunile viitoare ale standardului. Zona de
compatibilitate conţine caracterele care sunt mapate la alte zone în spaţiul de codificare.
Caracterele disponibile în această zonă specială au o utilizare largă, dar nu sunt direct
compatibile cu modul de reprezentare a caracterelor UCS, astfel încât nu pot fi incluse
direct în alte zone. Codurile FFFEh şi FFFFh nu reprezintă coduri de caractere, fiind
excluse din UCS.
44
cuvântului (în partea mai puţin semnificativă), iar octetul 1 se află în stânga, ordonarea
octeţilor este numită “little-endian”. Dacă octetul 0 se află în stânga cuvântului (în partea
mai semnificativă), iar octetul 1 se află în dreapta, ordonarea octeţilor este numită “big-
endian”. Presupunem că un şir de 2n octeţi reprezentând n caractere Unicode sunt
transferate de la un calculator cu ordonarea “little-endian” la un calculator cu ordonarea
“big-endian”. Ordinea octeţilor din cuvintele de 16 biţi va fi atunci schimbată. Dacă la
începutul şirului original se include codul FEFFh, care este un cod pentru marcajul ordinii
octeţilor (Byte Order Mark), atunci prin inversarea octeţilor acesta va apare sub forma
FFFEh, care este un cod invalid. Acest cod invalid indică faptul că ordinea octeţilor din
cadrul tuturor cuvintelor este incorectă, şi deci ordinea trebuie inversată înaintea
interpretării cuvintelor ca şi caractere. Astfel, prin plasarea codului FEFFh la începutul
unui şir de caractere, o aplicaţie poate determina dacă octeţii trebuie inversaţi înainte de
interpretarea codurilor. O situaţie similară apare la transferul de la un calculator cu
ordonarea “big-endian” la un calculator cu ordonarea “little-endian”.
45
SUGESTII TEME DE LABORATOR
C. Deduceţi algoritmii pentru conversia din binar în codul Gray şi din codul Gray în
binar.
46
4. ALGORITMI ŞI SCHEME LOGICE
Exemplu:
Algoritmul lui Euclid pentru determinarea celui mai mare divizor comun a 2 numere
naturale.
Pasul 1. Se notează cu M cea mai mare, iar cu N cea mai mică dintre cele 2 valori.
Pasul 2. Se împarte M la N şi se notează restul cu R.
Pasul 3. Dacă R este diferit de zero, se atribuie lui M valoarea N şi lui N valoarea R,
apoi se revine la pasul 2; în caz contrar cel mai mare divizor al celor 2 numere este valoarea
notată cu N.
Definiţie
Un algoritm reprezintă o succesiune finită de paşi, bine determinaţi, prin care din
mulţimea datelor de intrare ale unei probleme ce aparţine unei clase de probleme se produc
date de ieşire corecte.
47
x x x y x∧y x y x∨y
a f f f f f f f
f a a f f a f a
f a f f a a
a a a a a a
Proprietăţile algoritmilor
1) Determinarea
Un algoritm trebuie astfel conceput încât operaţiile sale şi succesiunea executării lor să fie
descrise clar, precis, fără ambiguităţi sau neclarităţi.
2) Generalitatea
Algoritmul va fi conceput astfel încât să asigure rezolvarea unei clase de probleme şi nu a
unei probleme particulare.
• Variabile sunt date a căror valori se pot modifica pe parcursul execuţiei algoritmului.
Variabilele pot fi numere întregi, reale, logice sau şiruri de caractere. Ele se notează cu litere
sau simboluri care să le sugereze semnificaţia.
• Operaţiile - aritmetice: +, -, *, /
- logice: - NOT (negaţia logică) : ¬
- AND (SI logic) :∧
- OR (SAU logic) :∨
Operaţiile logice se aplică unor operanzi logici, care pot avea valorile adevărat şi fals.
Tabelele de adevăr corespunzătoare acestor operaţii sunt prezentate în cele ce urmează.
- relaţionale: <, ≤, >, ≥, =, ≠.
• Expresiile sunt formate din constante şi variabile legate între ele prin operaţii şi
paranteze care să prezinte ordinea de evaluare a operaţiilor. Nu se vor folosi decât paranteze
( ). Expresiile pot fi: - aritmetice;
- relaţionale;
- logice.
48
Operaţiile de bază realizate de către un algoritm
Schema logică este o transcriere grafică a paşilor unui algoritm. Fiecărui pas i se
ataşează un simbol numit bloc, sensul de parcurgere fiind indicat prin săgeţi. Blocurile
folosite într-o schemă logică sunt următoarele:
Blocul de citire/scriere:
Blocul de atribuire:
Blocul de decizie:
NU DA
condiţie
fals adevărat
Nume procedură
Blocul de procedură:
Săgeată:
Conector de blocuri:
49
Exemplu: Algoritmul pentru rezolvarea ecuaţiei de gradul I, ax + b = 0 :
START
a,b
NU DA
a=0
x ← -b/a NU
b=0
DA
STOP
condiţie adevărat
a fals
b b a
50
c) Structura repetitivă:
condiţie NU
DA
Exemple
Observaţii:
Deşi structura ciclică prezentată este suficientă pentru a descrie un algoritm ciclic,
totuşi în anumite situaţii se mai utilizează şi structura ciclică cu test final, prezentată în figura
5.
Ciclul cu test final poate fi transformat în ciclul cu test iniţial, conform figurii 6.
51
START
START
Citeşte Citeşte
a,b,c a
NU DA
a<b max ← a
NU
min ← b min ← a a≠0
DA
Citeşte Scrie
a a
NU DA
min > c
STOP
DA
a > max
min ← c
max ← a
Scrie
min
STOP
a
a
NU
cond
NU DA
cond
DA
Fig. 5. Structura ciclică cu test final. Fig. 4.6. Structura ciclică cu test
final.
52
Descrierea algoritmilor în limbaj pseudocod
Există mai multe variante de limbaj pseudocod, care însă nu diferă esenţial. Un limbaj
pseudocod nu este standardizat şi conţine, în general următoarele comenzi (comenzi care
corespund structurilor de control utilizate în programarea structurată):
1) de atribuire:
variabilă ← expresie
2) de citire:
citeşte lista de variabile
3) de scriere:
scrie listă de expresii
4) de ramificare:
dacă condiţie
atunci operaţie 1
...
operaţie n
altfel operaţie1
...
operaţie n
sfârşit dacă
5) de ciclare:
cât timp condiţie execută
operaţie 1
a) ...
operaţie n
sfârşit cât timp
repetă
operaţie 1
b ...
operaţie n
până când condiţie
6) de oprire:
stop
53
Erorile în algoritmi
Proiectarea algoritmilor
Conceptele principale care s-au cristalizat în domeniul programării structurate sunt:
proiectarea top-down, proiectarea modulară şi proiectarea structurală. Cele trei tipuri nu se
exclud una pe cealaltă ci se intercorelează în obţinerea unor produse program.
Proiectarea top-down (de sus în jos) presupune descompunerea de la general la
particular a problemei date în subprobleme sau funcţii de prelucrat conducând la realizarea
algoritmului în mai multe faze succesive, fiecare fază fiind o detaliere a fazei anterioare până
când algoritmul este suficient de rafinat (detaliat) pentru a putea fi codificat.
Proiectarea modularizată presupune descompunerea problemelor în părţi numite
module, astfel încât fiecare din acestea să îndeplinească anumite funcţii bine definite.
Descompunerea se poate face în mai multe faze (la mai multe niveluri) prin metoda top-
down. Criteriile de descompunere depind în mare măsură de experienţa
proiectanţilor (programatorilor). Proiectarea modularizată presupune pe lângă identificarea
modulelor şi a relaţiilor dintre ele şi precizarea modului şi a ordinii în care sunt puse în lucru.
Reprezentarea modularizată a programelor se realizează prin intermediul diagramelor
(organigramelor) de tip arbore (figura 4.7.) .
54
A
B C
B1 B2 C1 C2 C3
55
Analiza algoritmilor
Această etapă constă în:
EXEMPLE
56
Prin identificarea modului în care instrucţiunile IF-THEN, respectiv IF-THEN-ELSE se
includ unele pe altele şi a instrucţiunilor simple şi compuse, se obţine organigrama mai
explicită:
57
SUGESTII TEME DE LABORATOR
Pentru rezolvarea urmǎtoarelor probleme (pentru care algoritmii se dau parţial sau
total), se cere detalierea algoritmilor, transpunerea lor în scheme logice (organigrame) şi
scrierea programelor în pseudocod:
D. Se citesc trei numere reale, în variabilele a, b, c. Dacă cele trei valori pot forma
unghiurile unui triunghi, să se verifice dacă toate unghiurile sunt ascuţite; în caz
contrar, verificati dacă există un unghi obtuz sau un unghi drept. Scrieti un mesaj
corespunzator pentru fiecare caz.
ax + by = c
dx + ey = f
58
5. TIPURI DE DATE. CITIREA ŞI TIPĂRIREA
DATELOR
Lungime
Tipul de dată Domeniu de valori
(octeţi)
[signed] char 1 -128..127 (-27..27-1)
unsigned char 1 0..255 (0..28-1)
Întreg unsigned [int] 2 0..65535
[short] [int] 2 -32768..32767
unsigned long 4 0..232-1
long [int] 4 -231..231-1
float 4 3.4*10-38..3.4*1038
Real
double 8 1.7*10-308.. 1.7*10308
long double 10 3.4*10-4932.. 3.4*104932
Cel mai adesea, pentru citirea/scrierea datelor se utilizează cin>>/cout<<, dar formatarea
este în acest caz în seama utilizatorului; ca alternativă, se pot utiliza funcţiile scanf/printf din
limbajul C.
FUNCŢIA scanf( )
59
- citeşte din fişierul standard de intrare stdio o secvenţă de câmpuri de intrare,caracter
cu caracter, până la terminarea introducerii câmpurilor şi apăsarea tastei < Enter > ;
- formatează fiecare câmp conform formatului specificat în lista de formate.
Dincaracterele citite se calculează valori numerice sau literale, conform tipului
fiecăreiv a r i a b i l e , d i m e n s i u n i l o r d e f o r m a t s p e c i f i c a t e ş i a s e p a r a t o r i l o r
d e c â m p u r i predefiniţi (spaţiu, tab şi enter) sau impuşi explicit ;
- valorile astfel construite sunt stocate la adresele variabilelor
s p e c i f i c a t e c a argumente; ordinea formatelor variabilelor trebuie să coincidă cu
ordinea listei adreselor variabilelor în care se face citirea. Fiecare variabilă care se
doreşte a fi citită trebuie corelată cu un format specific.
Observaţie: citirea cu ajutorul funcţiei scanf a şirurilor de caractere care conţin spaţii este
imposibilă. În cazul în care formatul specificat este necorespunzător, rezultatul obţinut poate
fi neprevăzut. Valoarea întoarsă de scanf în caz de succes, este numărul de variabile care
au fost citite corect. Dacă nu a fost citită nici o variabilă (de exemplu s-a introdus un şir în
loc de un număr) funcţia întoarce valoarea 0. Dacă apare o eroare înaintea oricărei
citiri şi asignări, funcţia returnează EOF (constantă de sistem avândvaloarea întreagă –1).
%c Citeşte un caracter
%d Citeşte un întreg zecimal
%i Citeşte un întreg zecimal
%e Citeşte un număr float
%f Citeşte un număr float
%g Citeşte un număr float
%o Citeşte un număr octal fără semn
%s Citeşte un şir de caractere
%x Citeşte un număr hexazecimal fără semn
%p Citeşte un pointer
%n A r g u m e n t u l a s o c i a t p r i m e ş t e o v a l o a r e î n t r e g ă e g a l ă c u n u m ă r u l de
caractere deja citite
%u Citeşte un număr întreg fără semn
60
- Modificatorul l, poate precede caracterele de conversie d, i, o, u, x, X, caz în
care valoarea trebuie memorată ca un „ long int” s a u „ unsigned long int”,
sau poate precede caracterele e , E , f , g , G , c a z î n c a r e v a l o a r e a t r e b u i e
m e m o r a t ă c a u n „ double”.
- Modificatorul L poate precede numai caracterele e, E, f , g, G şi precizează
că valoarea convertită trebuie memorată ca un „long double”
FUNCŢIA printf( )
61
- semnul minus (-) aliniază expresia afişată la stânga
- absenţa oricărui semn semnifică alinierea expresiei afişate la dreaptă
În cazul şirurilor de caractere, specificarea a două numere separate prin punct indică
faptul că primul număr reprezintă numărul de caractere din şir care se vor afişa, iar al doilea
reprezintă limita superioară de tipărire, completarea făcându-se cu spaţii la dreapta sau
stânga, în funcţie de modul de aliniere.
În cazul unui număr întreg, al doilea număr indică o completare la stânga cu zerouri
până se ajunge la dimensiunea de afişare specificată.
62
EXEMPLE
#include <conio.h>
void main(void)
{
clrscr();
putch(getch());
getch();
B. Funcţia getche( )
#include <conio.h>
void main(void)
{
clrscr();
putch(getche());
getch();
}
#include <conio.h>
63
#include <stdio.h>
void main(void)
{
clrscr();
putchar(getchar());
getch();
#include <conio.h>
#include <stdio.h>
void main(void)
{
64
E. Funcţia printf( ) pentru scrierea cu format
#include <stdio.h>
#include <conio.h>
void main(void)
{
clrscr();
int i=123;
65
printf("Apasa orice tasta. Valoarea lui i va fi afisata aliniata la
stanga\n");
printf("*%-10d*\n\n",i,getch());
printf("Apasa orice tasta. Valoarea lui i va fi afisata cu zerouri
in fata\n");
printf("*%010d*\n\n",i,getch());
printf("Pentru terminare, apasati orice tasta");
getch();
}
#include <conio.h>
#include <stdio.h>
void main(void)
{
char sir[50];
clrscr();
printf("\nSiruri citite cu functia ");
printf("gets()\n");
printf("\nIntroduceti un sir FORMAT DIN MAI MULTE
CUVINTE\n");
gets(sir);
printf("\nSirul tiparit cu functia puts() :\n");
puts(sir);
printf("\nSirul tiparit cu functia printf(%%s) :\n");
printf("%s\n",sir);
printf("\nPentru continuare, apasati o tasta !");
getch();
clrscr();
printf("\nSiruri citite cu functia ");
printf("scanf(%%s)\n");
printf("\nIntroduceti un sir FORMAT DIN MAI MULTE
CUVINTE\n");
66
scanf("%s",sir);
printf("\nSirul tiparit cu functia puts() :\n");
puts(sir);
printf("\nSirul tiparit cu functia printf(%%s) :\n");
printf("%s\n",sir);
printf("\nPentru terminare, apasati o tasta !");
getch();
#include <conio.h>
#include <stdio.h>
void main()
{
clrscr();
printf("Se afiseaza valoarea polinomului 3x^2-8x+7\n\
pentru o valoare intreaga a lui x, introdusa de la
tastatura\n");
int x;
printf("tastati valoarea lui x=");
scanf("%d",&x);
printf("x=%d\tp(x)=%d\n\n",x,3*x*x-8*x+7);
printf("\nPentru a continua programul, apasati orice
tasta\n\n");
getch();
67
printf("\n2/3=%20.18Lf",2/3.0l);
printf("\n\nPentru a incheia programul, apasati orice
tasta\n");
getch();
#include <conio.h>
#include <stdio.h>
void main()
{
clrscr();
int x;
printf("tastati valoarea lui x=");
scanf("%d",&x);
printf("Valoarea in zecimal este\n");
printf("x(10)=%d\n\n",x);
printf("Valoarea in octal este\n");
printf("x(8)=%o\n\n",x);
printf("Valoarea in hexagesimal este\n");
printf("x(16)=%X\n\n",x);
getch();
}
68
SUGESTII TEME DE LABORATOR
A. Se citesc de la tastatură coordonatele (xa, ya) şi (xb, yb) (valori întregi) a două puncte
A şi B din plan. Să si tipărească pe monitor lungimea segmentului AB.
P(x) = a*x2 + bx + c
P(0) = 3
P(1) = 6
P(2) = 11
P(3) = 18
69
6. INSTRUCŢIUNILE IF ŞI SWITCH
Instrucţiunile condiţionale determină programele să testeze diferite condiţii şi, în funcţie de
acestea, să decidă execuţia anumitor comenzi.
Avem la dispoziţie instrucţiunile condiţionale:
• if( ) - execută comenzile dorite atunci când o condiţie (scrisă între paranteze) este
adevarată.
• if( ) ... else - execută anumite comenzi când o conditie (scrisă între paranteze) este
adevarată şi alte comenzi când această condiţie este falsă.
• switch - selectează care comandă va fi executată.
A. Instructiunea "if"
Dacă rezultatul evaluării condiţiei este TRUE, se execută codul dintre acolade, în caz contrar,
când condiţia returnează FALSE, se trece peste acest cod.
Folosind instrucţiunea "if() ... else" (dacă ... altfel), putem stabili comenzi care să fie
executate şi când condiţia instrucţiunii "if( )" este FALSE.
Forma generala a instructiuni "if() ... else" este:
if (condiţie) {
// codul care va fi executat dacă este Adevarată condiţia
}
else {
// codul ce va fi executat daca condiţia este falsă
}
Dacă rezultatul condiţiei este TRUE, se execută codul dintre primele acolade, care
aparţin de "if()", în caz contrar, când condiţia returnează FALSE, sunt executate comenzile
din acoladele de la "else".
70
Formula "else if( )"
Cu "if() ... else" sunt posibile execuţiile a doar două opţiuni, cea de la "if( )" sau de la
"else". Dar sunt situaţii în care avem mai multe opţiuni, caz în care se foloseşte formula "else
if( )" (altfel dacă).
Cu aceasta se pot crea şi alte opţiuni (suplimentare) între cele două
Sintaxa generală este:
if (condiţie 1) {
// codul care va fi executat dacă este Adevarată condiţia 1
}
else if (condiţie 2) {
// codul ce va fi executat daca prima condiţie este Falsă şi este Adevarată condiţia
2
}
else if (condiţie 3) {
// codul care va fi executat daca primele două condiţii sunt False şi este Adevarată
condiţia 3
}
// ...
else {
// codul executat dacă toate condiţiile sunt False
}
B. Instrucţiunea switch
Această instrucţiune e folosită pentru a compara o valoare cu altele dintr-o listă şi, în
funcţie de acea valoare, se execută codul asociat ei în lista "switch".
switch (expresie) {
case valoare1:
cod executat dacă expresie = valoare1
break;
case valoare2:
cod executat dacă expresie = valoare2
break;
case valoare3:
cod executat pt. expresie = valoare3
break;
default :
cod executat dacă expresie e diferit de valoare1, valoare2 sau valoare3
}
71
Prima dată este evaluată expresia scrisă între paranteze rotunde, la "switch( )", apoi
valoarea expresiei este comparată pe rând cu fiecare valoare determinată de "case".
Dacă se găseşte o identitate, se execută codul asociat acelui "case", apoi se iese din
instrucţiunea "switch".
Dacă, parcurgand fiecare "case", nu se găseşte o egalitate, se execută codul de la
"default".
Prin folosirea lui "break" se opreşte parcurgerea corpului instrucţiunii atunci când s-a
găsit o valoare egală cu 'expresie' şi se iese din "switch".
Instructiunea "switch" poate inlocui un şir de condiţii cu "else if".
O alta metodă de a executa un cod în funcţie de faptul dacă o expresie este Adevarată sau
Falsă e operatorul "? :"
Acest operator condiţional, deşi la prima vedere arată diferit de ceilalţi, este o formă
prescurtată a instrucţiunii "if( ) else". Sintaxa generală de folosire a lui este:
• expresie-condiţionala ? dacă -TRUE : dacă -FALSE;
Operatorul condiţional evaluează expresia condiţională. Dacă expresia are valoarea
TRUE, operatorul condiţional returnează valoarea de la "daca-TRUE"; în caz contrar,
returnează valoarea de la "daca-FALSE".
Pe lângă atribuirea unei valori în funcţie de rezultatul unei expresii, acest operator
condiţional poate fi utilizat în aceeaşi forma şi la determinarea apelării unei anumite funcţii,
după rezultatul unei expresii logice.
72
EXEMPLE
#include <conio.h>
#include <stdio.h>
void main(){
ax+by=c
dx+ey=f */
double a,b,c,d,e,f,x,y,det,det1,det2;
printf("\n\nIntroduceti coeficientii a,b,c,d,e,f\n");
if (scanf("%lf %lf %lf %lf %lf %lf",&a, &b, &c, &d, &e, &f) != 6)
printf("Coeficienti eronati\n");
else
if ((det=a*e-b*d) == 0)
printf("Sistemul are determinantul nul\n");
else
{
det1=c*e-b*f;
det2=a*f-c*d;
x=det1/det;
y=det2/det;
printf("x=%g\ty=%g\n",x,y);
}
getch();
}
B. Ecuaţia de gradul II
73
În care semnificaţia mesajelor este următoarea:
74
Codul corespunzător este:
#include <conio.h>
#include <iostream.h>
#include <math.h>
void main(){
float a, b, c, d, x, x1, x2, re, im;
clrscr();
cout<<"a=";cin>>a;
cout<<"b=";cin>>b;
cout<<"c=";cin>>c;
if(a==0) if(b==0) if(c==0) cout<<"Aceasta nici macar nu e
o ecuatie!";
else cout<<"Imposibil!";
else{
cout<<"Este, de fapt, o ecuatie de gradul I"<<endl;
x=-c/b;
cout<<"Radacina este x="<<x;
}
else{
cout<<"Asta da! Este o ecuatie de gradul II"<<endl;
d=b*b-4*a*c;
if(d>0) {
cout<<"Radacini reale distincte"<<endl;
x1=(-b-sqrt(d))/2/a;
x2=(-b+sqrt(d))/2/a;
75
cout<<"x1="<<x1<<endl;
cout<<"x2="<<x2;
}
else if(d==0){
cout<<"Radacini reale confundate"<<endl;
x=-b/2/a;
cout<<"x1=x2="<<x;
}
else{
cout<<"Radacini complexe"<<endl;
re=-b/2/a;
im=-sqrt(-d)/2/a;
if(im<0)im=-im;
cout<<"x1="<<re<<" - "<<im<<" i"<<endl;
cout<<"x2="<<re<<" + "<<im<<" i";
}
}
getch();
}
#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
void main(){
int rasp;
clrscr();
cout<<"OPERATII CU LISTE :"<<endl<<endl;
cout<<
" 1: creare"<<endl<<
" 2: listare de la stanga la dreapta"<<endl<<
" 3: listare de la dreapta la stanga"<<endl<<
" 4: inserare nod in interiorul listei"<<endl<<
" 5: inserare nod pe prima pozitie"<<endl<<
" 6: inserare nod pe ultima pozitie"<<endl<<
76
" 7: stergerea unui nod din interiorul listei"<<endl<<
" 8: stergerea primului nod"<<endl<<
" 9: stergerea ultimului nod"<<endl<<
"10: salvez lista"<<endl;
cin>>rasp;
switch (rasp){
case 1: cout<<"voi crea o lista";break;
case 2: cout<<"voi lista de la stanga la dreapta";break;
case 3: cout<<"voi lista de la dreapta la stanga";break;
case 4: cout<<" voi insera un nod in interiorul listei";break;
case 5: cout<<" voi insera un nod inaintea primului nod";break;
case 6: cout<<" voi insera un nod dupa ultimul nod";break;
case 7: cout<<" voi sterge un nod din interiorul listei";break;
case 8: cout<<" voi sterge primul nod";break;
case 9: cout<<" voi sterge ultimul nod";break;
case 10: cout<<" voi salva lista";break;
default: exit(1);
}
getch();
77
SUGESTII TEME DE LABORATOR
1
− pentru x<0
x
E ( x) = 0 pentru x=0
x2 + x + 1 pentru x>0
G. Se citesc trei numere reale, în variabilele a, b, c. Dacă cele trei valori pot forma
unghiurile unui triunghi, să se verifice dacă toate unghiurile sunt ascuţite; în caz
contrar, verificati dacă există un unghi obtuz sau un unghi drept. Scrieti un mesaj
corespunzator pentru fiecare caz.
78
7. CICLUL FOR ŞI INSTRUCŢIUNILE
REPETITIVE DE TIP WHILE ŞI DO-WHILE
este o structură ciclică cu test iniţial şi implementează structura ciclică cu număr cunoscut
de paşi
Sintaxa:
Funcţionarea:
evaluare expresie1
ATÂTA TIMP CÂT expresie2 este TRUE REPETĂ
begin
instrucţiune
evaluare expresie3
end
Sintaxa:
while(<expresie>)
instr1;
79
Instrucţiunea/instrucţiunile din corpul ciclului while trebuie să modifice valoarea expresiei,
altfel va fi un „ciclu infinit”.
Sintaxa:
do instr1;
while(<expresie>)
Funcţionarea:
Instrucţiunea break
forţează ieşirea din interiorul unei bucle, fără a se mai ţine seama de condiţia de menţinere în
buclă. Instrucţiunile situate în corpul buclei după instrucţiunea break nu vor mai fi executate.
Intrucţiunea continue
duce la ignorarea instrucţiunilor din buclă, situate după aceasta, şi testarea din nou a expresiei
de menţinere în buclă. În cazul buclelor for, se realizează şi evaluarea celei de a treia expresii
, responsabilă cu incrementarea contorilor.
EXEMPLE
#include <conio.h>
#include <iostream.h>
#include <math.h>
void main() {
clrscr();
unsigned long n, d;
int prim=1;
cout<<"n=?";cin>>n;
for(d=2;d<=sqrt(n);d++) if(n%d==0) prim=0;
if(prim) cout<<n<<" este prim";
80
else cout<<n<<" nu este prim";
getch();
}
#include<iostream.h>
#include<conio.h>
void main(){
clrscr();
int a, b, p=0;
cout<<"a=";cin>>a;
cout<<"b=";cin>>b;
if(a==b)cout<<"cmmdc="<<a;
else while(a!=b){if(a>b)a=a-b;else b=b-a;p++;}
cout<<"cmmdc="<<a<<" in "<<p<<" pasi";
getch();
}
#include<iostream.h>
#include<conio.h>
void main(){
clrscr();
int a, b, r, p;
cout<<"a=";cin>>a;
cout<<"b=";cin>>b;
while(b){
r=a%b;
a=b;
b=r;
p++;
}
cout<<"cmmdc="<<a<<" in "<<p<<" pasi";
getch();
}
81
SUGESTII TEME DE LABORATOR
B. Scrieţi un program care afişează numerele divizibile cu 3 din intervalul [a,b]. Valorile
a şi b se citesc de la tastatură.
D. Scrieti un program care afişează numerele prime până la n. Valoarea lui n se citeşte de
la tastatură.
I. Scrieţi un program care determină perimetrul şi aria unui triunghi. Există 4 situaţii
posibile. Un triunghi poate fi:
1. isoscel
2. echilateral
3. dreptunghic
4. oarecare.
82
8. TABLOURI UNIDIMENSIONALE
PROBLEME DE ORDONARE
Tablourile sunt colecţii de date de acelaşi tip reunite sub un singur nume, care ne
permit să programăm mai uşor operaţii asupra grupurilor de valori de acelaşi tip.
Declararea tabloului este similară cu declaraţia unei variabile simple, cu o
singură excepţie: trebuie declarată şi dimensiunea tabloului. Numărul de componente se
declară între [ ]. Declaraţia
float date[50];
Primul element are indicele 0, al doilea are indicele 1, …, iar ultimul are indicele 49.
Un tablou unidimensional (numit de foarte multe ori vector) este deci o colecţie
structurată de elemente care pot fi accesate individual, specificând poziţia componentei
printr-un indice (variabilă de tip întreg).
Sintaxa unei declaraţii de tablou unidimensional (vector):
TipDată NumeTablou[ExpresieConstInt];
Exemplu
#include <iostream.h>
#include <conio.h>
void main( ){
float x[5] = {11.2, 3.172, 12.1, -27.3, 12};
int i ;
for(i = 0 ; i <5 ; i++)
cout << "x[" << i << "]= " << x[i] << endl ;
}
83
Dacă se înscriu mai multe valori, compilatorul semnalează o eroare. Dacă sunt mai
puţine valori în listă, restul sunt iniţializate cu valoarea 0.
Tablourile sunt transmise funcţiilor prin referinţă.
În lista parametrilor formali, declararea unui tablou nu include şi dimensiunea sa
între [ ]. Dacă se include dimensiunea, compilatorul o ignoră, deoarece compilatorului îi
este necesară doar informaţia referitoare la natura parametrului, adică faptul că este
vorba despre un tablou, şi la tipul componentelor sale. Acesta este motivul pentru care
trebuie adăugat un al doilea parametru al funcţiei prin care se precizează numărul de
componente.
În prototipul unei funcţii care are parametri de tip tablou nu este necesară prezenţa
numelor parametrilor formali.
EXEMPLE
A. Aflarea valorilor maxime şi minime şi a poziţiilor pe care acestea le ocupă într-un vector
#include <conio.h>
#include <iostream.h>
void main() {
clrscr();
int v[50], i, n, max, pozmax, min, pozmin;
cout<<"n=?";
cin>>n;
for(i=1;i<=n;i++){
cout<<"v["<<i<<"]=?";
cin>>v[i];
}
max=v[1];pozmax=1;
min=v[1];pozmin=1;
for(i=2;i<=n;i++){
if(v[i]>max){
max=v[i];pozmax=i;
}
if(v[i]<min){
min=v[i];pozmin=i;
}
}
cout<<"maximul este "<<max<<" si ocupa pozitia
"<<pozmax<<endl;
cout<<"minimul este "<<min<<" si ocupa pozitia "<<pozmin;
getch();
}
84
B. Ordonarea crescătoare a elementelor unui vector
#include <conio.h>
#include <iostream.h>
void main() {
clrscr();
int v[50], i, j, n, aux;
cout<<"n=?";
cin>>n;
for(i=1;i<=n;i++){
cout<<"v["<<i<<"]=?";
cin>>v[i];
}
for(j=1;j<=n-1;j++){
for(i=1;i<=n-1;i++) if(v[i]>v[i+1]){
aux=v[i];
v[i]=v[i+1];
v[i+1]=aux;
}
}
for(i=1;i<=n;i++) cout<<"v[i"<<"]="<<v[i]<<endl;
getch();
}
#include <conio.h>
#include <iostream.h>
void main() {
clrscr();
int v[50], i, j, n, aux;
cout<<"n=?";
cin>>n;
for(i=1;i<=n;i++){
cout<<"v["<<i<<"]=?";
cin>>v[i];
}
for(j=1;j<=n-1;j++){
for(i=1;i<=n-1;i++) if(v[i]<v[i+1]){
aux=v[i];
v[i]=v[i+1];
v[i+1]=aux;
}
}
85
for(i=1;i<=n;i++) cout<<"v[i"<<"]="<<v[i]<<endl;
getch();
}
C. Numere Fibonacci
#include <conio.h>
#include <iostream.h>
void main(){
clrscr();
unsigned long n, a, b, f[50], j;
int i, fv[50];
f[1]=1;
f[2]=2;
for(i=3; i<=46; i++) f[i]=f[i-2]+f[i-1];
for(i=1; i<=46; i++) cout<<i<<" "<<f[i]<<endl;
getch();
cout<<"n=?";
cin>>n;
for(i=1; i<=46; i++) fv[i]=0;
for(i=46; i>=1;i--) if(f[i]<=n){
n=n-f[i];
cout<<f[i]<<" ";
}
cout<<endl;
getch();
cout<<"a=";cin>>a;
cout<<"b=";cin>>b;
for(j=a; j<=b; j++){
n=j;
for(i=1; i<=46; i++) fv[i]=0;
for(i=46; i>=1;i--) if(f[i]<=n){
n=n-f[i];
fv[i]=1;
86
}
for(i=46; i>=1;i--) cout<<fv[i];
cout<<endl;
}
getch();
87
SUGESTII TEME DE LABORATOR
D. Să se introducă între fiecare dintre elementele succesive ale unui vector media lor
aritmetică.
88
9. TABLOURI BIDIMENSIONALE
Tablourile bidimensionale sunt denumite si matrici. La declararea unui tablou
bidimensional, se specifică numărul de elemente al fiecărei dimensiuni, incluzând fiecare
dintre aceste numere între paranteze drepte. Indexul inferior al fiecarei dimensiuni este 0.
tipul_datelor nume_matrice[nr_linii][nr_coloane];
Exemplu: o matrice de numere intregi , care are 5 linii a câte 4 componente pe linie :
int a[5][4];
EXEMPLE
#include <stdio.h>
#include <conio.h>
89
for (i=0;i<=m-1;i++)
for (j=0;j<=n-1;j++)
c[i][j]=a[i][j]+b[i][j];
printf("\nElementele matricei C sunt :\n");
for (i=0;i<=m-1;i++)
for (j=0;j<=n-1;j++)
printf("c[%d][%d]=%d\n",i,j,c[i][j]);
getch();
}
#include <stdio.h>
#include <conio.h>
void main() /* Produsul a doua matrici cu A(mxn) si B(nxp) */
{
int a[10][10],b[10][10],c[10][10];
int n,m,p,i,j,k,s;
printf("\nIntroduceti numarul de linii al primei matrici : ");
scanf("%d",&m);
printf("Introduceti numarul de coloane al primei matrici : ");
scanf("%d",&n);
printf("Introduceti numarul de coloane al celei de a doua matrici : ");
scanf("%d",&p);
printf("\nIntroduceti elementele matricii A\n");
for (i=0;i<=m-1;i++)
for (j=0;j<=n-1;j++) {
printf("a[%d][%d]=",i,j);
scanf("%d",&a[i][j]);
};
printf("\nIntroduceti elementele matricii B\n");
for (i=0;i<=n-1;i++)
for (j=0;j<=p-1;j++) {
printf("b[%d][%d]=",i,j);
scanf("%d",&b[i][j]);
};
for (i=0;i<=m-1;i++)
for (j=0;j<=p-1;j++) {
c[i][j]=0;
for (k=0;k<=n-1;k++)
c[i][j]=c[i][j]+a[i][k]*b[k][j];
}
printf("\nElementele matricii C sunt :\n");
for (i=0;i<=m-1;i++)
for (j=0;j<=n-1;j++)
90
printf("c[%d][%d]=%d\n",i,j,c[i][j]);
getch();
#include <stdio.h>
#include <conio.h>
void main() /* Transpusa unei matrici A(mxn) */
{
int a[10][10],b[10][10];
int n,m,i,j;
printf("\nIntroduceti numarul de linii : ");
scanf("%d",&m);
printf("Introduceti numarul de coloane : ");
scanf("%d",&n);
printf("\nIntroduceti elementele matricii A\n");
for (i=0;i<=m-1;i++)
for (j=0;j<=n-1;j++) {
printf("a[%d][%d]=",i,j);
scanf("%d",&a[i][j]);
};
for (i=0;i<=n-1;i++)
for (j=0;j<=m-1;j++)
b[i][j]=a[j][i];
printf("\nElementele matricii transpuse sunt :\n");
for (i=0;i<=n-1;i++)
for (j=0;j<=m-1;j++)
printf("b[%d][%d]=%d\n",i,j,b[i][j]);
getch();
}
91
SUGESTII TEME DE LABORATOR
A. Să se afişeze elementele de pe bordura unei matrici
92
10. FUNCŢII DEFINITE DE UTILIZATOR
Corpul funcţiei (compus din operatori) este amplasat după descrierea parametrilor,
între acolade.
unde
tip_parametru nume_parametru;
93
Dacă funcţia conţine mai mulţi parametri, ei vor fi descrişi împreună între parantezele
rotunde după numele funcţiei, despărţiţi prin virgulă
- Dacă funcţia foloseşte parametri, ea trebuie să indice numele unic şi tipul fiecărui
parametru.
- Când programul apelează funcţia, compilatorul atribuie valoarea parametrilor de la stânga la
dreapta
- Valorile transmise din program în funcţie, trebuie să coincidă ca număr, loc şi tip cu
parametrii din funcţie.
3. Prototipul funcţiei.
Înainte de apelul unei funcţii, compilatorul C++ trebuie să cunoască tipul valorii
returnate, cantitatea şi tipul parametrilor folosiţi de funcţie.
Există însă situaţii cînd unele funcţii în program sînt apelate reciproc. În aceste cazuri,
este posibilă situaţia cînd o funcţie va fi apelată înaintea descrierii sale.
94
În acest caz, se folosesc prototipuri ale funcţiilor. Prototipul unei funcţii este amplasat la
începutul programului şi conţine informaţia despre tipul valorii returnate, cantitatea şi tipul
parametrilor folosiţi de funcţie.
Odată declarat prototipul unei funcţii, înainte de a fi început corpul programului,
descrierea funcţiei poate fi făcută după acolada de închidere a programului principal.
unde:
Principiile de declarare şi folosire a unei variabile locale oricărei funcţii sunt identice
cu principiile de declarare şi utilizare a unei variabile declarate în corpul funcţiei
principalemain( );
O variabilă declarată în corpul funcţiei main() este şi ea locală acestei funcţii.
În general, tot ceea ce este valabil pentru a fost spus despre variabilele declarate în funcţia
main( ) - tipurile, numele, principiile de utilizare ş.a.- rămâne valabil şi pentru o
variabilă locală, din orice altă funcţie.
5. Variabile globale
Numim variabilă globală o variabilă pentru care numele şi valoarea sunt cunoscute pe
parcursul întregului program, orice funcţie din acest program.
Pentru a crea o variabilă globală, se foloseşte declararea ei la începutul programului,
în afara oricărei funcţii. Orice funcţie (inclusiv funcţia main), care va urma după această
declarare, poate folosi această variabilă globală.
# include<…>
# include<…>
…
# include<…>
95
tip_vg nume_vg;
unde tip_vg este tipul variabilei globale, iar nume_vg – numele variabilei globale.
EXEMPLE
#include <stdio.h>
#include <conio.h>
#include <math.h>
96
float f2(int *a,int *b,int *c)
{
*a=*a**a;
*b=*b**b;
*c=*c**c;
return sqrt(*a+*b+*c);
}
void main()
{
int x,y,z;
printf("\nIntroduceti pe x : ");
scanf("%d",&x);
printf("Introduceti pe y : ");
scanf("%d",&y);
printf("Introduceti pe z : ");
scanf("%d",&z);
printf("Rezultatul aplicarii lui f1 : %g\n",f1(x,y,z));
printf("x=%d\n",x);
printf("y=%d\n",y);
printf("z=%d\n",z);
printf("Rezultatul aplicarii lui f2 : %g\n",f2(&x,&y,&z));
printf("x=%d\n",x);
printf("y=%d\n",y);
printf("z=%d\n",z);
getch();
}
#include <fstream.h>
#include <conio.h>
void main(){
clrscr();
int x, y, z, rasp, gasit=0;
char fis[20];
cout<<"numele fisierului de intrare : ";cin>>fis;
ifstream f(fis);
f>>x>>y;
cout<<x<<" "<<y<<endl;
while((!gasit)&&(x<=y)){
z=(x+y)/2;
f>>rasp;
if(rasp) gasit=1;
else{
f>>rasp;
97
if(rasp)y=z-1;
else x=z+1;
}
}
if(gasit)cout<<z;
else cout<<"0";
getch();
f.close;
}
double factorial(int);
#include <stdio.h>
#include <conio.h>
double factorial(int n)
/* Calculeaza pe n! pentru n din intervalul [0,170];
pentru alte valori returneaza -1 */
{
double f;
int i;
if (n<0||n>170)
return -1.0;
for (i=2,f=1.0;i<+n;i++)
f*=i;
return f;
}
98
D. Aflarea unei rădăcini unice dintr-un interval dat
double f(double);
double radac(double ls,double ld);
#include <conio.h>
#include <stdio.h>
#include <math.h>
void main()
/* Calculeaza o radacina unica din intervalul [a,b] */
{
double a,b,x,fx;
printf("\n\nIntroduceti limita din stanga ");
scanf("%lf",&a);
printf("Introduceti limita din dreapta ");
scanf("%lf",&b);
if (fabs(f(a))<1e-30) printf("Radacina este chiar limita din stanga\n");
else if (fabs(f(b))<1e-30) printf("Radacina este chiar limita din
dreapta\n");
else if (f(a)*f(b)>0) printf("Varianta nepermisa\n");
else printf("Radacina este
x=%g\tf(x)=%g\n",radac(a,b),f(radac(a,b)));
getch();
}
double f(double x)
{
double val;
val=x*x-3.0*x+2.0;
return val;
}
99
E. Maxime din minime pe linii şi coloane
#include <stdio.h>
#include <conio.h>
100
for (i=1;i<=m;i++)
ajut[i]=a[i][j];
maxmin(ajut,m,mxcl[j],mncl[j]);
printf("coloana %d\tmax=%d\tmin=%d\n",j,mxcl[j],mncl[j]);
}
maxmin(mxcl,n,mxmxcl,mnmxcl);
printf("\nMaximumul din maximele pe coloane este %d",mxmxcl);
printf("\nMinimumul din maximele pe coloane este %d",mnmxcl);
maxmin(mncl,n,mxmncl,mnmncl);
printf("\nMaximumul din minimele pe coloane este %d",mxmncl);
printf("\nMinimumul din minimele pe coloane este %d\n\n",mnmncl);
getch();
}
101
SUGESTII TEME DE LABORATOR
I. Să se scrie o funcţie pentru extragerea elementelor comune din doi vectori (neordona)
într-un al treilea vector.
J. Să se scrie o funcţie care caculează aria intersecţiei a două dreptunghiuri, date prin
coordonatele colţurilor stânga-sus şi dreapta-jos. Coordonatele sunt numere întregi
pozitive.
K. Să se scrie o funcţie care determină aria reuniunii a două dreptunghiuri date prin
coordonatele întregi ale colţurile stânga-sus şi dreapta-jos.
102
11. POINTERI
int a;
int *b;
Variabila a este de tip întreg, iar variabila b este de tip pointer; ea conţine adresa unei
variabile de tip întreg (adresa la care se află o valoare de tip întreg).
În cazul declarării variabilei, semnul * din stânga acesteia semnifică faptul ca acesta
conţine o adresă (variabila este de tip pointer) şi nu o valoare. În momentul utilizării
variabilei semnul * din stânga acesteia are altă semnificaţie, acesta semnifică faptul că se
preia valoarea variabilei de tip pointer.
Ex:
a=*b;
Ex:
b=&a;
103
void free(void *p);
int *p;
p=(int*)malloc(n*sizeof(int))
if(p==NULL) { printf(„Memorie insuficienta!”);
......
if(p) // identic cu if(p!=NULL) free(p);
sau
int *p;
p=new int[n];
if(p==NULL)
{ printf(„Memorie insuficienta!”);
......
if(p) delete[] p;
#include <stdio.h>
#include <conio.h>
#include <alloc.h>
//aloca dinamic o matrice cu m linii si n coloane
double **aloca2double(int m,int n)
{double **A; int k,i;
A=(double**) malloc(m*sizeof(double*));
if(!A) return NULL;
for(k=0;k<m;k++)
{A[k]=(double*) malloc(n*sizeof(double));
if(!A) //alocare esuata, elibereaza mem. deja alocată
{ for(i=0;i<k;i++) free(A[i]);
free(A); return NULL;
}//endif
}//endfor
for(k=0;k<m;k++)
for(i=0;i<n;i++) A[k][i]=0;
return A;
}
104
//-----------------------------------------
//elibereaza zona de memorie alocata
void elib2double(double**A, int m)
{int k;
for(k=0;k<m;k++)
if(A[k]) free(A[k]);
if(A) free(A);
}
//--------------------------------------
void main(void)
{ double **x;
x=aloca2double(100,50);
if(x==NULL) printf("Memorie insuficienta");
//..... operatii cu matricea x ....
elib2double(x,100);
}
ARITMETICA POINTERILOR
Ex:
105
Ex:
EXEMPLE
A. Pointeri şi adrese
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <iostream.h>
void main(void){
int x, y, *adr;
clrscr();
getch();
}
106
B. Aceeaşi funcţie utilizator, definită cu şi fără pointeri
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
void main(void){
int x,y,d;
clrscr();
cout<<"functie definita fara pointeri\n";
cout<<"pe cine cresc ?";cin>>x;
cout<<"cu cat cresc ?";cin>>d;
cout<<"obtin "<<cresc1(x,d)<<"\n\n";
#include <conio.h>
#include <iostream.h>
void main(){
int a, b, * pa, * pb, v[100], i, n;
float x[100], * px;
clrscr();
cout<<"a=?";cin>>a;
cout<<"b=?";cin>>b;
pa=&a;
pb=&b;
cout<<a<<" "<<pa<<" "<<*pa<<endl;
107
cout<<b<<" "<<pb<<" "<<*pb<<endl;
getch();
/* cout<<"Cate elemente are vectorul v? ";cin>>n;
for(i=0;i<n;i++){
cout<<"Introduceti pe v["<<i<<"] ";cin>>v[i];
}
for(i=0;i<n;i++){
cout<<"v["<<i<<"]= "<<v[i]<<endl;
}
pa=v;
cout<<*pa<<endl;
pa=pa+3;
cout<<*pa<<endl; */
D. Aritmetica pointerilor
#include <stdio.h>
#include <conio.h>
void main()
{
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
unsigned long int adr0, adr1, adr2;
int *p1, *p2;
clrscr();
108
printf("sizeof(a + 1) = %d\n", sizeof(a + 1));
printf("sizeof(a + 3) = %d\n\n", sizeof(a + 3));
p1 = a + 4;
p2 = &(a[4]);
*p1 = *p1 + 1;
printf("\na[4] = %d\n", a[4]);
getch();
}
109
SUGESTII TEME DE LABORATOR
B. Să se scrie o funcţie pentru incrementarea unui moment de timp, dat ca oră, minut şi
secundă și perioada zilei (AM sau PM). Funcția va trebui să modifice 3 numere
întregi şi un vector de caractere şi va primi 3 argumente pointer şi un argument vector.
C. Să se scrie o funcţie care calculează panta şi ordonata pentru o dreaptă dată prin 2
puncte. Ecuaţia dreptei dată prin pantă şi ordonată este y = m * x + n. Funcţia va avea
antetul: void panta(int x1, int y1, int x2, int y2, float *m, float *n);
110
12. ŞIRURI DE CARACTERE
În limbajul C/C++, şirurile de caractere sunt vectori având tipul de bază char.
Un şir de caractere se termina prin marcatorul \0, sau caracterul nul.
De exemplu, şirul "x2A" este memorat pe 4 caractere, ultimul fiind \0, numărul de
elemente al şirului fiind, deci, 3, iar dimensiunea şirului 4.
Un caracter dintr-un şir (vector) de caractere "a" poate fi accesat folosind indexul
şirului (a[i], de exemplu) sau folosind pointeri la caracter.
Iniţializarea (citirea) unui şir de caractere se poate face în mai multe moduri:
a[0] = 'x';
a[1] = '2';
a[2] = 'A';
a[3] = '\0';
scanf("%s", a);
Formatul "%s" este folosit pentru citirea unui sir de caractere. Se efectuează trei pasi:
- poziţionarea pe primul caracter al şirului;
- citirea tuturor caracterelor diferite de <Enter> şi introducerea lor în vectorul "a";
- citirea se face până la întâlnirea EOF, după care se plasează '\0' la sfârşitul şirului.
Deoarece numele unui şir (ca la orice vector) este un pointer la adresa de bază a
şirului, expresia "a" este echivalentă cu "&a[0]".
Dacă şirul citit are mai multe caractere decat cele rezervate, se va obţine o eroare.
Este important de reţinut că 'b' şi "b" sunt două lucruri diferite, prima fiind o constantă
caracter, în timp ce a doua este o constantă şir de caractere, care conţine pe prima poziţie
constanta caracter 'b' şi pe a doua poziţie caracterul '\0'
sau, echivalent
4. Putem folosi un pointer către un şir constant, dar interpretarea este diferită:
char *p = "x2A";
111
Numele unui şir (ca la orice vector) poate fi tratat ca un pointer către adresa de bază a
şirului din memorie.
Constanta "x2A" este memorată de către compilator. În acelaşi timp, aceasta este "un
nume de şir". Diferenţa dintre un şir iniţializat cu o constantă şir şi un pointer iniţializat tot cu
o constantă şir este că şirul conţine caractere individuale urmate de caracterul "\0", în
timp ce pointerul este asignat cu adresa şirului constant din memorie.
#include <stdio.h>
#include <ctype.h>
main()
{
char c, nume[200];
int i, sum = 0;
A. Functia strlen
int strlen(nume_şir); – returnează lungimea efectivă a unui şir (fără a număra terminatorul de
şir).
Exemplu:
char s[20]=”Buna ziua!”; strlen(s) = 10
112
B. Funcţia strcpy
Exemplu:
char x[20]=”Ion”, y[30]=”Vasile”;
strcpy(x,y); x = ”Vasile”; y= ”Vasile”;
C. Funcţia strcat
strcat(dest,sursa); – adauga şirului dest şirul sursa. Sirul sursa rămâne nemodificat. Operatia
se numeste concatenare şi NU este comutativă.
Exemplu:
char *x=”Buna ”,*y=”ziua!”;
strcat(x,y); x = ”Buna ziua!
D. Funcţia strncat
strncat(dest,sursa,nr); – adaugă la dest primele nr caractere din şirul sursa. Şirul sursa
ramane nemodificat.
Exemplu:
char *x=”Buna ”,*y=”ziua!?”;
strncat(x,y,2); a = ”Buna zi”;
E. Funcţia strchr
strchr(sir,c); – are rolul de a căuta caracterul c in şirul sir. Căutarea se face de la stânga la
dreapta, iar funcţia întoarce adresa subşirului care începe cu prima apariţie a caracterului c.
Dacă nu este găsit caracterul, funcţia returnează 0. Diferenţa dintre adresa şirului iniţial şi cea
a subşirului returnat reprezintă chiar poziţia caracterului căutat în şirul dat.
Exemplu:
char *a=”acesta este un sir”,b=’t’,c=’x’,d;
cout<<strchr(a,b); se tipăreşte ”ta este un sir”;
cout<<strchr(a,c); nu se tipăreşte nimic (sau se tipăreşte 0, dacă se face o conversie la int a
lui strchr(a,c) ;
d= strchr(a,b);
cout<<”Caracterul apare prima data la pozitia ”<<d-a;
113
F. Funcţia strrchr
strrchr(sir,c); – are acelaşi rol cu strchr, cu deosebirea că returneaza adresa ultimei aparitii a
caracterului (căutarea se face de la dreapta spre stânga; r = right)
G. Funcţia strcmp
Funcţia strcmp face distincţie între literele mari şi cele mici ale alfabetului.
Funcţia strcmp returnează diferenţa dintre codurile ASCII ale primelor caractere care nu
coincid.
H. Functia stricmp
int stricmp(sir1,sir2); – are acelaşi rol cu strcmp, cu deosebirea că nu face distincţie între
literele mari şi cele mici ale alfabetului (i = ignore).
I. Funcţia strstr
strstr(sir1,sir2); – are rolul de a identifica dacă şirul sir2 este subşir al sirului sir1. Dacă este,
funcţia returnează adresa de început a subşirului sir2 în şirul sir1, altfel returnează adresa 0.
În cazul în care sir2 apare de mai multe ori în sir1, se returnează adresa de început a primei
apariţii. Căutarea se face de la stânga la dreapta
J. Functia strtok
strtok(sir1,sir2); – are rolul de a separa şirul sir1 în mai multe şiruri (cuvinte) separate între
ele prin unul sau mai multe caractere cu rol de separator. Şirul sir2 este alcătuit din unul sau
mai multe caractere cu rol de separator.
Funcţia strtok acţionează în felul următor:
- Primul apel trebuie să fie de forma strtok(sir1,sir2); Funcţia întoarce adresa primului
caracter al primei entităţi. După prima entitate, separatorul este înlocuit automat prin
caracterul nul.
- Urmatoarele apeluri sunt de forma strtok(NULL,sir2); De fiecare dată, funcţia întoarce
adresa de început a următoarei entităţi, adăugând automat după ea caracterul nul.
- Când şirul nu mai conţine entităţi, funcţia returnează adresa nula.
114
K. Functia strspn
int strspn(sir1,sir2); – are rolul de a returna numărul de caractere ale şirului sir1 (caractere
consecutive care încep obligatoriu cu primul caracter) care se găsesc în şirul sir2.
L. Funcţia strcspn
int strspn(sir1,sir2); – are rolul de a returna numărul de caractere ale şirului sir1 (caractere
consecutive care încep obligatoriu cu primul caracter) care NU se găsesc în şirul sir2.
M. Funcţia strlwr
strlwr(sir); – are rolul de a converti toate literele mari din şir în litere mici. Restul caracterelor
rămân neschimbate.
N. Funcţia strupr
strupr(sir); – are rolul de a converti toate literele mici din şir în litere mari. Restul caracterelor
raman neschimbate
O. Funcţia strbrk
P. Funcţia atof
double atof(sir); – converteşte un şir către tipul double. Dacă această conversie eşuează (se
întâlneşte un caracter nenumeric), valoarea întoarsă este 0. Această funcţie (ca şi cele
similare) necesită includerea bibliotecii stdlib.h.
Q. Funcţia _atold
long double _atold(sir); – converteşte un şir către tipul long double. Dacă această conversie
eşuează, valoarea întoarsa este 0.
P. Funcţia atoi
115
int atoi(sir); – converteşte un şir către tipul int. Dacă această conversie eşuează (se întâlneşte
un caracter nenumeric), valoarea întoarsă este 0.
R. Funcţia atoll
long atol(sir); – converteşte un şir către tipul long. Dacă această conversie eşuează, valoarea
întoarsa este 0.
S. Functia itoa
itoa(int valoare,sir,int baza); – converteşte o valoare de tip int în şir, care este memorat în
variabila sir. Baza reţine baza de numeraţie către care să se facă conversia. În cazul bazei 10,
şirul reţine şi eventualul semn -.
T. Funcţia ltoa
ltoa(long valoare,sir,int baza); – converteşte o valoare de tip long int în şir, care este memorat
în variabila sir.
U. Funcţia ultoa
ultoa(unsigned long valoare,sir,int baza); – converteşte o valoare de tip unsigned long în şir,
care este memorat în variabila sir.
EXEMPLE
#include <iostream.h>
#include <string.h>
void main( ){
char a[100],*p,c;
cin.get(a,100);
cin>>c;
p=strchr(a,c);
while (p){
cout<<"Pozitia "<<p-a<<endl;
p++;
p=strchr(p,c);
}
116
}
#include <iostream.h>
#include <conio.h>
#include <string.h>
void main(){
char text[100],cuv[10][10],*p,*r,separator[]=",. !?";int i=0,nr=0;
clrscr();
cout<<"Dati sirul:";cin.get(text,100);
strcpy(p,text);
p=strtok(p,separator);
while (p)
{strcpy(cuv[++nr],p);
p=strtok(NULL,separator);}
cout<<"Sunt "<<nr<<" cuvinte:"<<endl;
for (i=1;i<=nr;i++) cout<<cuv[i]<<endl;
getch();
}
#include <iostream.h>
#include <conio.h>
#include <string.h>
void main(){
char text[100],cifre[]="0123456789";
clrscr();
cout<<"Dati sirul:";cin.get(text,100);
if (strcspn(cifre,text)==strlen(text))
cout<<"exclusiv numeric";
else cout<<”nenumeric”;
getch();}
117
SUGESTII TEME DE LABORATOR
7. În directorul curent se află fişierul cuvinte.txt, care conţine mai multe linii de text
formate din cuvinte separate de câte un spaţiu. Să se afişeze cuvintele care au cel
puţin 3 consoane sau 3 vocale consecutive.
. Se citeşte un şir de caractere. Să se afişeze şirul oglindit, din care lipsesc vocalele.
10. Dintr-un fişier se citeşte un text. Textul conţine cuvinte separate printr-un spaţiu sau
mai multe. Se va genera un nou fişier, care va conţine textul iniţial, având spaţiile de
prisos eliminate (între cuvinte va rămâne numai câte un spaţiu).
11. Simulaţi comanda REPLACE, astfel încât într-un text veţi înlocui un caracter x, citit
de la tastatură, cu un alt caracter y, citit de la tastatură.
14. Într-un fişier sunt scrise cuvinte pe linii separate. Să se afişeze cuvintele care conţin
majuscule.
118
13. STRUCTURI
Structura este un grup de variabile de tipuri diferite reunite sub acelaşi nume,
ce pune la dispoziţia utilizatorului un mod convenabil de pastrare a informaţiilor legate
între ele.
Declararea unei structuri creează un nou tip de date, ce poate fi folosit pentru
a crea variabile de acel tip, sau pentru a crea direct variabile de acel tip. Variabilele
care fac parte din structură se numesc membri, elemente sau câmpuri ale structurii.
Între membrii structurii exista de obicei o legatură logică.
Spre deosebire de tablouri, fiecare element al unei structuri poate avea propriul
său tip, care poate diferi de tipul oricărui alt element.
struct nume_generic {
tip nume_membrul_l;
tip nume_membrul_2;
tip nume_membrul_n;
} lista_variabile;
unde:
nume_variabilă . nume_membru
unde
nume_variabilă - numele variabilei de tip structură
119
nume membru - numele câmpului ce se doreşte a fi accesat.
Un membru al unei structuri poate fi un tip simplu - unul din tipurile de bază, sau un
tip compus, cum ar fi un tablou de caractere, sau tablourile uni şi multidimensionale.
EXEMPLE
A. Numere complexe
#include<stdio.h>
#include<math.h>
typedef struct{
float a; //partea reala
float b; //partea imaginara
}complex; //numele structurii
120
//modulul unui nr. Complex
float modul(complex z)
{
return sqrt(pow(z.a,2)+pow(z.b,2));
}
complex conjugat(complex z)
{
complex t;
t.a=z.a;
t.b=-z.b;
return t;
}
{
complex z;
z.a=x.a+y.a;
z.b=x.b+y.b;
return z;
}
121
void main()
{
complex z1,z2;
//citirea celor doua nr. complexe
citire(&z1,"z1");
citire(&z2,"z2");
printf("\n");
//afisarea celor doua nr. complexe
afisare(z1,"z1");
afisare(z2,"z2");
printf("\n");
//modulele celor doua nr. complexe
printf("Modulul lui z1: %.2f\n",modul(z1));
printf("Modulul lui z2: %.2f\n",modul(z2));
printf("\n");
//conjugatele celor doua numere complexe
afisare(conjugat(z1),"conjugat z1");
afisare(conjugat(z2),"conjugat z2");
//suma a doua nr. complexe - varianta 1
complex s1;
s1=suma1(z1,z2);
afisare(s1,"z1+z2");
//suma a doua nr. complexe - varianta 2
complex s2;
suma2(z1,z2,&s2);
afisare(s2,"z1+z2");
//produsul a doua nr. complexe
complex p;
p=produs(z1,z2);
afisare(p,"z1*z2");
getchar();
int k;
scanf("%d", &k);
}
122
SUGESTII TEME DE LABORATOR
struct complex {
float a;
float b;
};
struct rational {
long int a;
long int b;
};
struct punct {
float a;
float b;
};
Să se scrie funcţiile pentru calculul distanţei dintre două puncte, calculul pantei şi
ordonatei pentru dreapta care trece prin două puncte, determinarea faptului dacă un punct se
găseşte pe dreapta care uneşte două puncte, calculul ariei şi perimetrului unui triunghi
determinat de trei puncte.
123
14. LISTE
Lista este o colectie de elemente (noduri, celule) înlănţuite, care formează o structură
dinamică, situată in memoria dinamică, în care toate elementele sunt de acelaşi tip (oricât de
complex).
Numărul de elemente este variabil, eventual chiar nul.
O listă este deci o secvenţă de zero, una, sau mai multe elemente, numite noduri, toate
fiind de acelaşi tip.
Spre deosebire de listă, tabloul este o structură statică, situata in memoria dinamică,
în care toate elementele sunt de acelaşi tip, numărul de elemente fiind constant. Un nod al
unei liste liniare apare ca o structura recursivă, având o componentă de tip pointer la
structură, care reprezintă legatura (înlănţuirea) spre nodul următor. Fiecare element
(celulă/nod) din cadrul listei înlanţuite are o structură de tipul:
→ Informaţie utilă;
→ Informaţie de înlanţuire către elemental (nodul) următor;
//pentru listele dublu înlănţuite
→ Informaţie de înlanţuire către elemental (nodul) anterior;
CAP
LISTĂ
Pentru lucrul cu liste simplu înlănţuite, este suficient să se cunoască adresa capului
listei, iar pentru listele dublu înlantuite se poate ajunge la orice nod, dacă se cunoaşte adresa
unui singur nod din listă, oricare ar fi el.
Dacă ultimul nod din listă se pune în legătură cu primul nod, avem de-a face cu o listă
circulară (simplu sau dublu înlănţuită, după cum lista originală era simplu sau dublu
înlănţuită.
124
EXEMPLE
A. Operaţii cu liste liniare dublu înlănţuite
#include <fstream.h>
#include <conio.h>
#include <stdlib.h>
struct nod{
nod * ante;
int val;
nod * post;
};
void creare(){
clrscr();
curent = new nod;
prim=curent;
ultim=curent;
curent->ante=0;
curent->post=0;
cout<<"Introduceti valoarea:";cin>>curent->val;
}
void listarestdr(){
clrscr();
curent=prim;
while(curent->post){
cout<<curent->val<<" ";
curent=curent->post;
}
cout<<curent->val;
getch();
}
void listaredrst(){
clrscr();
curent=ultim;
while(curent->ante){
cout<<curent->val<<" ";
curent=curent->ante;
}
cout<<curent->val;
getch();
}
125
void insmijloc(){
clrscr();
cout<<"In constructie. Voi insera un nod in interior";
getch();
}
void insinaint(){
clrscr();
cout<<"In constructie. Voi insera un nod in prima pozitie";
getch();
}
void inscoada(){
clrscr();
curent=new nod;
cout<<"Introduceti valoarea:";cin>>curent->val;
ultim->post=curent;
curent->ante=ultim;
curent->post=0;
ultim=curent;
}
void stergint(){
clrscr();
cout<<"In constructie. Voi sterge un nod din interior";
getch();
}
void stergprim(){
clrscr();
cout<<"In constructie. Voi sterge primul nod";
getch();
}
void stergult(){
clrscr();
cout<<"In constructie. Voi sterge ultimul nod";
getch();
}
void salvez(){
int i,n;
clrscr();
cout<<"In constructie. Voi salva lista in fisier";
ofstream f("lista.dat");
curent=prim;n=1;
while(curent->post){
126
curent=curent->post;
n++;
}
cout<<"Lista are "<<n<<" noduri"<<endl;
f<<n<<endl;
curent=prim;
for(i=1;i<=n;i++){
f<<curent->val<<endl;
curent=curent->post;
}
getch();
}
void meniu2(){
int rasp;
clrscr();
cout<<"OPERATII CU LISTE :"<<endl<<endl;
cout<<" 1: creare"<<endl<<
" 2: listare de la stanga la dreapta"<<endl<<
" 3: listare de la dreapta la stanga"<<endl<<
" 4: inserare nod in interiorul listei"<<endl<<
" 5: inserare nod pe prima pozitie"<<endl<<
" 6: inserare nod pe ultima pozitie"<<endl<<
" 7: stergerea unui nos din interiorul listei"<<endl<<
" 8: stergerea primului nod"<<endl<<
" 9: stergerea ultimului nod"<<endl<<
"10: salvez lista"<<endl;
cin>>rasp;
switch (rasp){
case 1: creare();break;
case 2: listarestdr();break;
case 3: listaredrst();break;
case 4: insmijloc();break;
case 5: insinaint();break;
case 6: inscoada();break;
case 7: stergint();break;
case 8: stergprim();break;
case 9: stergult();break;
case 10: salvez();break;
default: exit(1);
}
meniu2();
}
int citire(){
int i,n;
clrscr();
127
cout<<"In constructie. Voi citi lista din fisier";
ifstream f("lista.dat");
f>>n;
curent=new(nod);
prim=curent;
ultim=curent;
f>>(curent->val);
for(i=2;i<=n;i++){
curent=new(nod);
f>>(curent->val);
ultim->post=curent;
curent->ante=ultim;
curent->post=0;
ultim=curent;
}
getch();
return 0;
}
void meniu1(){
int rasp;
clrscr();
cout<<"EXPLOATAREA LISTELOR INLANTUITE"<<endl<<endl;
cout<<"1 Citire lista din fisier"<<endl;
cout<<"2 Lista noua"<<endl;
cin>>rasp;
switch (rasp){
case 1: citire();meniu2();break;
case 2: meniu2();break;
default: exit(1);
}
void main(){
clrscr();
meniu1();
}
128
SUGESTII TEME DE LABORATOR
A. Rescrieţi funcţiile din programul de mai sus pentru cazul listelor liniare simplu
înlănţuite
B. Rescrieţi funcţiile din programul de mai sus pentru cazul listelor circulare dublu
înlănţuite
129
15. RECURSIVITATE
long factorial(int n)
{if(n==0) return 1;
else return factorial(n-1)*n;}
void main()
{cout<<factorial(5) ;}
130
Fiecare apel de funcţie lucrează cu datele aflate pe nivelul corespunzator acelui apel.
La ieşirea din apelul unei funcţii, nivelul respectiv se eliberează şi datele aflate acolo se pierd.
Există posibilitatea ca subprogramul să lucreze direct cu variabilele globale, dar în acest caz,
subprogramul işi pierde independenţa.
Observaţii :
- în cazul unui număr mare de autoapelări, există posibilitatea ca segmentul de stivă
sa se ocupe total, caz în care programul se va termina cu eroarea STACK
OVERFLOW. Aceasta se întamplă mai ales atunci când condiţia de terminare este
pusă greşit şi subprogramul se apelează la nesfârşit.
- pentru orice algoritm recursiv există unul iterativ, care rezolvă aceeaşi problemă.
- mecanismul recursivităţii înlocuieste instrucţiunile repetitive.
- datorită faptului că la fiecare autoapel se ocupă o zonă de memorie, recursivitatea
este eficientă numai dacă numărul de autoapelări nu este prea mare, pentru a nu se
ajunge la umplerea zonei de memorie alocată.
- recursivitatea oferă avantajul unor soluţii mai clare pentru probleme şi a unei
lungimi mai mici a programului. Ea prezintă însă dezavantajul unui timp mai
mare de execuţie şi a unui spaţiu de memorie alocată mai mare. Este de preferat
ca, atunci când programul recursiv poate fi transformat cu uşurinţă într-unul
iterativ, să se facă apel la cel din urmă.
EXEMPLE
A. factorial recursiv
#include <conio.h>
#include <iostream.h>
131
}
void main(){
int n;
clrscr();
cout<<"n=";cin>>n;
cout<<n<<"!="<<fact(n);
getch();
}
B. Euclid recursiv
#include<iostream.h>
#include<conio.h>
void main(){
clrscr();
int a, b, c;
cout<<"a=";cin>>a;
cout<<"b=";cin>>b;
c=euclid(a,b);
cout<<"cmmdc="<<c;
getch();
}
#include <conio.h>
#include <stdlib.h>
#include <iostream.h>
int nl,nc,i,j,l,c,a[100][100];
char r;
132
if ((c>1)&&(a[l][c-1]==0)) umplu(l,c-1);
if ((c<nc)&&(a[l][c+1]==0)) umplu(l,c+1);
if ((l<nl)&&(a[l+1][c]==0)) umplu(l+1,c);
}
void main(void){
r='d';
clrscr();
cout<<'\333';
cout<<"linii=";cin>>nl;
cout<<"coloane=";cin>>nc;
randomize();
for (i=1;i<=nl;i++)
for (j=1;j<=nc;j++)
a[i][j]=random(2);
clrscr();
cout<<" ";
for (j=1;j<=nc;j++) cout<<j%10;cout<<endl<<endl;
for (i=1;i<=nl;i++) {
cout<<i%10<<" ";
for (j=1;j<=nc;j++) cout<<a[i][j];
cout<<endl;
}
cout<<endl;
while (r=='d') {
cout<<"linia=";cin>>l;
cout<<"coloana=";cin>>c;
umplu(l,c);
clrscr();
cout<<" ";
for (j=1;j<=nc;j++) cout<<j%10;cout<<endl<<endl;
for (i=1;i<=nl;i++) {
cout<<i%10<<" ";
for (j=1;j<=nc;j++) cout<<a[i][j];
cout<<endl;
}
cout<<endl;
cout<<"continuati?";r=getch();
cout<<endl;
}
}
133
SUGESTII TEME DE LABORATOR
n −1 m=0
A(m, n) = A(m − 1, 1) n=0
A(m − 1, A(m, n − 1))
altfel
134
16. METODA DIVIDE ET IMPERA
#include<iostream.h>
#include<conio.h>
135
int V[MAX_N], N, K;
int main() {
// se citeste vectorul si valoarile N si K
cout << binarySearch( 1, N );
}
EXEMPLE
#include <fstream.h>
#include <conio.h>
void main(){
clrscr();
int x, y, z, rasp, gasit=0;
char fis[20];
cout<<"numele fisierului de intrare : ";cin>>fis;
ifstream f(fis);
f>>x>>y;
cout<<x<<" "<<y<<endl;
while((!gasit)&&(x<=y)){
z=(x+y)/2;
f>>rasp;
if(rasp) gasit=1;
else{
f>>rasp;
if(rasp)y=z-1;
else x=z+1;
}
}
if(gasit)cout<<z;
else cout<<"0";
getch();
136
f.close;
}
Dată fiind o listă neordonată, o împărţim în două liste, de dimensiuni egale sau foarte
apropriate, ordonăm cele două liste (folosind acelaşi algoritm) şi apoi efectuăm operaţia
"merge", adică vom combina cele două liste ordonate într-una singură, obţinând astfel lista
originală ordonată.
Operaţia principală care se efectuează este comparaţia dintre elementele listei.
#include<iostream.h>
#include<conio.h>
int a[1000],n;
void interclas(int i,int m,int j)
{int b[1000];
int x=i;
int k=1;
int y=m+1;
while(x<=m && y<=j)
if(a[x]<a[y]) b[k++]=a[x++];
else b[k++]=a[y++];
while(x<=m) b[k++]=a[x++];
while(y<=j) b[k++]=a[y++];
int t=i;
for(k=1;k<=(j-i)+1;k++)
a[t++]=b[k];
}
int divimp(int i, int j)
{if(i<j) {int m=(i+j)/2;
divimp(i,m);
divimp(m+1,j);
interclas(i,m,j);}
}
void main()
{clrscr();
cout<<”n= ”; cin>>n;
for(int i=1;i<=n;i++)
{cout<<”a[”<<i<<”]= ”;
cin>>a[i];
}
divimp(1,n);
for(i=1;i<=n;i++)
cout<<a[i]<<” ”;
getch();
137
C. Turnurile din Hanoi
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <iostream.h>
void main(void){
int n;
cout<<"n=";cin>>n;
hanoi(n,1,2,3);
}
138
SUGESTII TEME DE LABORATOR
D. Să se caute o valoare într-un vector. Dacă se găseşte, se va afişa poziţia pe care s-a
găsit, altfel se va afişa un mesaj.
F. Să se numere câte valori sunt egale cu x dintr-un şir de numere întregi citite
139
17. METODA BACKTRACKING
Metoda Backtracking se aplică problemelor care îndeplinesc simultan două condiţii:
a. necesită o soluţie de tip vector, x, cu x ∈ Ai , unde Ai sunt mulţimi finite şi ordonate
b. nu dispunem de altă metodă mai rapidă.
Metoda foloseşte pentru generarea produsului cartezian o stivă, al cărui nivel îl notăm
cu st. În momentul în care dispunem de o soluţie parţială (din produsul
A1 × A2 × × Ast , unde st < n ), variabila st este incrementată, pentru a căuta o soluţie mai
complexă (eventual completă), din produsul cartezian A1 × A2 × × Ast +1 .
Dacă nici un element din Ast nu mai poate constitui o soluţie, variabila st este
decrementată (momentul de “Backtracking”), pentru a încerca alegerea unui nou element din
Ast , care ar putea, eventual, contribui la construcţia unei soluţii parţiale.
Validitatea unei soluţii (parţiale sau totale) este determinată cu ajutorul unei funcţii-
utilizator notată bun şi care primeşte ca parametru valoarea parametri vectorul soluţie x şi
valoarea variabilei st.
Dacă x[st] îndeplineşte condiţiile problemei, funcţia bun întoarce valoarea 1, altfel
întoarce valoarea 0.
#include <conio.h>
#include <iostream.h>
void main(){
clrscr();
int n, x[20], m[20], i, j, p, st;
cout<<"dimensiunea problemei "; cin>>n;
for(i=1; i<=n; i++){
cout<<"pana la cat merge x["<<i<<"] ? ";
cin>>m[i];
}
st=1;x[1]=0;
while(st){
p=0;
while((!p) && (x[st]<m[st])){
140
x[st]++;
if(bun(x,st)) p=1;
}
if(!p) st--;
else if(st<n) {
st++;
x[st]=0;
}
else {
for(j=1; j<=n; j++) cout<<x[j]<<" ";
cout<<endl;
}
}
getch();
}
EXEMPLE
A. Generarea produsului cartezian
Nici o modificare nu este necesară în funcţia bun; programul construieşte în mod nativ
produsul cartezian.
În problema noastră, n joacă rolul lui k, iar m[1] = m[2] = … = m[n] joacă rolul lui n
141
Funcţia bun este
În problema noastră, n joacă rolul lui k, iar m[1] = m[2] = … = m[n] joacă rolul lui n
Funcţia bun este
142
SUGESTII TEME DE LABORATOR
C. Problema drapelelor
Având la dispoziţie c culori, să se determine toate drapelele tricolore care se pot realize,
astfel încât să nu avem două culori successive identice
143
18. GRAFURI ŞI ARBORI
Se numeşte graf neorientat un graf G = (V, E) în care relaţia binară este simetrică:
(v,w)∈E atunci (w,v) ∈E.
DEFINIŢII
Se numeşte nod orice element al mulţimii V, unde G = (V, E) este un graf neorientat.
Se numeşte muchie orice element al mulţimii E ce descrie o relaţie existentă între
două vârfuri din V, unde G = (V, E) este un graf neorientat;
Adiacenţă: Într-un graf neorientat existenţa muchiei (v,w) presupune că w este
adiacent cu v şi v adiacent cu w.
Incidenţă: o muchie este incidentă cu un nod dacă îl are pe acesta ca extremitate.
Muchia (v,w) este incidentă în nodul v respectiv w.
Grad: gradul unui nod v, dintr-un graf neorientat, este un număr natural ce reprezintă
numărul de noduri adiacente cu acesta (sau numarul de muchii incidente cu nodul respectiv)
Nod izolat : un nod cu gradul 0.
Nod terminal: un nod cu gradul 1
Lanţ: o secvenţă de noduri ale unui graf neorientat G=(V,E), cu proprietatea că
oricare două noduri consecutive din lant sunt adiacente:
L = [w 1 , w 2 , w 3 ,. . ,w n ] cu proprietatea că (w i , w i+1 ) ∈ E pentru 1 ≤i <n.
Lungimea unui lanţ: numărul de muchii din care este format.
Lanţ simplu: lanţul care conţine numai muchii distincte
Lanţ compus: lanţul care nu este format numai din muchii distincte
Lanţ elementar: lanţul care conţine numai noduri distincte
Ciclu: un lanţ în care primul nod coincide cu ultimul.
Ciclu elementar: ciclu format doar din noduri distincte, excepţie făcând primul şi
ultimul. Lungimea unui ciclu nu poate fi = 2.
Graf parţial : dacă dintr-un graf G = (V,E) se suprimă cel puţin o muchie, atunci noul
graf G’ = (V,E’), E’⊂ E se numeşte graf parţial al lui G.
Subgraf: dacă dintr-un graf G = (V,E) se suprimă cel puţin un nod, împreună cu
muchiile incidente lui, atunci noul graf G’ = (V’,E’), E’⊂ E si V’⊂V se numeşte subgraf al
grafului G.
Graf regulat : graf neorientat în care toate nodurile au acelaşi grad;
144
Graf complet: graf neorientat G = (V,E) în care există muchie între oricare două
noduri. Numărul de muchii ale unui graf complet este: nr*(nr-1)/2, unde nr este numarul de
noduri
Graf conex : graf neorientat G = (V,E) în care pentru orice pereche de noduri (v,w)
există un lanţ care le uneşte.
Componentă conexă: subgraf al grafului de referinţă, maximal în raport cu
proprietatea de conexitate (între oricare două vârfuri există lanţ);
Lanţ hamiltonian: un lanţ elementar care conţine toate nodurile unui graf
Ciclu hamiltonian: un ciclu elementar care conţine toate nodurile grafului
Graf Hamiltonian: un graf G care conţine un ciclu hamiltonian
Lanţ eulerian: un lanţ simplu care conţine toate muchiile unui graf
Ciclu eulerian: un ciclu simplu care conţine toate muchiile grafului
Graf eulerian: un graf care conţine un ciclu eulerian. Condiţie necesară şi suficientă:
un graf este eulerian dacă şi numai dacă oricare vârf al său are gradul par.
Matricea de adiacenţă
1 daca [i, j ] ∈ E
a[i, j ] =
0 altfel
Observaţii:
145
Vectori de muchii
Fiecare arc al grafului poate fi privit ca o înregistrare cu două componente, în speţă cele două
noduri care constituie extremităţile arcului:
- nod_in -> nodul din care iese arcul (“nodul de început” al arcului);
- nod_sf -> nodul în care intră arcul (“nodul de sfârşit” al arcului);
type ARC=record
nod_in, nod_sf: integer;
end;
Graful în ansamblul său, este o mulţime de arce, adică o mulţime de elemente de tipul
ARC. În consecinţă, definim graful ca un “vector de arce”, adică un vector de elemente de
tipul ARC:
Numărul real de elemente este numărul de arce m. Astfel, elementele efectiv folosite ale
vectorului vor fi v[1], v[2],…, v[m]. Fiecare element {1, 2, …, m}) este de tipul ARC şi
reprezintă unev[i] (cu i arc al grafului, având două componente:
v[i].nod_in şi v[i].nod_sf -> nodurile extremităţi ale arcului.
Aceste moduri de reprezentare (prin matrice de adiacenţă, prin liste de vecini, sau prin
vectori de muchii) se folosesc după natura problemei. Dacă în problemă se doreşte un acces
frecvent la muchii, atunci se va folosi matricea de adiacenţă, sau vectorul de muchii; dacă
numarul de muchii care se reprezintă este mult mai mic dect n x n, este de preferat sa se
folosescă listele de adiacenţă, pentru a se face economie de memorie.
EXEMPLE
#include<iostream.h>
int n,i,j,m,a[10][10];
int main()
{
cout<<"Dati nr. de vf. ";cin>>n;
cout<<"Dati elementele matricei de
adiacenta"<<endl; for(i=1;i<=n-1;i++)
for(j=i+1;j<=n;j++)
{
146
cout<<"a["<<i<<"]["<<j<<"]=";
cin>>a[i][j];
a[j][i]=a[i][j];
}
// determinarea nr.
de muchii m=0;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++
)
if(a[i][j]==1
) m++;
m=m/2;
if(m == n * (n-1)/2)
cout<<"Graf
complet";
else
cout<<"Graful nu este complet";
}
Exemplul 2. Să se verifice dacă o succesiune de vârfuri date este ciclu pentru un graf. În caz
afirmativ, să se stabilească dacă este sau nu ciclu elementar
#include<iostream.h>
int
n,i,j,m,a[10][10],x
[50]; int main()
{
cout<<"Dati nr. de vf. ";cin>>n;
cout<<"Dati elementele matricei de
adiacenta"<<endl; for(i=1;i<=n-1;i++)
for(j=i+1;j<=n;j++){
cout<<"a["<<i<<"]["<<j<<"]=";
cin>>a[i][j];
a[j][i]=a[i][j];
}
// citirea succesiunii de
vf. cout<<"Dati nr. de
muchii ";cin>>m;
for(i=1;i<=m;i++)
{
cout<<"x["<<i<<"]=";
cin>>x[i];
}
// verificare daca este lant: vf. consecutive sa fie
muchii in matrice int lant=1;
for(i=1;i<=n-1;i++)
if(a[x[i]][x[i+1]]==0) lant=0;
147
// primul si ultimul vf. sunt egale
int
ok=1;
if(lant
==1)
if(x[1]!=x[m]) ok=0;
// muchii
distincte int
ciclu=1;
if(ok==1)
for(i=1;i<=m-
2;i++)
for(j=i+1;j<=
m;j++)
if(x[i]==x[j] && x[i+1]==x[j+1] || x[i]==x[j+1]
&& x[i+1]==x[j])
ciclu=0;
// verificare daca este ciclu elementar: vf. sa fie distincte doua cate
doua, mai putin primul si ultimul
if(ciclu==1 &&
x[1]==x[m])
cout<<"Ciclu
elementar";
else
cout<<"Ciclu neelementar";
}
#include<fstream.h>
fstream
f("date.in",ios::in);
fstream
g("date2.in",ios::in);
int a[100][100],b[100][100],n,m,k,l;
int subgraf()
{for(int
i=1;i<=k;i++)
for(int
j=1;j<=k;j++)
if(a[i][j]!=b[i][j])
return 0; return 1;
}
148
int main(void)
{
int x,y,i;
f>>n>>m;
for(i=1;i<=
m;i++)
{
f>>x>>y;
a[x][y]=1;
a[y][z]=1;
}
g>>k>>l;
for(i=1;i<=l;i++)
{
g>>x>>y;
b[x][y]=1;
b[y][x]=1;
}
}
if(subgraf())
cout<<"da";
else
cout<<"nu";
}
#include
<fstream.h>
#include
<vector.h>
ifstream
fin("date.in");
ofstream
fout("date.out");
struct muchie
{
int i,j;
};
int A[50][50]; // matrice de adiacenta
muchie
M[1000]; // vector muchiilor
149
int V1[50],
V2[50]; // liste de vecini
//n – nr. de noduri, m – nr. de
int n,m; muchii
void citire()
{
int x,y,i;
fin>>n>>m
;
for(i=1;i<=
m;i++)
{
fin>>x>>y;
M[i].i=x;
M[i].j=y;
A[x][y]=A[y]
[x]=1;
V1[i]=x;
V2[i]=y;
}
}
void afisare()
{
int i,j;
fout<<"matricea de
adiacenta:\n";
for(i=1;i<=n;i++)
{
for(j=1;j<=n
;j++)
fout<<A[i][j]
<<" ";
fout<<endl;
}
fout<<"lista muchiilor:\n";
for(i=1;i<=m;i++) fout<<M[i].i<<"
"<<M[i].j<<endl; fout<<"lista
vecinilor:\n";
for(i=1;i<=n;i++)
{
fout<<i<<": ";
fout<<V1[i]<<","<<V2[
i]<< " "; fout<<endl;
}
fin.close();
fout.close();
150
}
int main()
{
citire();
afisare();
}
a) Să se identifice mulţimea X
b) Să se identifice mulţimea U
c) Să se calculeze gradele nodurilor impare
d) Să se verifice dacă graful are vârfuri izolate; dacă da, să se afişeze nodul, dacă nu, să se
afişeze un mesaj
#include<iostream.h>
#include<conio.h>
#include<fstr
eam.h> int
a[20][20];
int main()
{
ifstream
f(“matricegraf.in”);
int n,i,j,k;
f>>n;
//a)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
f>>a[i][j];
cout<<”X=”;
for(i=1;i<=
n;i++)
cout<<i<<
” “;
cout<<endl
;
//b)
cout<<”U=”;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(a[i][j]==1&&i<j) cout<<”(“<<i<<”,”<<j<<”)”;
//c)
151
for(i=1;i<=n;i++)
{
k=0;
for(j=1;j<=n;j++)
if(i%2==1&&a[i][j]=
=1) k++;
if(i%2==1) cout<<endl<<”gradul nodului “<<i<<” este
“<<k<<endl;
}
//d)
for(i=1;i<=n;i++)
{
r=0;
for(j=1;j<=n;j++)
if(a[i][j]==1) return 0;
}
Exemplul 6. Se citeşte de pe prima linie din fişierul graf.in numărul de vârfuri şi numărul de
muchii(n,m), iar de pe următoarele m rânduri, perechi de vârfuri reprezentând muchiile
grafului.
#include<iostream.h>
#include<conio.h>
#include<fstr
eam.h> int
a[20][20],v[4
0]; int main()
{
ofstream
g(“mat.out”);
ifstream
f(“graf.in”);
int
j,i,n,m,h,r=0,x,y,o
k=0; f>>n;
f>
>
m
;
//
a)
for(i=1;i<=m;i++)
{
152
f>>x;
f>>y;
a[x][y]=a[y][x]=1;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=
n;j++)
g<<a[i][j]
<<” “;
g<<endl;
}
//b)
for(i=1;i<=n;i++)
{
h=0;
for(j=1;j<=n;
j++)
if(a[i][j]==
1) h++;
r++;
v[r]=h;
}
for(i=1;i<=n;i++)
cout<<”nodul “<<i<<”are rangul “<<v[i]<<endl;
// c)
for(i=1;i<=
n;i++)
if(v[i]==0)
ok=0; else
ok=1;
if(ok==0) cout<<”Graful are varfuri
izolate”<<” “; else cout<<”Graful nu
are varfuri izolate”<<” “;
}
#include<iostream.h>
#include<conio.h>
#include<fstr
eam.h> int
a[20][20];
int main()
{
153
int
i,j,n,m,x,y,k;
ifstream
f(“graf.txt”);
f>>n;
f>>m;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
f>>x;
f>>y;
a[x][y]=1;
a[y][x]=1;
}
for(i=1;i<=
n;i++)
{
for(j=1;j<=n;j+
+)
cout<<a[i][j
]<<” “;
cout<<endl;
}
for(i=1;i<=n;i++)
{
k=0;
for(j=1;j<=n;j
++)
if(a[i][j]==
1) k++;
if(k==1) cout<<”Nod terminal
=”<<i<<endl; if(k>1) cout<<”Nod
intermediar= “<<i<<endl;
}
}
#include <fstream.h>
#include <conio.h>
#include <stdlib.h>
struct nod{
int nr;
nod * ante;
154
};
void bf(){
nod *q;
while(ic<=sc){
q=l[c[ic]];
while(q){
if(!v[q->nr]){
sc++;
c[sc]=q->nr;
v[q->nr]=1;
}
q=q->ante;
}
ic++;
bf();
}
}
void bfm(){
int q;
while(ic<=sc){
for(q=k;q<=n;q++)
if((!v[q])&&(m[c[ic]][q])){
sc++;
c[sc]=q;
v[q]=1;
}
ic++;
bfm();
}
}
155
cout<<k<<" ";
q=l[k];
v[k]=1;
while(q){
if(!v[q->nr])df(q->nr);
q=q->ante;
}
}
void main(){
clrscr();
for(k=1;k<=nrarc;k++){
f>>i;
f>>j;
m[i][j]=1;
}
ifstream g(numefis);
g>>n;g>>nrarc;
for(k=1;k<=nrarc;k++)l[k]=0;
156
for(k=1;k<=nrarc;k++){
g>>i;
g>>j;
cout<<k<<" : arc de la "<<i<<" la "<<j<<endl;
p=new nod;p->nr=j;p->ante=l[i];l[i]=p;
p=new nod;p->nr=i;p->ante=l[j];l[j]=p;
} getch();
g.close();
cout<<endl<<"listele de adiacenta :"<<endl<<endl;
for(i=1;i<=n;i++){
cout<<i<<" ";
p=l[i];
while(p){
cout<<p->nr<<" ";
p=p->ante;
}
cout<<endl;
}
157
cout<<endl<<endl<<"strabaterea in adancime (cazul matricei de
adiacenta) :"<<endl;
cout<<"nodul de inceput :";
cin>>k;
cout<<endl;
for(i=1;i<=n;i++)v[i]=0;
dfm(k);
getch();
}
158
18.2 GRAFURI ORIENTATE
Un graf orientat reprezintă o pereche ordonată de mulţimi G=(X,U), unde X este o mulţime
finită şi nevidă, numită mulţimea nodurilor, şi U este o mulţime formată din perechi ordonate
de elemente ale lui X, numită mulţimea arcelor.
Exemplu
In graful din figura1, mulţimea nodurilor este X={1, 2, 3, 4, 5, 6, 7} iar mulţimea arcelor este
U={u 1 , u 2 , u 3 , u 4 , u 5 , u 6 , u 7 , u 8 , u 9 , u 10 }={(1,2), (2,3), (3,4), (4,1), (3,5), (5,3), (5,6), (6,7),
(7,6), (7,5)}.
Exemplu
Graful este conex pentru că, oricum am lua două noduri, putem ajunge de la unul la celălalt
pe un traseu de tip lanţ. De exemplu, de la nodul 4 la nodul 2 putem ajunge pe traseul de
159
noduri (4,3,2), stabilind astfel lanţul {u 5 , u 3 }, dar şi pe traseul de noduri (4,1,2) stabilind
lanţul {u 6 , u 2 }
Componenta conexa
Componenta conexă a unui graf G=(X, U) este un subgraf G 1 =(X 1 , U 1 ) conex, a lui G, cu
proprietatea că nu există nici un lanţ care să lege un nod din X 1 cu un nod din X-X 1 (pentru
orice nod, nu există un lanţ între acel nod şi nodurile care nu fac parte din subgraf).
De exemplu, graful din figura 3 nu este conex , însa în el distingem două componente conexe:
G 1 =(X 1 , U 1 ), unde X 1 ={1,2,3} şi U 1 ={u 1 , u 2 , u 3 }; şi G 2 =(X 2 , U 2 ), unde X 2 ={4,5,6} şi
U 2 ={u 4 , u 5 }.
Graful tare conex este un graf orientat G=(X, U), dacă pentru oricare două noduri x şi y
aparţin lui X, există un drum de la x la y, precum şi un drum de la y la x.
Exemplu
160
Graful cu n=3 din figura 4 este tare conex.
Pentru a demonstra acest lucru, formăm toate perechile posibile de noduri distincte (x, y), cu
x, y aparţând mulţimii {1,2,3} şi, pentru fiecare astfel de pereche, căutam un drum de la x la
y şi un drum de la y la x.
x=1, y=2
De la 1 la 2 – drumul [1,3,2], pe arcele (1,3) si (3,2);
De la 2 la 1 – drumul [2,3,1], pe arcele (2,3) si (3,1).
x=1, y=3
De la 1 la 3 – drumul [1,2,3], pe arcele (1,2) si (2,3);
De la 3 la 1 – drumul [3,2,1], pe arcele (3,2) si (2,1).
x=2, y=3
De la 2 la 3 – drumul [2,1,3], pe arcele (2,1) si (1,3);
De la 3 la 2 – drumul [3,1,2], pe arcele (3,1) si (1,2).
Un subgraf se obţine dintr-un graf G= (X, U) eliminând nişte vârfuri şi păstrând doar acele
muchii care au ambele extremităţi în mulţimea vârfurilor rămase.
Fie un subgraf tare conex G 1 =(X 1 , U 1 ) al grafului G=(X, U). Adăugăm la subgraf un nod x,
care nu face parte din mulţimea nodurilor sale (x apartine X-X 1 ). Obţinem astfel mulţimea de
vârfuri X 1 reunit cu {x}. Subgraful indus pe mulţimea X 1 reunit cu {x} se obţine luând şi
arcele care trec prin nodul x.
Dacă acest subgraf nu mai este tare conex, atunci el se numeşte componentă tare conexă.
Exemplu
161
Am obţinut astfel subgraful tare conex G 1 =(X 1 , U 1 ).
Acestui graf îi adăugam un nod x care nu face parte din mulţimea nodurilor subgrafului G 1 .
Matricea costurilor
Considerăm un graf orientat G=(X,U) cu n noduri, în care fiecărui arc îi este asociat un număr
întreg numit cost. Semnificaţia acestui cost poate fi foarte variată, în funcţie de domeniul pe
care îl descrie graful. De exemplu, dacă graful reprezintă harta unui oraş în care arcele sunt
străzile iar nodurile sunt intersecţiile dintre stăyi, atunci putem vorbi despre costul deplasării
unui automobil între două intersecţii, de-a lungul unei străzi. Acesta s-ar putea măsura în
cantitatea de benzină consumată, calculată prin prisma lungimii străzii în m sau in km.
Pentru evidenţierea costurilor tuturor arcelor unui graf cu n noduri se poate defini o matrice a,
cu n linii *n coloane.există două forme ale acestei matrici:
Forma a): Fiecare element a[i,j] poate fi:
-c, dacă există un arc de cost c>0 între nodurile i şi j;
-0, dacă i=j;
-+∞, dacă nu există arc între nodurile i şi j.
Forma b): Este absolut similară, cu singura deosebire că în loc de +∞ avem -∞.
Forma a)se foloseşte pentru determinarea drumurilor de cost minim între două noduri, iar
forma b) este utilizată în aflarea drumurilor de cost maxim.
Dacă dorim să citim matricea costurilor, evident că nu putem introduce de la tastatură “+∞”!
În loc de “+∞” vom da un num[r de la tastatură foarte mare.
162
Problema determinării drumului minim/ maxim între două noduri face obiectul algoritmului
următor.
Algoritmul Roy-Floyd
Se consideră un graf orientat cu n noduri, pentru care se dă matricea costurilor în forma a).
Se cere ca, pentru fiecare pereche de noduri (i, j), să se tipărească costu drumului minim de la
i la j.
Plecăm de la următoarea idee: dacă drumul minim între două noduri oarecare i şi j trece
printr-un nod k, atunci drumurile de la i la k şi de la k la j sunt la rândul lor minime. Pentru
fiecare pereche de noduri (i, j ), cu i, j ∈{1,2,…,n}, procedăm astfel:
Dăm lui k pe rând valorile 1,2,…,n, pentru ca nodul k despre care vorbeam mai sus poate fi,
cel puţin teoretic, orice nod al grafului. Pentru fiecare k:
dacă suma dintre costul drumului de la i la j şi costul drumului de la k la j este mai mică decât
costul drumului de la i la j {a[i, k]+a[k, j]<a[i, j]}, atunci drumul iniţial de la i la j este
înlocuit cu drumul indirect i→k→j. această înlocuire fireşte că se va opera ca atare în
matrocea costurilor: {a[i, j]:=a[i, k]+a[k, j]}.
Observaţii
Drumurile minime între toate nodurile se regăsesc la finele algoritmului tot în matricea
costurilor, care a suferit n trasformări, pentru k=1,2,…,n.
Unele elemente pot fi +∞, iar pentru simularea lui +∞ am spus că se introduce un număr
întreg foarte mare. Prin adunări repetate a două numere întregi foarte mari putem ajunge la un
rezultat care depăşeşte cea mai mare valoare posibilă de tipul integer. De aceea, recomandăm
ca elementele matricei costurilor să fie de tipul longint.
În cazul în care problema cerea pentru fiecare pereche de noduri (i, j) costul drumului maxim,
modificările necesare ar fi minore :
se foloseşte forma b) a matricei costurilor;
condiţia testată în linia if devine “a[i, k]+a[k, j]<a[i, j]”
#include <conio.h>
#include <fstream.h>
int n;
float m[50][50];
163
}
void main(){
clrscr();
int i,j,k,ni,nf;
char numefis[20];
cout<<"numele fisierului de intrare ? ";cin>>numefis;
ifstream f(numefis);
f>>n;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
f>>m[i][j];
for(i=1;i<=n;i++){
for(j=1;j<=n;j++) if(m[i][j]>1.e+19)cout<<"ì ";else cout<<m[i][j]<<" ";
cout<<endl;
}
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(m[i][j]>m[i][k]+m[k][j]) m[i][j]=m[i][k]+m[k][j];
cout<<endl<<endl;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++) if(m[i][j]>1.e+19)cout<<"ì ";else cout<<m[i][j]<<" ";
cout<<endl;
}
cout<<endl<<endl;
cout<<"nodul initial ";cin>>ni;
cout<<"nodul final ";cin>>nf;
if(m[ni][nf]<1.e20){
cout<<ni<<" ";
drum(ni,nf);}
else cout<<"nu exista drum de la "<<ni<<" la "<<nf;
getch();
}
Să se rescrie toate funcţiile din programul de mai sus pentru cazul grafurilor orientate
164
19. MINICULEGERE DE PROBLEME
LABORATOR IF – THEN - ELSE
BONUS
9. Se citeşte de la tastatură un număr natural n, mai mare decât 100 şi mai mic decât
65000. Să se elimine din număr cifra (cifrele) din mijloc şi să se afişeze numărul
obţinut. *
*
Dacă numărul n are un număr impar de cifre, se elimină o singură cifră, iar dacă numărul n
are un număr par de cifre, se elimină două cifre.
165
LABORATOR CICLUL FOR
166
LABORATOR WHILE ŞI DO WHILE
2. Aplicând faptul că n%10 reprezintă ultima cifră a unui număr natural n, iar operaţia
n=n/10 realizează “scurtarea” cu o cifră a lui n, să se afişeze pe monitor cifrele
constitutive ale unui număr natural n, introdus de la tastatură
6. Utilizând algoritmul lui Euclid cu scăderi repetate, să se afişeze cmmdc (cel mai mare
divizor comun) pentru două numere naturale a şi b, introduse de la tastatură, precum
şi numărul de paşi efectuaţi
7. Utilizând algoritmul lui Euclid cu împărţiri repetate, să se afişeze cmmdc (cel mai
mare divizor comun) pentru două numere naturale a şi b, introduse de la tastatură,
precum şi numărul de paşi efectuaţi
8. Să se calculeze cmmmc (cel mai mic multiplu comun) pentru două numere naturale a
şi b, introduse de la tastatură
167
LABORATOR TABLOURI UNINIMENSIONALE
BONUS:
SUPERBONUS
168
LABORATOR TABLOURI BIDIMENSIONALE
11 12 13 …
21 22 23 …
31 32 33 …
…………...
având nl linii şi nc coloane (nl şi nc valori mai mici decât 10, citite de la tastatură)
BONUS
9. Să se găsească (dacă există) “punctele şa” ale unei matrice citite de la tastatură (un
element al unei matrice este punct şa dacă este simultan maximul de pe linia sa ŞI
minimul de pe coloana sa, sau este simultan minimul de pe linia sa ŞI maximul de pe
coloana sa)
169
LABORATOR ŞIRURI DE CARACTERE
3. Se citeşte un text. Textul conţine cuvinte separate prin unul sau mai multe spaţii. Să se
determine câte cuvinte conţine textul.
6. Să se afişeze vocalele dintr-un text, în ordinea în care apar ele, apoi, pe rândul
următor, numărul de apariţii pentru fiecare din cele 5 vocale posibile, în ordinea lor
alfabetică
BONUS
10. Se citeşte un text. Textul conţine cuvinte separate prin unul sau mai multe spaţii. Să se
afişeze pe monitor textul în care cuvintele îşi păstrează poziţia iniţială, dar fiecare
dintre ele este inversat
170
LABORATOR SUBPROGRAME (1)
3. Subprogramul aparcifra are doi parametri: numar – un număr natural lung şi cifra –
un număr natural de o singură cifră. Programul returnează numărul de apariţii ale
cifrei cifra în numărul numar. Programul principal citeşte de la tastatură un număr n,
care reprezintă numărul de elemente ale unui vector v, ale cărui elemente se vor citi de
la tastatură, şi o cifră – c. Cu ajutorul subprogramului aparcifra, elementele
vectorului v se vor înlocui fiecare cu numărul de apariţii ale cifrei c în respectivul
element; vectorul nou format se va tipări pe monitor.
171
LABORATOR SUBPROGRAME (2)
172
LABORATOR RECURSIVITATE ŞI DIVIDE ET IMPERA
6. Să se scrie un program recursiv care caută un număr k într-un şir ordonat, returnând
poziţia acestuia, în cazul în care numărul a fost găsit, şi -1, în cazul în care numărul nu
a fost găsit
173