Pagina Modulului 5
Ora 32 - Modulul 5

Proiect: Calculator geometric

Meniu interactiv cu opțiuni pentru distanță, arie, perimetru. Introducere în eficiență.

45min teorie + 15min probleme PbInfo + exerciții practice
Competențele 4.1, 5.1
Săptămâna 3
Sigla liceului
TEORIE - 45 minute

Ce vei învăța astăzi?

Vom crea un program complex cu meniu care combină toate formulele geometrice învățate. Vom discuta și despre eficiență: câte operații face programul nostru?

Meniu cu opțiuni (distanță, arie, perimetru)
Funcții C++ pentru reutilizare cod
Formula lui Heron pentru arie
Coliniaritate și clasificare triunghi
Introducere în eficiență: câte operații facem?

Dicționar: Matematică ↔ C++ (Această lecție)

Simbol matematicCum se citeșteÎn C++Observație
√x„radical din x"sqrt(x)<cmath>
|x|„modul / val. absolută"fabs(x)pt double, nu abs()
a ≈ b„a aproximativ egal b"fabs(a-b) < epseps = 0.001
S = |det|/2„aria cu determinant"fabs(x1*(y2-y3)+...)/2coliniaritate
s = P/2„semiperimetru"s = (a+b+c)/2.02.0, nu 2!
G„centru de greutate"(x1+x2+x3)/3.03.0, nu 3!
R = abc/(4S)„raza cercului circumscris"a*b*c/(4.0*aria)4.0, nu 4!

Proiectarea meniului

Structura meniului:

  1. Calculează distanța dintre 2 puncte
  2. Calculează perimetrul unui triunghi
  3. Calculează aria unui triunghi (Heron)
  4. Verifică dacă 3 puncte sunt coliniare
  5. Clasifică triunghiul
  6. Ieșire

Analogie: Meniul ca un aparat de cafea

Un meniu cu do-while e ca un aparat de cafea: afișează opțiunile, iei alegerea, prepară băutura, apoi întreabă din nou. Se oprește doar când apeși „Anulare". Structura do-while garantează că meniul se afișează cel puțin o dată.

Sfat practic: Folosind do-while cu switch, poți crea programe cu meniu care rulează până când utilizatorul alege să iasă.

Funcții C++ — reutilizarea codului

Funcție: Un bloc de cod cu un nume, care primește parametri și returnează un rezultat. Se definește O SINGURĂ DATĂ, se apelează de câte ori ai nevoie.

Funcția dist() — distanța între 2 puncte
double dist(double x1, double y1, double x2, double y2) {
    return sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}
// Apeluri:
double d = dist(0, 0, 3, 4);   // d = 5.0
double e = dist(1, 2, 4, 6);   // e = 5.0
Urmărire pas cu pas: dist(0, 0, 3, 4)
PasOperație manualăVariabilă C++Valoare
1x₂ − x₁ = 3 − 0x2-x13
2y₂ − y₁ = 4 − 0y2-y14
33 + 4(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)25
4√25sqrt(25)5.0

Formula lui Heron pentru arie

Formula lui Heron: Calculează aria triunghiului cunoscând cele 3 laturi, fără a cunoaște înălțimea.

s = (a + b + c) / 2   (semiperimetrul)

S = √(s · (s-a) · (s-b) · (s-c))

s = (a + b + c) / 2   →   S = √(s·(s−a)·(s−b)·(s−c))
double s = (a+b+c)/2.0; double aria = sqrt(s*(s-a)*(s-b)*(s-c));
„s egal suma laturilor supra 2 (cu .0!); aria egal radical din s ori factorii"
Exemplu: Triunghi cu laturile 3, 4, 5

s = (3+4+5)/2 = 6

S = √(6 · (6-3) · (6-4) · (6-5)) = √(6 · 3 · 2 · 1) = √36 = 6

Verificare: triunghi dreptunghic cu catete 3,4 → S = 3×4/2 = 6 ✓

Implementare Heron în C++
double a = dist(x2,y2,x3,y3);  // latura opusă lui A
double b = dist(x1,y1,x3,y3);  // latura opusă lui B
double c = dist(x1,y1,x2,y2);  // latura opusă lui C
double s = (a + b + c) / 2;
double aria = sqrt(s * (s-a) * (s-b) * (s-c));

Atenție: Dacă s-a, s-b sau s-c e negativ, ai introdus un triunghi invalid (inegalitatea triunghiului nu e satisfăcută). Verifică: a+b > c, a+c > b, b+c > a.

