Pagina Modulului 5
Ora 38 - Modulul 5

Proiect: Calculator molecular

Extindere cu mai multe elemente, funcții separate, testare glucoză C₆H₁₂O₆, lucru în perechi

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

Obiectivele proiectului

Vei construi un calculator molecular complet care parsează automat formule chimice și calculează masa moleculară.

Parsare automată a formulei chimice
Tabel cu 15+ elemente chimice
Testare: H₂O, CO₂, C₆H₁₂O₆
Compoziție procentuală
Lucru în perechi

Dicționar: Chimie proiect ↔ C++

Concept chimicCum se citeșteÎn C++Observație
Parsare formulă„citire caracter cu caracter"formula[i]acces indexat la string
Vectori paraleli„simboluri[] și mase[]"string s[N]; int m[N];aceeași poziție = același element
Simbol chimic (1-2 litere)„ex: Na = sodiu"string simbol="";prima MARE + eventual mică
M = Σ(cantitate × masă)„suma produselor"total += nr * masaElem;acumulare în while
Compoziție procentuală„procent fiecare element"(double)(nr*m)/total*100cast la double!
Cifră multipla (ex: H₁₂)„numărul 12, nu 1 și 2"nr = nr*10 + (c-'0');construire Horner
'A'...'Z' / 'a'...'z'„majusculă / minusculă"c >= 'A' && c <= 'Z'comparatii ASCII
Element necunoscut„nu e în baza de date"return -1;valoare santinelă

Analogie: Cum citim o formulă chimică?

Rețeta de bucătărie

O formulă chimică e ca o rețetă: H₂O = „2 părți Hidrogen + 1 parte Oxigen". La fel cum calculăm costul rețetei (2×preț_ou + 1×preț_făină), calculăm masa moleculară: 2×masă_H + 1×masă_O = 2×1 + 16 = 18 u.

Masa moleculară (M): suma maselor atomice × cantitate pentru fiecare element din formulă
Exemplu: H₂SO₄ → 2×1 + 32 + 4×16 = 2 + 32 + 64 = 98 u
M = Σ (cantitatei × masă_atomicăi)
total += nr * masaElem;
Se citește: „total egal total plus nr ori masa elementului"

Tabelul elementelor în program

SimbolElementMasă atomică (u)Prezent în...
HHidrogen1H₂O, H₂SO₄, NH₃
CCarbon12CO₂, C₆H₁₂O₆, CH₄
NAzot14NH₃, HNO₃
OOxigen16H₂O, CO₂, CaCO₃
NaSodiu23NaCl, NaOH
MgMagneziu24MgO, MgCl₂
AlAluminiu27Al₂O₃
PFosfor31H₃PO₄
SSulf32H₂SO₄, SO₂
ClClor35NaCl, HCl
KPotasiu39KOH, KCl
CaCalciu40CaCO₃, CaO
FeFier56Fe₂O₃, FeSO₄
CuCupru64CuSO₄, CuO
ZnZinc65ZnO, ZnCl₂

Simboluri cu 2 litere: Na, Mg, Al, Cl, Ca, Fe, Cu, Zn — prima literă e MARE, a doua e mică. Parsarea trebuie să distingă: „Ca" = Calciu, nu „C" + „a"!

Plan de lucru (3 faze)

Faza 1: Baza de date cu elemente (20 min)

Creează vectori paraleli pentru simboluri și mase atomice. Scrie funcția gasesteMasa(simbol) care returnează masa sau -1 dacă elementul e necunoscut.

Faza 2: Parsarea formulei (40 min)

Parcurge string-ul caracter cu caracter: literă mare = element nou, literă mică = continuare simbol, cifre = cantitate, fără cifră → cantitate 1.

Faza 3: Testare și meniu (30 min)

Testează cu formule cunoscute (H₂O=18, CO₂=44, glucoză=180), adaugă meniu interactiv, tratează erori.

Pseudocod: Algoritmul de parsare

FUNCTION calculeazaMasa(formula) total 0 i 0 // index curent în string WHILE i < lungime(formula) // Pas 1: citim simbolul (literă mare + eventuale litere mici) simbol "" IF formula[i] e LITERĂ MARE simbol simbol + formula[i] i i + 1 WHILE formula[i] e literă mică simbol simbol + formula[i] i i + 1 END WHILE END IF // Pas 2: citim cantitatea (cifre consecutive) nr 0 WHILE formula[i] e CIFRĂ nr nr × 10 + (formula[i] - '0') // Horner i i + 1 END WHILE IF nr = 0 THEN nr 1 // fără cifră = cantitate 1 // Pas 3: căutăm masa și adăugăm la total masaElem gasesteMasa(simbol) total total + nr × masaElem END WHILE RETURN total END FUNCTION

