Ce vei învăța astăzi?
După această lecție, vei stăpâni găsirea divizorilor și testarea divizibilității pentru orice număr.
Ce sunt Divizorii?
Definiție: Un număr d este divizor al numărului n dacă restul împărțirii lui n la d este 0.
Exemplu: Pentru n=12, divizorii sunt: 1, 2, 3, 4, 6, 12
12 ÷ 1 = 12, 12 ÷ 2 = 6, 12 ÷ 3 = 4, 12 ÷ 4 = 3, 12 ÷ 6 = 2, 12 ÷ 12 = 1
Analogie: Cutii și obiecte
Gândește-te la divizori ca la cutii în care încap exact obiecte fără a rămâne rest:
Încap în:
1 cutie cu 12 2 cutii cu 6 3 cutii cu 4n % d == 0
restul este 0?1↔12, 2↔6, 3↔4
produsul = nCum găsim divizorii unui număr - 3 metode de gândire:
Metoda 1: Împărțiri succesive
"Împarte n la toate numerele mai mici"
Pentru fiecare număr d de la 1 la n:
- Dacă n % d == 0 → d este divizor
- Altfel → nu este divizor
✅ Simplu de înțeles ❌ Ineficient pentru numere mari
Metoda 2: Perechi de divizori
"Găsește perechile care înmulțite dau n"
Dacă d este divizor, atunci și n/d este divizor!
✅ Optimizat: căutăm doar până la √n ❌ Trebuie ordonat pentru afișare
Metoda 3: Pătrate perfecte
"Atenție când d = n/d"
Pentru pătrate perfecte (16, 25, 36), divizorul √n apare o singură dată!
Exemplu pentru n=16: √16=4, divizori: (1,16), (2,8), (4) - fără pereche!
✅ Tratează toate cazurile ❌ Cod mai complex
Concluzie: Vom folosi metoda perechilor (B) pentru că este optimă și funcționează pentru orice număr. Adăugăm doar verificarea pentru pătrate perfecte din metoda C.
Algoritmul pentru Găsirea Divizorilor
Simplu dar ineficient: Pentru n=1000000, face 1 milion de iterații. Folosit doar pentru numere mici.
Optimizare matematică: Dacă d divide pe n, atunci și n/d îl divide. Ne oprim la √n pentru că după aceea divizorii se repetă.
| Metodă | Număr iterații | Pentru n=100 | Pentru n=1000000 | Recomandare |
|---|---|---|---|---|
| Până la n | n iterații | 100 iterații | 1.000.000 iterații | ❌ Doar pentru n≤1000 |
| Până la √n | √n iterații | 10 iterații | 1000 iterații | ✅ Pentru orice n |
Exemplu pentru n=36: Metoda 1: 36 iterații, Metoda 2: √36=6 iterații. Divizorii apar în perechi: (1,36), (2,18), (3,12), (4,9), (6,6).
De ce includem <cmath> și când folosim sqrt?
În algoritmul de găsire a divizorilor, trebuie să parcurgem numerele până la rădăcina pătrată a lui n. Pentru aceasta, avem două variante:
Varianta 1: Folosim funcția sqrt() din biblioteca <cmath>
#include <iostream>
#include <cmath> // pentru sqrt()
using namespace std;
int main() {
int n;
cin >> n;
for (int d = 1; d <= sqrt(n); d++) {
// ... verifică dacă d este divizor
}
return 0;
}
Varianta 2: Folosim condiția d * d <= n (fără sqrt)
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int d = 1; d * d <= n; d++) {
// ... verifică dacă d este divizor
}
return 0;
}
Care este mai bine?
Condiția d * d <= n este de preferat, deoarece:
- Este mai rapidă: evită apelarea funcției
sqrtcare calculează o valoare cu virgulă mobilă. - Este mai precisă: evită erorile de rotunjire care pot apărea la conversia la întreg.
- Este mai simplă: nu necesită includerea bibliotecii suplimentare.
Dar atunci de ce se folosește <cmath> în exerciții?
Uneori, în probleme mai complexe, ai nevoie de alte funcții matematice (puteri, logaritmi, etc.) și atunci includerea <cmath> este necesară. În cazul nostru, pentru găsirea divizorilor, putem folosi oricare dintre variante, dar varianta cu d * d <= n este recomandată.
Concluzie: Pentru găsirea divizorilor, folosește condiția d * d <= n în bucla for. Este mai eficientă și nu necesită <cmath>. Totuși, este bine să cunoști și funcția sqrt pentru alte situații.
Exersăm împreună: n = 24
| Pas | d | d * d <= 24? | 24 % d == 0? | Divizor găsit (d) | Divizor pereche (n/d) |
|---|---|---|---|---|---|
| Start | 1 | ✅ 1*1=1 ≤ 24 | ✅ 24%1=0 | 1 | 24 |
| Pas 1 | 2 | ✅ 2*2=4 ≤ 24 | ✅ 24%2=0 | 2 | 12 |
| Pas 2 | 3 | ✅ 3*3=9 ≤ 24 | ✅ 24%3=0 | 3 | 8 |
| Pas 3 | 4 | ✅ 4*4=16 ≤ 24 | ✅ 24%4=0 | 4 | 6 |
| Pas 4 | 5 | ✅ 5*5=25 ≤ 24? ❌ | ❌ 24%5=4 | - | - |
| Final | - | ✓ 6 iterații totale | ✓ 4 divizori găsiți |
Divizori în perechi: (1,24) (2,12) (3,8) (4,6) |
|
Divizorii lui 24 în ordine crescătoare: 1, 2, 3, 4, 6, 8, 12, 24
Observație importantă: Algoritmul se oprește la d=4 (deoarece 5*5=25>24). Am făcut doar 6 iterații (d=1,2,3,4,5) în loc de 24! Asta este optimizarea.
Exercițiu de completare - Găsirea divizorilor:
Completează codul C++ pentru afișarea divizorilor:
Bancă de cuvinte:
Test de înțelegere: Care sunt divizorii?
Test rapid:
Câți divizori are numărul 24 și care sunt ei?
Algoritmul pentru n=24 (√24≈4.9):
Capcane comune și soluții pentru divizori
Greșeli frecvente la găsirea divizorilor:
Soluție: for (int d = 1; d * d <= n; d++) sau int limit = sqrt(n);
Pentru n=16, d=4: n/d=4 → ar afișa 4 de două ori.
n=10⁶ → 1 milion de iterații vs 1000 iterații cu metoda optimă.
Best Practices pentru divizori:
Reguli rapide pentru a lucra cu divizori:
Regula 1: Toate numerele au cel puțin 2 divizori
1 și numărul însuși - numerele prime au exact acești 2 divizori.
Exemplu: 7 are divizorii: 1, 7
Excepție: 1 are un singur divizor (1)
Regula 2: Divizorii apar în perechi
Dacă d este divizor, atunci și n/d este divizor.
Exemplu pentru n=12: 3 × 4 = 12 (3 și 4 sunt divizori perechi)
Regula 3: Pătratele perfecte au număr impar de divizori
√n este divizor, dar nu are pereche (d = n/d).
Exemplu pentru n=16: √16=4, divizori: 1,2,4,8,16 (5 divizori - impar)
Exemplu pentru n=12: √12≈3.46, divizori: 1,2,3,4,6,12 (6 divizori - par)
Test rapid: Dacă √n este întreg → pătrat perfect → număr impar de divizori
Regula 4: Numărul de divizori crește rapid
Numerele mici pot avea mulți divizori, numerele mari pot avea puțini!
| Număr | Divizori | Număr de divizori |
|---|---|---|
| 12 | 1,2,3,4,6,12 | 6 |
| 17 (prim) | 1,17 | 2 |
| 24 | 1,2,3,4,6,8,12,24 | 8 |
| 36 | 1,2,3,4,6,9,12,18,36 | 9 |
Concluzie: Nu uita să verifici numerele prime (doar 2 divizori)!
PbInfo #7 - Divizorii unui număr
Enunț:
Cerință: Se citește un număr natural n. Să se afișeze toți divizorii lui n în ordine crescătoare.
Date de intrare:
Programul citește de la tastatură numărul n.
Date de ieșire:
Programul va afișa pe ecran toți divizorii lui n, separați prin spațiu, în ordine crescătoare.
Exemplu:
Intrare:
12
Ieșire:
1 2 3 4 6 12
Explicație: 1, 2, 3, 4, 6, 12 sunt divizorii lui 12
Cum gândim soluția:
- Citim numărul n
- Folosim for (d = 1; d * d <= n; d++)
- Dacă
n % d == 0:- Afișăm d (primul divizor)
- Dacă
d != n/d: - Salvăm n/d (pentru afișare mai târziu)
- Pentru ordine crescătoare, afișăm divizorii mari la final
Execuție pentru n=16 (pătrat perfect):
Ce trebuie să verifici mereu?
- Cazul n=1: are un singur divizor (1)
- Numere prime: au exact 2 divizori (1 și numărul însuși)
- Pătrate perfecte: au divizorul √n care nu are pereche
- Condiția d*d <= n pentru optimizare
Scrie soluția în C++:
Soluție corectă:
Varianta fără vector:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
// Afișăm divizorii mici
for (int d = 1; d * d <= n; d++) {
if (n % d == 0) {
cout << d << " ";
}
}
// Afișăm divizorii mari în ordine crescătoare
for (int d = (int)sqrt(n); d >= 1; d--) {
if (n % d == 0 && d != n / d) {
cout << n / d << " ";
}
}
return 0;
}
Ce învățăm din această problemă:
- Optimizare matematică: Ne oprim la √n în loc de n
- Perechi de divizori: Dacă d divide n, atunci n/d îl divide
- Ordinea afișării: Divizorii mici, apoi cei mari pentru ordine crescătoare
- Cazuri speciale: n=1, numere prime, pătrate perfecte
- Structuri de date: Vector pentru stocarea divizorilor mari
PbInfo #8 - Suma divizorilor
Problemă care calculează suma tuturor divizorilor
Enunț:
Cerință: Se citește un număr natural n. Să se calculeze suma divizorilor lui n.
Date de intrare:
Programul citește de la tastatură numărul n.
Date de ieșire:
Programul va afișa pe ecran suma divizorilor lui n.
Exemplu:
Intrare:
12
Ieșire:
28
Explicație: 1+2+3+4+6+12 = 28
Cum gândim soluția:
- Citim numărul n
- Inițializăm
suma ← 0 - Folosim for (d = 1; d * d <= n; d++):
- Dacă
n % d == 0: suma += d(primul divizor)- Dacă
d != n/d: suma += n/d(divizorul pereche)
- Dacă
- Afișăm suma
Cum funcționează pentru n=12:
De ce această problemă este mai ușoară?
Pentru că nu trebuie să păstrăm ordinea! Adunăm divizorii imediat ce îi găsim:
- Nu avem vectori - doar o sumă
- Nu contează ordinea - suma este comutativă
- Nu avem afișare complexă - doar un număr
Formula cheie: suma += d + (d != n/d ? n/d : 0)
Scrie soluția în C++:
Soluție corectă:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int suma = 0;
for (int d = 1; d * d <= n; d++) {
if (n % d == 0) {
suma += d; // Adaugă divizorul mic
// Adaugă divizorul mare dacă e diferit
if (d != n / d) {
suma += n / d;
}
}
}
cout << suma;
return 0;
}
Varianta alternativă (mai scurtă):
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int suma = 0;
for (int d = 1; d * d <= n; d++) {
if (n % d == 0) {
suma += d;
suma += (d == n / d ? 0 : n / d);
}
}
cout << suma;
return 0;
}
Ce învățăm din această problemă:
- Optimizare: Același algoritm ca pentru găsirea divizorilor
- Simplificare: Nu e nevoie de vectori pentru sumă
- Cazuri speciale:
- n=1 → suma=1
- Numere prime → suma = 1 + n
- Pătrate perfecte → atenție la dublare
- Operație ternară: Folosirea lui ?: pentru cod compact
PbInfo Bonus - Numărul de divizori ai unui număr
Pentru cei care vor o provocare în plus
Enunț:
Cerință: Se citește un număr natural n. Să se afișeze câți divizori are numărul n.
Date de intrare:
Programul citește de la tastatură numărul n.
Date de ieșire:
Programul va afișa pe ecran numărul de divizori ai lui n.
Exemplu:
Intrare:
12
Ieșire:
6
Explicație: 12 are divizorii 1, 2, 3, 4, 6, 12 (6 divizori)
Cum gândim soluția:
- Citim numărul n
- Inițializăm
contor ← 0 - Folosim for (d = 1; d * d <= n; d++):
- Dacă
n % d == 0: contor++(pentru divizorul mic)- Dacă
d != n/d: contor++(pentru divizorul mare)
- Dacă
- Afișăm contorul
Cum funcționează pentru n=16 (pătrat perfect):
De ce această problemă este importantă?
Numărul de divizori este o proprietate fundamentală a numerelor:
- Numere prime: 2 divizori (1 și numărul însuși)
- Pătrate perfecte: număr impar de divizori
- Numere compuse: număr par de divizori (cel puțin 4)
Formula cheie: contor += (d != n/d) ? 2 : 1
Scrie soluția în C++:
Soluție corectă:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int contor = 0;
for (int d = 1; d * d <= n; d++) {
if (n % d == 0) {
// Am găsit un divizor d
contor++;
// Dacă d și n/d sunt diferiți, mai avem un divizor
if (d != n / d) {
contor++;
}
}
}
cout << contor;
return 0;
}
Varianta alternativă (mai scurtă):
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int contor = 0;
for (int d = 1; d * d <= n; d++) {
if (n % d == 0) {
// Adaugă 2 divizori dacă sunt diferiți, 1 dacă sunt egali
contor += (d == n / d) ? 1 : 2;
}
}
cout << contor;
return 0;
}
Ce învățăm din această problemă:
- Optimizare: Același algoritm ca pentru găsirea divizorilor
- Număr impar vs par: Pătratele perfecte au număr impar de divizori
- Cazuri speciale:
- n=1 → 1 divizor
- Numere prime → 2 divizori
- Pătrate perfecte → număr impar de divizori
- Teste recomandate:
- n=12 → 6 divizori
- n=16 → 5 divizori (impar!)
- n=17 → 2 divizori (prim)
- n=100 → 9 divizori (100=10, pătrat perfect)
Execuție pentru n=18:
Exercițiul Practic: Divizori și Proprietăți
Sarcini de lucru
Completează următoarele sarcini pentru a-ți consolida cunoștințele despre divizori
Sarcina 1: Analiză algoritm divizori
Analizează algoritmul pentru găsirea divizorilor:
Pentru n=36, câte iterații face algoritmul optim (până la √n) vs algoritmul simplu (până la n)?
Soluție pentru Sarcina 1:
Analiza pentru n=36:
- Algoritmul simplu (până la n):
- Parcurge toate numerele de la 1 la 36
- Face 36 de iterații
- Verifică pentru fiecare număr: 36%1, 36%2, ..., 36%36
- Algoritmul optim (până la √n):
- √36 = 6
- Parcurge numerele de la 1 la 6
- Face doar 6 iterații
- La fiecare divizor găsit, afișează și perechea sa
- Raport de optimizare: 36/6 = 6 ori mai rapid!
Divizorii lui 36 în perechi: (1,36), (2,18), (3,12), (4,9), (6,6)
Concluzie: Pentru numere mari, algoritmul optim este esențial!
Sarcina 2: Implementare în C++ pentru divizori proprii
Cerință: Implementează în C++ un program care citește n și afișează divizorii proprii (excluzând 1 și n).
Soluție pentru Sarcina 2:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector divizoriProprii;
// Parcurgem de la 2 la n-1
for (int d = 2; d * d <= n; d++) {
if (n % d == 0) {
// Dacă d este divizor propriu și diferit de 1
if (d != 1) {
divizoriProprii.push_back(d);
}
// Dacă n/d este divizor propriu și diferit de n și d
if (n / d != n && n / d != d && n / d != 1) {
divizoriProprii.push_back(n / d);
}
}
}
if (divizoriProprii.empty()) {
cout << "Nu are divizori proprii";
} else {
cout << "Divizorii proprii sunt: ";
for (int divizor : divizoriProprii) {
cout << divizor << " ";
}
}
cout << endl;
return 0;
}
Explicații importante:
- Divizorii proprii: Divizori diferiți de 1 și n
- Pornește de la 2: Sărim peste 1
- Verificări multiple: Evităm să includem 1 și n
- Numere prime: Nu au divizori proprii
- Teste recomandate:
- n=12 → divizori proprii: 2, 3, 4, 6
- n=17 (prim) → "Nu are divizori proprii"
- n=4 → divizori proprii: 2
- n=1 → niciun divizor propriu