Coliniaritate — 3 puncte pe aceeași dreaptă

Coliniaritate: Trei puncte sunt coliniare dacă aria triunghiului format de ele = 0.

S = |x₁(y₂-y₃) + x₂(y₃-y₁) + x₃(y₁-y₂)| / 2

Dacă S ≈ 0 (S < 0.001) → punctele sunt coliniare

S = |x₁(y₂−y₃) + x₂(y₃−y₁) + x₃(y₁−y₂)| / 2
double det = fabs(x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2)); if(det < 0.001) ...
„S egal modul din suma produselor cu diferențele, supra 2. Dacă ≈ 0 → coliniare"
Exemplu: (1,1), (2,2), (3,3) — pe diagonală

S = |1·(2-3) + 2·(3-1) + 3·(1-2)| / 2 = |(-1) + 4 + (-3)| / 2 = 0 / 2 = 0 → Coliniare ✓

Clasificarea triunghiului

TipCondițieCod C++
Echilaterala = b = cfabs(a-b)<eps && fabs(b-c)<eps
Isoscel2 laturi egalefabs(a-b)<eps || fabs(b-c)<eps || fabs(a-c)<eps
ScalenToate diferitealtfel (else)
Dreptunghica = b + cfabs(a2-b2-c2)<eps (testezi toate 3 combinații)

De ce folosim fabs(a-b) < eps? Deoarece calculele cu double introduc erori de rotunjire. Nu comparăm niciodată cu == la numere reale. Folosim o toleranță mică: eps = 0.001.

Implementarea completă

Calculator geometric - Program complet
#include <iostream>
#include <cmath>
using namespace std;

double dist(double x1, double y1, double x2, double y2) {
    return sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}

int main() {
    int optiune;
    double x1, y1, x2, y2, x3, y3;
    
    do {
        cout << "\n=== CALCULATOR GEOMETRIC ===" << endl;
        cout << "1. Distanta dintre 2 puncte" << endl;
        cout << "2. Perimetrul triunghiului" << endl;
        cout << "3. Aria triunghiului (Heron)" << endl;
        cout << "4. Verifica coliniaritate" << endl;
        cout << "5. Clasificare triunghi" << endl;
        cout << "0. Iesire" << endl;
        cout << "Alege optiunea: ";
        cin >> optiune;
        
        switch(optiune) {
            case 1: {
                cout << "A(x1,y1): "; cin >> x1 >> y1;
                cout << "B(x2,y2): "; cin >> x2 >> y2;
                cout << "Distanta = " << dist(x1,y1,x2,y2) << endl;
                break;
            }
            case 2: {
                cout << "A(x1,y1): "; cin >> x1 >> y1;
                cout << "B(x2,y2): "; cin >> x2 >> y2;
                cout << "C(x3,y3): "; cin >> x3 >> y3;
                double a = dist(x2,y2,x3,y3);
                double b = dist(x1,y1,x3,y3);
                double c = dist(x1,y1,x2,y2);
                cout << "Perimetrul = " << a+b+c << endl;
                break;
            }
            case 3: {
                cout << "A(x1,y1): "; cin >> x1 >> y1;
                cout << "B(x2,y2): "; cin >> x2 >> y2;
                cout << "C(x3,y3): "; cin >> x3 >> y3;
                double a = dist(x2,y2,x3,y3);
                double b = dist(x1,y1,x3,y3);
                double c = dist(x1,y1,x2,y2);
                double s = (a+b+c) / 2;
                double aria = sqrt(s*(s-a)*(s-b)*(s-c));
                cout << "Aria = " << aria << endl;
                break;
            }
            case 4: {
                cout << "A(x1,y1): "; cin >> x1 >> y1;
                cout << "B(x2,y2): "; cin >> x2 >> y2;
                cout << "C(x3,y3): "; cin >> x3 >> y3;
                double det = fabs(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2));
                if (det < 0.001) cout << "Punctele SUNT coliniare" << endl;
                else cout << "Punctele NU sunt coliniare" << endl;
                break;
            }
            case 5: {
                cout << "A(x1,y1): "; cin >> x1 >> y1;
                cout << "B(x2,y2): "; cin >> x2 >> y2;
                cout << "C(x3,y3): "; cin >> x3 >> y3;
                double a = dist(x2,y2,x3,y3);
                double b = dist(x1,y1,x3,y3);
                double c = dist(x1,y1,x2,y2);
                double eps = 0.001;
                if(fabs(a-b)<eps && fabs(b-c)<eps)
                    cout << "Echilateral" << endl;
                else if(fabs(a-b)<eps || fabs(b-c)<eps || fabs(a-c)<eps)
                    cout << "Isoscel" << endl;
                else cout << "Scalen" << endl;
                break;
            }
            case 0:
                cout << "La revedere!" << endl;
                break;
            default:
                cout << "Optiune invalida!" << endl;
        }
    } while (optiune != 0);
    
    return 0;
}