Codul complet al proiectului

Calculator molecular complet cu parsare string
#include <iostream>
#include <string>
using namespace std;

// Baza de date cu elemente
const int NR_ELEM = 15;
string simboluri[NR_ELEM] = {"H","C","N","O","S","Na","Cl","Ca","Fe","P",
                             "Mg","K","Zn","Cu","Al"};
int mase[NR_ELEM] = {1, 12, 14, 16, 32, 23, 35, 40, 56, 31,
                     24, 39, 65, 64, 27};

// Functie: gaseste masa atomica dupa simbol
int gasesteMasa(string simbol) {
    for (int i = 0; i < NR_ELEM; i++)
        if (simboluri[i] == simbol)
            return mase[i];
    return -1; // element necunoscut
}

// Functie: calculeaza masa moleculara
int calculeazaMasa(string formula) {
    int total = 0;
    int i = 0, n = formula.length();
    
    while (i < n) {
        // Pas 1: citeste simbolul (litera mare + eventuale litere mici)
        string simbol = "";
        if (formula[i] >= 'A' && formula[i] <= 'Z') {
            simbol += formula[i]; i++;
            while (i < n && formula[i] >= 'a' && formula[i] <= 'z') {
                simbol += formula[i]; i++;
            }
        }
        
        // Pas 2: citeste numarul (daca exista)
        int nr = 0;
        while (i < n && formula[i] >= '0' && formula[i] <= '9') {
            nr = nr * 10 + (formula[i] - '0'); i++;
        }
        if (nr == 0) nr = 1;
        
        // Pas 3: cautam elementul si adaugam la total
        int masaElem = gasesteMasa(simbol);
        if (masaElem == -1) {
            cout << "Element necunoscut: " << simbol << endl;
            return -1;
        }
        total += nr * masaElem;
    }
    return total;
}

int main() {
    string formula;
    int optiune;
    
    do {
        cout << "\n=== CALCULATOR MOLECULAR ===" << endl;
        cout << "1. Calculeaza masa moleculara" << endl;
        cout << "2. Testeaza formule cunoscute" << endl;
        cout << "3. Afiseaza elementele disponibile" << endl;
        cout << "4. Compozitie procentuala" << endl;
        cout << "0. Iesire" << endl;
        cout << "Alege: "; cin >> optiune;
        
        if (optiune == 1) {
            cout << "Formula chimica: "; cin >> formula;
            int masa = calculeazaMasa(formula);
            if (masa != -1)
                cout << formula << " = " << masa << " u" << endl;
        }
        else if (optiune == 2) {
            string teste[] = {"H2O","CO2","NH3","C6H12O6","H2SO4","NaCl","CaCO3","Fe2O3"};
            int asteptate[] = {18, 44, 17, 180, 98, 58, 100, 160};
            int nrTeste = 8;
            cout << "\n--- Teste automate ---" << endl;
            int corecte = 0;
            for (int i = 0; i < nrTeste; i++) {
                int rez = calculeazaMasa(teste[i]);
                bool ok = (rez == asteptate[i]);
                cout << teste[i] << ": " << rez 
                     << " (asteptat " << asteptate[i] << ") "
                     << (ok ? "OK" : "EROARE!") << endl;
                if (ok) corecte++;
            }
            cout << corecte << "/" << nrTeste << " teste corecte." << endl;
        }
        else if (optiune == 3) {
            cout << "\nElemente disponibile:" << endl;
            for (int i = 0; i < NR_ELEM; i++)
                cout << simboluri[i] << " = " << mase[i] << " u\t";
            cout << endl;
        }
        else if (optiune == 4) {
            cout << "Formula: "; cin >> formula;
            int total = calculeazaMasa(formula);
            if(total > 0)
                cout << "Masa totala: " << total << " u" << endl;
        }
    } while (optiune != 0);
    
    return 0;
}

Cum funcționează parsarea?

Trace pas cu pas: "C6H12O6" (Glucoză)
IterațieiSimbolNumărMasă elemSubtotalTotalC++ echivalent
10→2C6127272total += 6 * 12;
22→5H1211284total += 12 * 1;
35→7O61696180total += 6 * 16;
Trace: "NaCl" (simbol cu 2 litere)
IterațieiSimbolNumărMasăTotalC++ echivalent
10→2Na (N+a)1 (lipsește cifra)2323simbol="Na"; nr=1;
22→4Cl (C+l)13558simbol="Cl"; nr=1;

Observă: 'a' după 'N' e literă mică → face parte din simbol. 'l' după 'C' e literă mică → face parte din simbol.

