Algoritmi și date
Introducere în conceptul de algoritm, proprietăți și modalități de reprezentare, alături de tipuri de date utilizate în programare.
Bacalaureat
Ce este un algoritm și ce proprietăți trebuie să aibă
Un algoritm este o succesiune finită de pași, descriși fără ambiguitate, care pornește de la niște date de intrare și produce, într-un timp finit, date de ieșire — adică rezultatul problemei. Nu orice listă de instrucțiuni este algoritm: ea trebuie să respecte trei proprietăți care se cer explicit la examen.
- Finitudine – algoritmul se termină după un număr finit de pași, pentru orice date de intrare valide. O buclă care nu se oprește niciodată încalcă exact această proprietate.
- Determinism (claritate) – fiecare pas este precis definit: în aceeași situație, algoritmul face întotdeauna același lucru. Nu există pași de tipul „alege o valoare oarecare”.
- Generalitate (universalitate) – algoritmul rezolvă o clasă întreagă de probleme, nu un singur caz particular. Un algoritm care calculează media a două numere trebuie să funcționeze pentru orice pereche de numere, nu doar pentru 4 și 6.
Confuzia clasică: elevii cred că un program care merge pe exemplul din enunț este corect. Corectitudinea se judecă pe toate datele de intrare posibile — inclusiv cazurile-limită: zero, numere negative, șirul cu un singur element.
Reprezentarea algoritmilor: pseudocod și schemă logică
Același algoritm poate fi descris în mai multe moduri. La BAC se folosește pseudocodul — un limbaj intermediar între limbajul natural și limbajul de programare, cu cuvinte-cheie românești standardizate:
- citește a, b – preluarea datelor de intrare;
- scrie rezultat – afișarea datelor de ieșire;
- x ← expresie – atribuirea: valoarea expresiei din dreapta se memorează în variabila din stânga;
- dacă condiție atunci ... altfel ... – decizia;
- cât timp condiție execută ... – repetarea cu test inițial;
- repetă ... până când condiție – repetarea cu test final;
- pentru i ← a, b execută ... – repetarea cu contor.
Schema logică exprimă același lucru grafic: oval pentru start/stop, paralelogram pentru citire/scriere, dreptunghi pentru atribuiri (prelucrări) și romb pentru decizii, cu două ieșiri (da/nu). Săgețile arată ordinea de execuție.
De reținut din convențiile de subiect: [x] înseamnă partea întreagă a lui x, iar x % y (sau „restul împărțirii”) este restul împărțirii întregi. De exemplu [7/2] = 3, iar 7 % 2 = 1. Aceste notații apar în aproape orice subiect de bacalaureat.
Tipuri de date, variabile și constante
Orice dată prelucrată de calculator are un tip, care stabilește ce valori poate lua și ce operații are voie să suporte:
- întreg (int) – numere fără parte zecimală: -3, 0, 2026; suportă împărțirea întreagă și restul;
- real (float, double) – numere cu zecimale: 3.14, -0.5; NU are sens operatorul de rest;
- caracter (char) – un singur simbol: o literă, o cifră ca semn, un spațiu; intern e memorat printr-un cod numeric (ASCII);
- șir de caractere (string) – o secvență de caractere: un nume, o propoziție;
- logic (bool) – doar două valori: adevărat sau fals; e tipul rezultat al oricărei condiții.
O variabilă este o zonă de memorie cu un nume, un tip și o valoare care se poate schimba în timpul execuției. O constantă are valoare fixată o singură dată (de exemplu numărul zilelor unei săptămâni).
Capcana cea mai frecventă: împărțirea între întregi este întreagă. În C++, expresia 7/2 dă 3, nu 3.5 — partea zecimală se pierde, nu se rotunjește. Ca să obții 3.5 trebuie ca măcar un operand să fie real. Tot aici: cifra „7” ca caracter și numărul 7 ca întreg sunt date diferite, cu tipuri diferite.
Operatori și expresii
O expresie combină valori, variabile și operatori și produce un rezultat de un anumit tip.
- Operatori aritmetici: + , - , * , / , % (restul). Pe întregi, / este împărțire întreagă (câtul), iar % dă restul: 17 / 5 = 3 și 17 % 5 = 2. Restul are voie să existe doar între întregi.
- Operatori relaționali: < , <= , > , >= , == (egal), != (diferit). Rezultatul lor este întotdeauna logic (adevărat/fals).
- Operatori logici: și (&&), sau (||), not (!). „Și” e adevărat doar când ambele condiții sunt adevărate; „sau” e fals doar când ambele sunt false.
Prioritatea operatorilor decide ordinea de calcul: întâi not, apoi operatorii aritmetici (*, /, % înaintea lui + și -), apoi relaționalii, apoi && și la final ||. Parantezele bat orice prioritate.
Două greșeli care costă puncte: confuzia dintre = (atribuire) și == (comparație) — în pseudocod atribuirea e săgeata ←, dar în C++ scrierea „dacă (x = 5)” atribuie în loc să compare; și negarea condițiilor compuse — negația lui „x > 0 și y > 0” este „x <= 0 sau y <= 0” (legile lui De Morgan schimbă „și” în „sau”), nu „x <= 0 și y <= 0”.
Structurile de bază: secvențială, alternativă, repetitivă
Teorema de structură (Böhm–Jacopini) spune că orice algoritm se poate construi doar din trei structuri:
- Structura secvențială – pașii se execută unul după altul, în ordinea scrisă. Ordinea contează: dacă schimbi două atribuiri între ele, rezultatul se poate schimba complet.
- Structura alternativă – dacă condiția e adevărată se execută o ramură, altfel cealaltă. Ramura altfel poate lipsi. Deciziile se pot imbrica: un dacă în interiorul altui dacă.
- Structura repetitivă – un grup de instrucțiuni se execută de mai multe ori. Există trei forme, iar diferența dintre ele e subiect sigur de examen:
- cât timp condiție execută – testul e la început; dacă de prima dată condiția e falsă, corpul se execută de zero ori;
- repetă ... până când condiție – testul e la sfârșit; corpul se execută cel puțin o dată, iar repetarea se oprește când condiția devine adevărată;
- pentru i ← a, b – repetare cu contor, folosită când numărul de pași e cunoscut dinainte.
Atenție la sensul condițiilor: la cât timp se continuă cât condiția e adevărată, la până când se continuă cât condiția e falsă. Transformarea dintr-o formă în alta — cerință frecventă la BAC — presupune negarea condiției, nu copierea ei.
Urmărirea execuției pas cu pas — instrumentul de bază la examen
Cea mai sigură metodă de a răspunde la întrebarea „ce afișează algoritmul?” este urmărirea execuției (trasarea): construiești un tabel cu câte o coloană pentru fiecare variabilă și execuți pașii pe hârtie, rând cu rând, exact ca un calculator — fără să „ghicești” ce ar vrea algoritmul să facă.
Regulile de aur ale trasării:
- scrii valorile după fiecare atribuire, nu doar la final;
- la fiecare intrare în buclă verifici condiția cu valorile curente, nu cu cele inițiale;
- nu sări peste iterații „pentru că se vede tiparul” — exact în penultima iterație apar surprizele;
- verifici separat prima și ultima iterație: acolo se produc erorile „off by one” (o repetare în plus sau în minus).
Un algoritm-model care apare constant la examen: prelucrarea cifrelor unui număr. Cât timp n este diferit de 0, ultima cifră se extrage cu n % 10, apoi numărul se scurtează cu n ← [n/10]. Cu acest tipar se calculează suma cifrelor, numărul de cifre, oglinditul (inversul) unui număr. Merită exersat până devine reflex, pentru că e cărămida multor subiecte de la bacalaureat.
De reținut
- algoritm
- succesiune finită de pași precis descriși care, pornind de la datele de intrare, produce în timp finit datele de ieșire
- finitudine
- proprietatea unui algoritm de a se încheia după un număr finit de pași pentru orice date de intrare valide
- generalitate
- proprietatea unui algoritm de a rezolva o întreagă clasă de probleme, nu un singur caz particular
- pseudocod
- limbaj de descriere a algoritmilor, intermediar între limbajul natural și limbajul de programare, cu cuvinte-cheie precum citește, scrie, dacă, cât timp
- atribuire
- operația prin care valoarea unei expresii se memorează într-o variabilă, notată în pseudocod cu săgeata ←; vechea valoare a variabilei se pierde
- variabilă
- zonă de memorie identificată printr-un nume, care are un tip și o valoare ce se poate modifica în timpul execuției programului
- tip de date logic
- tip de date cu exact două valori, adevărat și fals; este tipul rezultatului oricărei condiții
- împărțire întreagă
- operația care păstrează doar câtul împărțirii a două numere întregi, fără partea zecimală: 7/2 = 3; în pseudocod partea întreagă se notează [x]
- operatorul rest (%)
- operatorul care dă restul împărțirii a două numere întregi: 17 % 5 = 2; se folosește la extragerea ultimei cifre a unui număr prin n % 10
- structură repetitivă cu test final
- structura repetă ... până când, în care corpul se execută cel puțin o dată, iar repetarea se oprește când condiția devine adevărată
Greșeli frecvente
Greșit: 7/2 ar fi 3.5 atunci când ambii operanzi sunt întregi
Corect: Între întregi împărțirea este întreagă: 7/2 = 3 (se pierde partea zecimală, nu se rotunjește); rezultatul 3.5 se obține doar dacă măcar un operand este real
Greșit: La transformarea unei structuri cât timp în repetă ... până când se copiază condiția neschimbată
Corect: Cele două structuri continuă în situații opuse: cât timp repetă pe condiție adevărată, până când se oprește pe condiție adevărată — deci condiția trebuie negată
Greșit: Negarea condiției x > 0 și y > 0 ar fi x <= 0 și y <= 0
Corect: După legile lui De Morgan, negarea schimbă și în sau: negația este x <= 0 sau y <= 0; păstrarea lui și duce la răspunsuri greșite la itemii cu expresii logice
Greșit: O structură cât timp cu condiția falsă de la început și-ar executa corpul o dată
Corect: Cât timp are testul la început: dacă prima evaluare a condiției dă fals, corpul se execută de zero ori; cea care garantează minimum o execuție este repetă ... până când
Greșit: Se verifică algoritmul doar pe exemplul din enunț și se declară corect
Corect: Corectitudinea se judecă pe toate datele valide, inclusiv cazuri-limită: n = 0, numere negative, o singură cifră; multe subiecte cer exact o valoare pentru care algoritmul se comportă diferit
Test — 6 întrebări ca la examen
1. Care dintre următoarele este o proprietate obligatorie a unui algoritm?
- să fie scris într-un limbaj de programare
- să se termine după un număr finit de pași
- să folosească cel puțin o structură repetitivă
- să afișeze cel puțin două rezultate
Vezi răspunsul
să se termine după un număr finit de pași. Finitudinea este una dintre proprietățile fundamentale: algoritmul trebuie să se încheie în timp finit. Prima variantă e tentantă, dar algoritmul poate fi descris și în pseudocod sau schemă logică — limbajul de programare e doar una dintre formele de reprezentare.
2. Ce valoare are expresia 23 % 4 + 23 / 4, dacă toate valorile sunt întregi?
- 8
- 9.75
- 3
- 10
Vezi răspunsul
8. 23 % 4 = 3 (restul) și 23 / 4 = 5 (câtul împărțirii întregi), deci 3 + 5 = 8. Varianta 9.75 e capcana clasică: presupune că 23/4 dă 5.75, dar între întregi partea zecimală se pierde.
3. În pseudocod, [x] reprezintă partea întreagă a lui x. Ce valoare are [15/2] - [13/2]?
- 0.5
- 0
- 1
- 2
Vezi răspunsul
1. [15/2] = [7.5] = 7, iar [13/2] = [6.5] = 6, deci diferența este 7 - 6 = 1. Varianta 0 e capcana: presupune că partea întreagă se rotunjește (7.5 → 8 sau ambele la 7), dar [x] doar taie zecimalele, nu rotunjește.
4. O secvență repetă ... până când n = 0 primește de la început n = 0. De câte ori se execută corpul ei?
- de zero ori
- exact o dată
- la nesfârșit
- de două ori
Vezi răspunsul
exact o dată. Structura cu test final își execută corpul înainte de prima verificare a condiției, deci minimum o dată; abia apoi găsește n = 0 (condiție adevărată) și se oprește. Varianta de zero ori descrie comportamentul lui cât timp, care testează înainte.
5. Care expresie este echivalentă cu negarea condiției (a < 5 sau b < 5)?
- a >= 5 sau b >= 5
- a > 5 și b > 5
- a >= 5 și b >= 5
- a < 5 și b < 5
Vezi răspunsul
a >= 5 și b >= 5. De Morgan: negarea unui sau devine și, iar fiecare condiție se neagă individual; negația lui a < 5 este a >= 5 (nu a > 5, pentru că egalitatea trece de partea cealaltă). Varianta cu a > 5 pierde exact cazul a = 5 — capcana standard.
6. Se citește n = 2708 și se execută: cât timp n ≠ 0 execută s ← s + n % 10; n ← [n/10] (inițial s = 0). Ce valoare are s la final?
- 17
- 8
- 2708
- 4
Vezi răspunsul
17. Algoritmul adună cifrele: 8 + 0 + 7 + 2 = 17. Varianta 4 numără cifrele (ar corespunde lui s ← s + 1), iar 8 e doar prima cifră extrasă — greșeala celor care nu trasează execuția până la capăt, când n devine 0.
Deschide varianta interactivă — cu AI care îți explică