Algoritmul complet (Pseudocod)

// Calculator geometric — meniu cu do-while + switch FUNCTION dist(x1,y1,x2,y2): RETURN sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)) DO PRINT meniu (1..5, 0=exit) READ optiune SWITCH optiune: CASE 1: // distanța READ x1,y1,x2,y2 PRINT dist(x1,y1,x2,y2) CASE 2: // perimetru SET a,b,c laturile cu dist PRINT a+b+c CASE 3: // aria Heron SET s (a+b+c)/2.0 SET aria sqrt(s*(s-a)*(s-b)*(s-c)) CASE 4: // coliniaritate SET det fabs(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2)) IF det < 0.001 THEN PRINT "Coliniare" CASE 5: // clasificare (echilateral/isoscel/scalen) IF fabs(a-b)<eps AND fabs(b-c)<eps THEN "Echilateral" ELSE IF 2 egale THEN "Isoscel" ELSE "Scalen" WHILE optiune ≠ 0

Introducere în eficiență

Câte operații face programul nostru?

  • Funcția dist() face: 2 scăderi, 2 înmulțiri, 1 adunare, 1 radical = 6 operații
  • Perimetrul: 3 apeluri la dist + 2 adunări = 20 operații
  • Aria (Heron): 3 apeluri la dist + semiperimetru + 4 scăderi + 4 înmulțiri + 1 radical = ~30 operații
Întrebare de reflecție

La opțiunile 3, 4 și 5, calculăm de fiecare dată laturile de la zero. Putem optimiza?

Răspuns: Da! Calculăm laturile o singură dată și le reutilizăm. Aceasta este optimizare prin evitarea calculelor redundante.

VersiuneOperații per cicluMotivare
Inițială~30 per opțiuneRecalculează laturile de fiecare dată
Optimizată~18 + reutilizareCalculează laturile o dată, reutilizează
Economie~40%Mai puține apeluri sqrt redundante

Greșeli frecvente

Lipsa break în switch: Fără break, execuția „cade" în case-ul următor (fall-through). Uneori e intenționat, dar de regulă e o eroare.

Variabile declarate în case: Trebuie puse între acolade { }. Altfel, compilatorul dă eroare „jump to case label".

Compararea cu == la double: if(a == b) e riscant. Folosește fabs(a-b) < eps cu eps = 0.001.

do-while vs while: do-while execută întâi blocul, apoi verifică condiția. while verifică ÎNAINTE. Pentru meniuri, do-while e potrivit: vrem ca meniul să se afișeze cel puțin o dată.

Verificare cunoștințe

1. Ce structură repetitivă este potrivită pentru un meniu?

2. Ce face break în switch?

3. Aria triunghiului cu laturile 5, 12, 13 (Heron) este:

4. Cum verifici dacă 3 puncte sunt coliniare?

5. De ce scriem fabs(a-b) < eps în loc de a == b pentru double?

6. dist(0,0,3,4) returnează:

Întrebări frecvente (FAQ)

Ce e diferența dintre funcție și cod direct?

O funcție e un bloc reutilizabil. Scrii formula O DATĂ în funcție, apoi o apelezi de câte ori ai nevoie: dist(0,0,3,4), dist(1,2,5,6). Fără funcție, ai copia codul de 3 ori.

Pot folosi pow(x,2) în loc de x*x?

Da, dar x*x e mai rapid decât pow(x,2). pow e util la puteri fracționare/mari. Pentru pătrat, înmulțirea directă e preferată.

Ce se întâmplă dacă sqrt() primește un număr negativ?

Returnează NaN (Not a Number). De aceea verifici mereu ca argumentul să fie ≥ 0. La Heron, un s-a negativ înseamnă triunghi invalid.

Cum afișez rezultatele cu exact 2 zecimale?

Doar când enunțul cere două zecimale. Adaugă #include <iomanip>, apoi afișează rezultatul așa: cout << fixed << setprecision(2) << valoare;. Pe scurt: fixed înseamnă „afișează zecimale normale”, iar setprecision(2) înseamnă „arată 2 cifre după virgulă”.

PROBLEME PBINFO - 15 minute