Regula: Literă mare = element nou, literă mică = continuare simbol, cifre = cantitate
if (formula[i] >= 'A' && formula[i] <= 'Z') { simbol += formula[i]; i++; }
Se citește: „dacă caracterul e literă mare, începe un simbol nou"
Construire număr din cifre (Horner): nr = nr × 10 + cifra_curentă
nr = nr * 10 + (formula[i] - '0');
Se citește: „nr egal nr ori 10 plus cifra curentă" — exemplu: '1','2' → 0→1→12

Greșeală frecventă: Dacă nu verifici litera mică, "NaCl" se citește ca N-a-C-l = N(14) + eroare('a') + C(12) + eroare('l'). Trebuie: Na(23) + Cl(35) = 58.

Cifre mari: H₁₂ are cifra 12, nu 1 și apoi 2. Bucla nr = nr*10 + (formula[i]-'0') construiește numărul: nr=0→1→12.

Compoziție procentuală

Compoziția procentuală arată cât la sută din masa totală contribuie fiecare element. Formula: procent = (cantitate × masă_atom / masă_totală) × 100.

procent = (cantitate × masă_atom / masă_totală) × 100
double proc = (double)(nr * m) / total * 100;
Se citește: „procent egal cantitate ori masa atomică împărțit la total, ori 100" — atenție la cast (double)!
Exemplu: H₂O (M = 18 u)

H: 2×1/18 × 100 = 11.11% | O: 1×16/18 × 100 = 88.89% | Total = 100%

Trace parsare completă: "H2SO4"
PasiCaracterSimbolNrmasaElemSubtotaltotal
10'H' (mare)"H"0
21'2' (cifră)"H"2122
32'S' (mare)"S"2
43'O' (mare, nu cifră)"S"1 (default)323234
53'O' (mare)"O"34
64'4' (cifră)"O"4166498
Funcție: compoziție procentuală
void compozitie(string formula) {
    int total = calculeazaMasa(formula);
    if(total <= 0) return;
    
    // Re-parsam formula si afisam procentul fiecarui element
    int i = 0, n = formula.length();
    cout << "Compozitia lui " << formula << " (M=" << total << " u):" << endl;
    while(i < n) {
        string simbol = "";
        simbol += formula[i]; i++;
        while(i < n && formula[i] >= 'a' && formula[i] <= 'z') {
            simbol += formula[i]; i++;
        }
        int nr = 0;
        while(i < n && formula[i] >= '0' && formula[i] <= '9') {
            nr = nr*10 + (formula[i]-'0'); i++;
        }
        if(nr == 0) nr = 1;
        int m = gasesteMasa(simbol);
        double proc = (double)(nr * m) / total * 100;
        cout << simbol << ": " << nr << " x " << m 
             << " = " << nr*m << " u (" << proc << "%)" << endl;
    }
}

Verificare cunoștințe

1. Ce returnează calculeazaMasa("NaCl")?

2. Ce face `if (nr == 0) nr = 1;`?

3. Cât e masa moleculară a H₂SO₄?

4. Cum distingem "Ca" (calciu) de "C" + "a"?

5. Ce procent de oxigen are H₂O?

6. Ce returnează gasesteMasa("X")?

Extensie: proiectarea tabelului de elemente

Când lista de elemente crește, multe if-uri devin greu de întreținut. O soluție mai bună este un tabel de perechi simbol-masă, căutat cu o funcție.

struct Element {
    string simbol;
    double masa;
};

Element tabel[] = {{"H", 1}, {"C", 12}, {"O", 16}, {"Na", 23}, {"Cl", 35.5}};

Avantajul este că adăugarea unui element nou înseamnă o singură linie în tabel, nu modificări în toată logica programului.

Pași pentru o căutare robustă
  1. Citește simbolul ca string, nu ca char.
  2. Caută simbolul în tabel.
  3. Dacă nu există, returnează -1 și afișează mesaj de eroare.

Întrebări frecvente (FAQ)

De ce folosim vectori paraleli și nu o structură?

Vectorii paraleli sunt mai simpli la acest nivel. Într-un program mai avansat, am folosi struct Element { string simbol; int masa; }; — dar conceptul de struct nu a fost încă predat.

Ce facem cu elemente cu simbol de 2+ litere ca Fe?

Parsarea noastră le gestionează automat: 'F' e literă mare = start simbol, 'e' e literă mică = continuare. Deci simbol="Fe". La fel: Na, Mg, Al, Cl, Ca, Cu, Zn.

Masele atomice nu sunt numere întregi în realitate?

Corect! H = 1.008, C = 12.011, O = 15.999. Noi folosim mase rotunjite (int) pentru simplitate. Poți schimba int mase[] în double mase[] pentru precizie mai mare.

Cum adaug un element nou în baza de date?

1. Mărește NR_ELEM. 2. Adaugă simbolul în vectorul simboluri[]. 3. Adaugă masa în vectorul mase[] (la aceeași poziție). Testează cu o formulă ce conține noul element.

PROBLEME PBINFO - 15 minute

PbInfo - Masa pentru H2O

Formula simpla

Enunț:

Cerință: Calculeaza masa moleculara pentru formula H2O.

Date de intrare:

Formula H2O.

Date de ieșire:

Masa moleculara.

Exemplu:

Intrare:

H2O

Ieșire:

18

Explicație: 2*1+16=18.

Cum gândim soluția:
  1. Citim formula ca string.
  2. Pentru H adaugam 1 inmultit cu numarul de atomi.
  3. Pentru O adaugam 16.

Tip: Pentru aceasta problema poti trata doar H, C, O. Daca dupa element apare o cifra, ea spune cati atomi sunt; daca nu apare, presupunem 1.

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

int masaAtomica(char e) {
    if (e == 'H') return 1;
    if (e == 'C') return 12;
    if (e == 'O') return 16;
    return 0;
}

int main() {
    string f;
    cin >> f;
    int total = 0;
    for (int i = 0; i < f.size(); i++) {
        char e = f[i];
        int nr = 1;
        if (i + 1 < f.size() && isdigit(f[i + 1])) {
            nr = f[i + 1] - '0'; // '2' devine numarul 2
            i++;                 // sarim peste cifra deja folosita
        }
        total += masaAtomica(e) * nr;
    }
    cout << total;
}

PbInfo - Masa pentru CO2

Atom implicit

Enunț:

Cerință: Calculeaza masa moleculara pentru CO2.

Date de intrare:

Formula CO2.

Date de ieșire:

Masa moleculara.

Exemplu:

Intrare:

CO2

Ieșire:

44

Explicație: C=12 si O2=32.

Cum gândim soluția:
  1. C are numar implicit 1.
  2. O are numar 2.
  3. Adunam 12+32.

Tip: Numarul lipsa inseamna 1.

Scrie soluția în C++:
Soluție corectă:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int masa(char e) {
    if (e == 'H') return 1;
    if (e == 'C') return 12;
    if (e == 'O') return 16;
    return 0;
}
int main() {
    string f;
    cin >> f;
    int total = 0;
    for (int i = 0; i < f.size(); i++) {
        char e = f[i];
        int nr = 1;
        if (i + 1 < f.size() && isdigit(f[i + 1])) {
            nr = f[i + 1] - '0';
            i++;
        }
        total += masa(e) * nr;
    }
    cout << total;
}

PbInfo - Formula cu mai multi termeni

Parsare simpla

Enunț:

Cerință: Se citeste o formula formata din H, C, O si cifre de o singura cifra. Afiseaza masa.

Date de intrare:

Formula chimica.

Date de ieșire:

Masa moleculara.

Exemplu:

Intrare:

C6H6O

Ieșire:

94

Explicație: 6*12+6*1+16=94.

Cum gândim soluția:
  1. Parcurgem caracterele.
  2. Cand gasim element, citim cifra de dupa el daca exista.
  3. Adunam masa atomica * numar.

Tip: Aceasta varianta nu trateaza paranteze. isdigit(f[i+1]) verifica daca urmatorul caracter este cifra, iar f[i+1] - '0' transforma caracterul '6' in numarul 6.

Scrie soluția în C++:
Soluție corectă:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int masa(char e) {
    if (e == 'H') return 1;
    if (e == 'C') return 12;
    if (e == 'O') return 16;
    return 0;
}
int main() {
    string f;
    cin >> f;
    int total = 0;
    for (int i = 0; i < f.size(); i++) {
        char e = f[i];
        int nr = 1;
        if (i + 1 < f.size() && isdigit(f[i + 1])) {
            nr = f[i + 1] - '0';
            i++;
        }
        total += masa(e) * nr;
    }
    cout << total;
}
EXERCIȚII PRACTICE - 2 sarcini / 20 minute

Exercițiul Practic: Calculator molecular

Extinde calculatorul molecular cu afișare detaliată, sortare și verificarea formulelor chimice. Pentru formule cu elemente de o literă poți folosi parserul simplu; pentru Na, Cl, Ca e mai clar să lucrezi direct cu perechi element-cantitate.