PbInfo - Distanta dintre doua puncte

Formula distantei

Enunț:

Cerință: Se citesc coordonatele a doua puncte. Afiseaza distanta dintre ele cu doua zecimale.

Date de intrare:

x1 y1 x2 y2.

Date de ieșire:

Distanta dintre puncte.

Exemplu:

Intrare:

0 0 3 4

Ieșire:

5.00

Explicație: Distanta este sqrt(3^2+4^2)=5.

Cum gândim soluția:
  1. Calculam dx=x2-x1 si dy=y2-y1.
  2. Aplicam sqrt(dx*dx+dy*dy).
  3. Afisam cu doua zecimale.

Tip: double pastreaza rezultatul cu virgula. Daca enuntul cere exact doua zecimale, folosim fixed si setprecision(2) doar la afisarea acelui rezultat.

Scrie soluția în C++:
Soluție corectă:
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

int main() {
    double x1, y1, x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;
    double d = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    // Enuntul cere doua zecimale: 5 devine 5.00, 3.14159 devine 3.14.
    cout << fixed << setprecision(2) << d;
}

PbInfo - Aria Heron

Triunghi din laturi

Enunț:

Cerință: Se citesc trei laturi. Daca formeaza triunghi, afiseaza aria prin formula lui Heron.

Date de intrare:

a b c.

Date de ieșire:

Aria sau mesajul Imposibil.

Exemplu:

Intrare:

3 4 5

Ieșire:

6.00

Explicație: Semiperimetrul este 6, aria este 6.

Cum gândim soluția:
  1. Verificam inegalitatile triunghiului.
  2. Calculam s=(a+b+c)/2.
  3. Aplicam sqrt(s*(s-a)*(s-b)*(s-c)).

Tip: Nu aplica Heron inainte sa verifici triunghiul.

Scrie soluția în C++:
Soluție corectă:
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

int main() {
    double a, b, c;
    cin >> a >> b >> c;
    if (a + b <= c||a + c <= b||b + c <= a) {
        cout << "Imposibil";
        return 0;
    }
    double s = (a + b + c) / 2;
    // Afisam cu doua zecimale deoarece exemplul cere 6.00, nu doar 6.
    cout << fixed << setprecision(2) << sqrt(s * (s - a) * (s - b) * (s - c));
}

PbInfo - Coliniaritate

Arie zero

Enunț:

Cerință: Se citesc trei puncte. Afiseaza DA daca sunt coliniare, altfel NU.

Date de intrare:

Coordonatele celor trei puncte.

Date de ieșire:

DA sau NU.

Exemplu:

Intrare:

0 0 1 1 2 2

Ieșire:

DA

Explicație: Punctele sunt pe aceeasi dreapta.

Cum gândim soluția:
  1. Calculam determinantul.
  2. Daca determinantul este 0, aria este 0.
  3. Afisam DA/NU.

Tip: Pentru coordonate intregi poti compara determinantul exact cu 0.

Scrie soluția în C++:
Soluție corectă:
#include <iostream>
using namespace std;

int main() {
    long long x1, y1, x2, y2, x3, y3;
    cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;
    long long det = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2);
    cout << (det == 0?"DA":"NU");
}
EXERCIȚII PRACTICE - 2 sarcini / 20 minute

Exercițiul Practic: Calculator geometric

Extinde calculatorul geometric si optimizeaza formulele reutilizate in mai multe optiuni. Important: aici exersam ideea, nu copierea unei singure variante de cod.

Cum lucram la calculator: poti porni de la programul complet din lectie sau poti scrie o varianta mai mica, doar cu optiunile cerute. Este corect si daca citesti coordonatele in fiecare case, si daca le citesti o singura data inainte de meniu; diferenta este de organizare si eficienta. Important este sa explici in cod ce varianta ai ales.

Sarcina 1: Adaugă opțiunea de triunghi dreptunghic

Adauga o optiune pentru verificarea triunghiului dreptunghic. Poti folosi numarul 6 in meniul existent sau poti face un program separat care citeste trei puncte si afiseaza rezultatul.

Pași de rezolvare:
  1. Daca lucrezi in meniul existent, adauga case 6: in switch, cu { } pentru variabile locale
  2. Daca faci program separat, citeste direct punctele A, B, C si sari peste meniu
  3. Calculeaza distantele la patrat (fara sqrt): a2 = (x3-x2)*(x3-x2)+(y3-y2)*(y3-y2)
  4. Testeaza Pitagora pe toate 3 combinatii: fabs(a2-b2-c2)<eps
  5. In varianta cu switch, nu uita break; la final