Limitarea parserului simplu: codul de bază citește simboluri de o singură literă și cifre de o singură cifră. Nu tratează paranteze, indici de două cifre sau elemente precum Na și Cl fără o extindere separată.

Sarcina 1: Afișare detaliată
Pași de rezolvare
  1. Localizează linia total += nr * masaElem; în funcția calculeazaMasa()
  2. Imediat după, adaugă cout << simbol << " x " << nr << " = " << nr * masaElem << endl;
  3. Înainte de return total;, adaugă linia de separare și totalul
  4. Testează cu "C6H12O6" → trebuie să afișeze C×6=72, H×12=12, O×6=96, Total=180

Modifică funcția calculeazaMasa să afișeze la fiecare pas: "C × 6 = 72", "H × 12 = 12", etc., apoi totalul.

Afișare: simbol × cantitate = subtotal, la fiecare pas al parsării
cout << simbol << " x " << nr << " = " << nr * masaElem << endl;
Se citește: „afișăm simbolul, cantitatea și produsul lor la fiecare iterație"
// Pseudocod: afișare detaliată a parsării FUNCȚIE calculeazaMasaDetaliat(formula) total 0 // ... parsare normală (simbol + nr) ... total total + nr · masaElem SCRIE simbol, " × ", nr, " = ", nr · masaElem // afișare la fiecare pas SCRIE "---" SCRIE "Total = ", total, " u" RETURNEAZĂ total SFÂRȘIT FUNCȚIE

De ce funcționează? Afișarea la fiecare pas al parsării ne ajută la debugging — vedem exact ce simbol a detectat programul, câte atome și masa parțială. Dacă totalul nu e corect, putem identifica imediat care element a fost citit greșit.

// Dupa linia "total += nr * masaElem;" adauga:
cout << simbol << " x " << nr   // afiseaza simbol si cantitate
     << " = " << nr * masaElem << endl;  // si subtotalul
// La final, inainte de return:
cout << "---\nTotal = " << total  // linie separare
     << " u" << endl;               // unitatea de masura
Sarcina 2: Top 3 cele mai grele
Pași de rezolvare
  1. Citește n formule chimice (string) într-un vector + calculează masele cu calculeazaMasa()
  2. Sortează descrescător vectorul de mase (cu swap simultan pe vectorul de formule)
  3. Afișează primele min(n, 3) elemente
  4. Testează cu: H2O (18), C6H12O6 (180), H2SO4 (98), NaCl (58) → top: glucoză, acid, sare

Citește n formule chimice de la tastatură. Afișează top 3 cele mai grele substanțe cu masa lor.

Sortare descrescătoare: dacă masa[j] > masa[i], interschimbăm
if(maseCalc[j] > maseCalc[i]) { swap(...); swap(...); }
Se citește: „dacă substanța j e mai grea decât i, le inversăm ca cele mai grele să ajungă primele"
// Pseudocod: top 3 cele mai grele substanțe CITEȘTE n PENTRU i 0, n-1 CITEȘTE formule[i] maseCalc[i] calculeazaMasa(formule[i]) SFÂRȘIT PENTRU // Sortare descrescătoare PENTRU i 0, n-2 PENTRU j i+1, n-1 DACĂ maseCalc[j] > maseCalc[i] ATUNCI interschimbă(maseCalc[i], maseCalc[j]) interschimbă(formule[i], formule[j]) SFÂRȘIT PENTRU top min(n, 3) PENTRU i 0, top-1 SCRIE i+1, ". ", formule[i], " = ", maseCalc[i], " u" SFÂRȘIT PENTRU

De ce funcționează? Sortarea descrescătoare mută substanțele cu masa cea mai mare pe primele poziții. Interschimbăm simultan și vectorul de formule și cel de mase, ca fiecare formulă să rămână asociată cu masa ei corectă.

int n; cin >> n;                           // cate formule citim
string formule[100]; int maseCalc[100];    // vectori paraleli
for(int i=0; i<n; i++){
    cin >> formule[i];                     // citim formula
    maseCalc[i] = calculeazaMasa(formule[i]); // calculam masa
}
// Sortare descrescatoare (selection sort)
for(int i=0; i<n-1; i++)
    for(int j=i+1; j<n; j++)
        if(maseCalc[j] > maseCalc[i]){    // j mai grea?
            swap(maseCalc[i], maseCalc[j]); // inversam masele
            swap(formule[i], formule[j]);   // si formulele
        }
int top = (n < 3) ? n : 3;               // maxim 3 rezultate
for(int i=0; i<top; i++)
    cout << i+1 << ". " << formule[i]    // pozitia + formula
         << " = " << maseCalc[i] << " u" << endl;

Descarcă toate exercițiile și soluțiile