Pitagora: a = b + c (unghiul drept e opus laturii celei mai mari)
fabs(a2 - b2 - c2) < eps
„Testăm toate 3 combinații — nu știm care latură e ipotenuza. Lucrăm cu pătrate (fără sqrt) pentru eficiență."
// Verificare triunghi dreptunghic (Pitagora) CITEȘTE coordonatele celor 3 puncte // Calculăm pătratele laturilor — fără sqrt! a2 (x3x2) + (y3y2) // BC b2 (x3x1) + (y3y1) // AC c2 (x2x1) + (y2y1) // AB DACĂ |a2b2c2| < eps SAU |b2a2c2| < eps SAU |c2a2b2| < eps ATUNCI SCRIE "Dreptunghic" ALTFEL SCRIE "Nu este dreptunghic"

De ce funcționează? Teorema lui Pitagora spune că într-un triunghi dreptunghic, ipotenuza = cateta₁ + cateta₂. Nu știm care latură e ipotenuza, așa că testăm toate 3 combinații. Folosim fabs() (valoare absolută) deoarece numerele double au erori de rotunjire — nu comparăm niciodată cu ==!

case 6: {
    // Citim coordonatele celor 3 varfuri
    cout << "A,B,C: "; cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;
    // Calculam patratele laturilor (fara sqrt — e mai eficient!)
    double a2 = (x3-x2)*(x3-x2) + (y3-y2)*(y3-y2);  // BC^2
    double b2 = (x3-x1)*(x3-x1) + (y3-y1)*(y3-y1);  // AC^2
    double c2 = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);  // AB^2
    double eps = 0.001;  // toleranta pentru erori de rotunjire
    // Testam Pitagora pe toate 3 combinatii (nu stim care e ipotenuza)
    if(fabs(a2-b2-c2)<eps || fabs(b2-a2-c2)<eps 
       || fabs(c2-a2-b2)<eps)
        cout << "Dreptunghic" << endl;
    else
        cout << "Nu este dreptunghic" << endl;
    break;
}
Sarcina 2: Optimizare - evitarea calculelor repetate

Rescrie sau reorganizeaza programul astfel incat valorile folosite de mai multe ori sa nu fie recalculate inutil. Poti optimiza tot calculatorul sau doar partea de triunghi.

Pași de rezolvare:
  1. Alege ce optimizezi: toate optiunile cu triunghi sau doar cele care folosesc aceleasi laturi
  2. Citeste coordonatele o singura data pentru acel triunghi
  3. Calculeaza laturile a, b, c imediat dupa citire
  4. Calculeaza doar ce iti trebuie: P pentru perimetru, s si aria pentru Heron, a2, b2, c2 pentru Pitagora
  5. In fiecare case, foloseste variabilele deja calculate
// Optimizare: precalculăm totul O SINGURĂ DATĂ CITEȘTE x1,y1, x2,y2, x3,y3 // Laturile — se calculează o singură dată! a dist(x2,y2,x3,y3) b dist(x1,y1,x3,y3) c dist(x1,y1,x2,y2) P a + b + c s P / 2 aria sqrt(s·(sa)·(sb)·(sc)) // Acum switch-ul folosește direct a, b, c, P, aria SWITCH (optiune) ... // fără recalculare!

De ce funcționează? În versiunea inițială, fiecare case recalcula laturile a, b, c de la zero — adică 3 × sqrt() de fiecare dată. Mutând calculele înainte de switch, facem 3 sqrt() o singură dată, indiferent câte operații alege utilizatorul. E un exemplu clasic de precalculare (memoizare simplificată).

// === OPTIMIZARE: citim si calculam O SINGURA DATA ===
cout << "A: "; cin >> x1 >> y1;  // coordonatele varfului A
cout << "B: "; cin >> x2 >> y2;  // coordonatele varfului B
cout << "C: "; cin >> x3 >> y3;  // coordonatele varfului C

// Laturile triunghiului — calculate o singura data
double a = dist(x2,y2,x3,y3);  // latura BC
double b = dist(x1,y1,x3,y3);  // latura AC
double c = dist(x1,y1,x2,y2);  // latura AB
// Valori derivate — reutilizabile in orice case
double P = a + b + c;                    // perimetru
double s = P / 2;                        // semiperimetru
double aria = sqrt(s*(s-a)*(s-b)*(s-c)); // Heron
// Apoi in switch reutilizam a, b, c, P, aria — FARA recalculare!

Descarcă toate exercițiile și soluțiile