Sunteți pe pagina 1din 14

Algoritmi elementari

Maxime și minime
Determinarea minimului
Să rezolvăm următoarea problemă: Se dau n numere întregi. Calculaţi cel mai
mic dintre cele n numere date.

Numărul de valori se cunoaște de la început. Algoritmul de rezolvare va fi:

• inițializăm variabila min corespunzător


• citim cele n numere
o fiecare număr se compară cu min și dacă este cazul se
actualizează min

Cu ce valoare putem inițializa min? Distingem două posibilități:

• inițializăm min cu prima dintre cele n valori; celelalalte n-1 valori se vor
compara cu min
• inițializăm min cu o valoare foarte mare; fiecare dintre cele n valori citite se
va compara cu min. Alegerea valorii inițiale a lui min depinde de restricțiile
problemei; pentru problema #n_minim poate fi 1.000.000.000.

În secvențele care urmează, n este numărul de valori citite, în x se citesc pe rând


valorile iar în min vom determina valoarea minimă:

Inițializare cu prima valoare

min = x[1];
for(int i =2 ; i <= n ; i ++)
{
if(x[i] < min)
min = x;
}

• Cifrele unui număr


• #include <iostream>
• using namespace std;
• int main()
• {
• int n;
• cin >> n;
• while(n != 0) // cat timp n este nenul - mai are cifre
• {
• int uc = n % 10; //determinam ultima cifra a lui n
• cout << uc << " "; // prelucram ultima cifra
• n /= 10; // eliminam ultima cifra (trunchiem numarul)
• }
• return 0;
• }

Exemplul 1: Determinarea oglinditului unui număr dat

Prin oglinditul (inversul) unui număr se înțelege un numărul scris cu cifrele


numărului inițial, în ordine inversă. De exemplu, oglinditul lui 274 este 472, iar
oglinditul lui 1300 este 31 – numerele nu pot să înceapă cu cifra 0.

Rezolvare:

• Fie n numărul dat, și ogl variabila în care vom calcula rezultatul.


• Inițial ogl = 0.
• Vom aplica procedeul de determinare prin trunchieri succesive a cifrelor
lui n.
• Fiecare cifră a lui n, calculată prin n % 10 va fi adăugată la sfârșitul lui ogl,
prin atribuirea ogl = 10 * ogl + n % 10.

Program C++:

#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
int ogl= 0;
while(n){
ogl =10*ogl + n%10;
n /= 10;
}
cout << ogl << endl;
return 0;
}

Divizibilitate
o Divizorii unui număr

Soluția acceptabilă este să observăm că divizorii oricărui număr n sunt în


pereche: dacă d este divizor al lui n, atunci și n/d este divizor al lui n. De
exemplu, pentru n = 75.

• 1este divizor 75, atunci și 75/1 = 75 este divizor al lui 75;


• 2nu este divizor al lui 75
• 3este divizor 75, atunci și 75/3 = 25 este divizor al lui 75;
• 4nu este divizor al lui 75
• 5este divizor 75, atunci și 75/5 = 15 este divizor al lui 75;
• 6nu este divizor al lui 75
• 7nu este divizor al lui 75
• 8nu este divizor al lui 75
• 9nu este divizor al lui 75. Mai mult, 9 * 9 > 75, alți divizori nu vom mai
găsi.

Divizorii lui 75 sunt: 1 75 3 25 5 15. Constatăm astfel că pentru a determina


divizorii lui n este suficient să parcurgem numerele de la 1 la n−−√n.
Un caz special îl constituie pătratele perfecte. În cazul lor trebuie evitată
analizarea de două ori a lui n−−√n, care este divizor al lui n. Pentru 36 avem
divizorii:

• 1 în pereche cu 36
• 2 în pereche cu 18
• 3 în pereche cu 12
• 4 în pereche cu 9
• 5 nu este divizor al lui 36
• 6 în pereche cu 6. 6 trebuie luat o singură dată!
• 7*7>36, ne oprim!

Noua variantă a programului care afișează divizorii lui n este:

#include <iostream>
Using namespace std;
int main()
{
int n;
cin >> n;
for(int d =1 ; d * d <= n ; d ++ )
if(n % d == 0)
{
cout << d << " ";
if(d * d < n) // dacă d != sqrt(n)
std :: cout << n / d << " ";
}
return 0;
}

În programul de mai sus am evitat utilizarea funcției sqrt, pentru calculul


radicalului, prin expresia d <= sqrt(n). Am preferat o formă echivalentă: d * d
<= n, mai eficientă!

o Algoritmul lui Euclid

Definiții
Fie aa și bb două numere naturale. Un număr natural dd se numește cel mai
mare divizor comun (pe scurt cmmdc) al lui aa și bb dacă îndeplinește
condițiile:

1. d|ad|a și d|bd|b;
2. dacă c|ac|a și c|bc|b, atunci c|dc|d.

Cel mai mare divizor comun al numerelor aa și bb se


notează (a,b)(a,b) sau gcd(a,b)gcd(a,b) – greatest common divisor.
Dacă (a,b)=1(a,b)=1, spunem că aa și bb sunt prime între ele sau relativ prime,
sau că aa este prim cu bb.

Proprietăți cmmdc
Cel mai mare divizor comun al numerelor naturale are proprietățile:
1. ((a,b),c)=(a,(b,c)),∀a,b,c∈N((a,b),c)=(a,(b,c)),∀a,b,c∈N
2. (a,b)=1,(a,c)=1⇒(a,bc)=1(a,b)=1,(a,c)=1⇒(a,bc)=1
3. a|bc și (a,b)=1⇒a|ca|bc și (a,b)=1⇒a|c
4. a|c,b|c și (a,b)=1⇒ab|ca|c,b|c și (a,b)=1⇒ab|c

Determinarea celui mai mare divizor comun


Cel mai mare divizor comun al două numere naturale n și m poate fi determinat
folosind descompunerea în factori primi a celor două numere. Această metodă
este mai dificil de implementat. Există o metodă mai simplu de implementat într-
un program, numită algoritmul lui Euclid.

Sunt două variante ale algoritmului lui Euclid: cu scăderi și cu împărțiri.

Algoritmul lui Euclid cu scăderi


Algoritmul lui Euclid cu scăderi se bazează pe ideea că cele mai mare divizor
a două numere divide și diferența acestora. Algoritmul este:

• Cât timp numerele sunt diferite, se scade numărul mai mic din numărul mai
mare.
• Când numerele devin egale, valoare comună este cel mai mare divizor
comun al valorilor inițiale.
• Algoritmul nu poate fi aplicat dacă unul dintre numere este 0. De ce?

Exemplu:

• Fie n=32 și m=24.


• Numerele nu sunt egale, scădem numărul mai mic din numărul mai mare, n
= n - m = 32 - 24 = 8.
• Acum n = 8 și m = 24.
• Numerele nu sunt egale, scădem numărul mai mic din numărul mai mare, m
= m - n = 24 - 8 = 16.
• Acum n = 8 și m =16.
• Numerele nu sunt egale, scădem numărul mai mic din numărul mai mare, m
= m - n = 16 - 8 = 8.
• Acum n = 8 și m = 8.
• Numerele sunt egale. Valoarea comună, 8, este cel mai mare divizor
comun al valorilor inițiale, 32 și 24

Program C++:

#include <iostream>
int main()
{
int n , m;
cin >> n >> m;
while(n != m)
if(n > m)
n -= m;
else
m -= n;
cout << n << std :: endl;
return 0;
}

Algoritmul lui Euclid cu împărțiri


Algoritmul lui Euclid cu împărțiri se bazează pe ideea că cel mai mare divizor
a două numere divide și restul împărțirii acestora, conform teoremei împărțirii cu
rest. Algoritmul este:

• Cât timp m != 0:
o Determinăm restul împărțirii lui n la m.
o În continuare n devine m, iar m devine restul calculat.
• Valoarea actuală a lui n este cel mai mare divizor comun a valorilor inițiale.

Exemplu:

• Fie n=32 și m=24.


• m != 0:
o Calculăm r = n % m = 8
o n devine m, iar m devine r.
• Acum n=24 și m=8.
• m != 0:
o Calculăm r = n % m = 0
o n devine m, iar m devine r.
• Acum n=8 și m=0.
• m este 0. Valoarea actuală a lui n = 8 este cel mai mare divizor comun al
valorilor inițiale, 32 și 24.

Program C++:

#include <iostream>
int main()
{
int n , m;
cin >> n >> m;
while(m != 0)
{
int r = n % m;
n = m;
m = r;
}
cout << n << std :: endl;
return 0;
}

Determinarea cmmdc pentru mai multe numere


Pentru a determina cel mai mare divizor comun a mai multor numere:

• determinăm cmmdc dintre primele două numere.


• determinăm cmmdc între cmmdc anterior și al treilea număr.
• determinăm cmmdc între cmmdc anterior și al patrulea număr.
• ș.a.m.d.

Cel mai mic multiplu comun


Fie aa și bb două numere naturale. De numește cel mai mic multiplu
comun (pe scurt cmmmc) al lui aa și bb cel mai mic număr natural nenul cu
proprietatea că se divide atât cu aa cât și cu bb.
Cel mai mic multiplu comun al numerelor aa și bb se
notează [a,b][a,b] sau lcm(a,b)lcm(a,b) – least common multiple.

Determinarea cmmmc
Pentru a determina cel mai mic multiplu comun se pot folosi mai multe metode:

Determinarea cmmmc folosind cmmdc


Observație: Produsul a două numere naturale nenule este egal cu produsul
dintre cel mai mare divizor comun al lor și cel mai mic multiplu comun al lor.

a⋅b=(a,b)⋅[a,b]a⋅b=(a,b)⋅[a,b]
a⋅b=gcd(a,b)⋅lcm(a,b)a⋅b=gcd(a,b)⋅lcm(a,b)

Determinarea cmmmc folosind un algoritm de tip Euclid


Fie a și b valorile date. Vom construi valorile m și n, astfel:

1. inițial n ← a, m ← b;
2. cât timp m != n:
o dacă m < n, atunci m crește cu valoarea lui a: n ← n + a
o dacă m > n, atunci n crește cu valoarea lui b: m ← m + b
3. valoarea finală, comună, a lui n și m este cel mai mic multiplu comun
pentru a și b

Observație: Algoritmul poate fi aplicat similar pentru trei sau mai multe numere!

Verificare primalității
Definiții
Un număr natural p>1p>1 este se numește prim dacă:
p|ab⇒p|a sau p|bp|ab⇒p|a sau p|b
Un număr natural p>1p>1 este se numește indecompozabil (sau ireductibil) dacă:
d|p⇒d=1 sau d=pd|p⇒d=1 sau d=p

Observații
• Pentru orice număr natural p>1p>1, pp este prim dacă și numai dacă este
indecompozabil.
• Cei doi divizori ai unui număr indecompozabil (prim)sunt 1 și însuși
numărul.
• Conform definiției, numerele 0 și 1 nu sunt prime!
• Un număr natural mai mare decât 1 care nu este prim se
numește compus sau decompozabil sau reductibil.

Verificarea primalității
Pentru a stabili dacă un număr p este prim:

• numărăm divizorii săi. Dacă sunt 2 divizori, p este prim.


• determinăm suma divizorilor. Dacă suma este p + 1, numărul este prim.
• căutăm divizori ai săi diferiți de 1 și de el însuși. Dacă nu găsim, numărul
este prim.

Cum verificăm algoritmic dacă un număr natural n este prim?

• presupunem că numărul este prim;


• verificăm cazurile particulare; dacă n este 0 sau 1, schimbăm
presupunerea
• căutăm un divizor în intervalul [2,n−−√][2,n], parcurgând numerele din
interval
o dacă îl găsim, schimbăm presupunerea

Observație: Deoarece divizorii unui număr n sunt în pereche, dacă nu găsim


divizor în intervalul [2,n−−√][2,n], nu vom găsi nici în intervalul [n−−√,n)[n,n).
#include <iostream>
int main()
{
int n;
td :: cin >> n;
prim = 1; // presupunem ca n este prim
if(n < 2)
prim = 0; // 0 si 1 nu sunt prime
for(int d =2 ; d * d <= n ; d ++)
if(n % d == 0)
prim = 0;
if(prim)
cout << n << " este prim";
else
cout << n << " nu este prim";
return 0;
}

Descompunerea în factori primi


escompunerea în factori primi se bazează pe Teorema fundamentală a
aritmeticii: Orice număr natural n mai mare decât 1 se poate scrie în mod unic
sub forma n=pe11⋅pe22⋅…⋅pekkn=p1e1⋅p2e2⋅…⋅pkek,
unde p1<p2<…<pkp1<p2<…<pk sunt numere prime, iar ei>0,i=1..kei>0,i=1..k
Exemplu: 140=22⋅51⋅71140=22⋅51⋅71
Pentru a determina descompunerea, vom proceda deductiv:

• știm că factorii primi ai lui n sunt cuprinși între 2 și n;


• vom parcurge succesiv aceste numere și pentru un divizor curent d al lui n;
o determinăm puterea sa în descompunere numărând de câte ori se
poate împărții n la d. Această împărțire se realizează efectiv.
o afișam divizorul curent d și puterea sa;
• procesul se încheie când n devine 1.

Program C++:

#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
int d = 2, // d va fi, pe rand, fiecare factor prim din descompunere
p; // p va fi puterea lui d in descompunere
// il im partim pe n la d in mod repetat, pana cand devine 1
while(n > 1)
{
// numaram de cate ori se imparte n la d. Aceasta va fi puterea lui d
in descompunere
p = 0;
while(n % d == 0)
{
++p;
n /= d;
}
// daca s-a facut cel putin o impartire, afisam factorul si puterea
if(p)
cout << d << " " << p << endl;
++ d;
// daca d * d il depaseste pe n si n nu este 1, decidem ca n este
prim,
// si este factor in descompunerea valorii initiale a lui n
if(n>1 && d * d > n){
d = n; // trecem direct la n, urmatorul factor din descompunere
}
}
return 0;
}

Programul de mai sus afișează pentru n descompunerea în factori primi; pe


fiecare linie se afișează perechea factor putere. Merită observat că nu se
parcurg toate numerele de la 1 la n. Dacă la un moment dat se decide că
valoarea curentă a lui n este număr prim, se trece direct la aceasta, fără a mai
parcurge un șir lung de numere care nu mai pot fi factori primi ai lui n.

Șirul lui Fibonacci

Numerele Fibonacci sunt numere naturale care fac parte din următorul șir, în care
fiecare număr este egal cu suma celor două de dinainte:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …

Uneori, șirul este extins cu încă un termen, la început:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …

Termenul Fn este calculat prin următoarea relație de recurență:

Fn = Fn-1 + Fn-2

cu valorile inițiale F1=1, F2=1 sau F0=0 și F1=1.


Cum determinăm primii N termeni din șirul lui Fibonacci? Vom folosi trei variabile
simple a b c. Două dintre ele vor reprezenta termenii anteriori Fn-1 și Fn-2, iar a
treia va reprezenta termenul curent Fn:

a ← 1
b ← 1
scrie a, b
pentru i ← 3,n execută
c ← a + b
scrie c
a ← b
b ← c
sfarsit_pentru

Baze de numeratie
orice număr natural b mai mare decât 1 poate fi considerat bază de numerație, iar
cifrele folosite în baza b sunt 0, 1, 2, …, b-1. În contextul computerelor sunt
utilizate frecvent bazele 10, 2, 8 și 16. Să le analizăm:

• numerele scrise în baza 10 vor conține cifrele: 0 1 2 3 4 5 6 7 8 9


• numerele scrise în baza 2 vor conține cifrele: 0 1
• numerele scrise în baza 8 vor conține cifrele: 0 1 2 3 4 5 6 7
• ce facem cu baza 16. Cele spuse mai sus conduc la ideea că cifrele trebuie
să fie 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 – resturile posibile la
împărțirea cu 16, dar apare o întrebare: Care sunt cifrele numărului 112(16)?
Are trei cifre, 1, 1 și 2? Are două cifre, 11 și 2 sau 1 și 12? Este o situație
confuză, inacceptabilă, astfel că, pentru cifrele cu valorile 10 11 12 13 14
15 se folosesc următoarele notații:
o 10 se notează cu A (sau a)
o 11 se notează cu B (sau b)
o 12 se notează cu C (sau c)
o 13 se notează cu D (sau d)
o 14 se notează cu E (sau e)
o 15 se notează cu F (sau f)
• cu convențiile de mai sus, situația numărului 112(16) este clarificată: are trei
cifre, 1, 1 și 2. Dacă dorim să scriem numărul cu două cifre cu
valorile 11 și 2, vom avea: B2(16).
Transformarea din baza 10 în baza b
Cum transformăm un număr oarecare n din baza 10 într-o bază oarecare, b?
Algoritmul de transformare este foarte asemănător cu cel de determinare a
cifrelor în baza 10. De fapt, este chiar identic, doar că baza nu este 10, ci b. Mai
exact:

• împărțim numărul n la b. Obținem un cât și un rest;


• împărțim câtul la b. Obținem un cât și un rest;
• împărțim noul cât la b și obținem un cât și un rest;
• continuăm împărțirile până când obținem câtul 0;
• resturile obținute, scrise în ordinea inversă obținerii, reprezintă scrierea în
baza b a lui n.

Exemplu: să transformăm numărul 24 din baza 10 în baza 2. Efectuăm


împărțirile:

24:212:26:23:21:2=12 rest 0=6 rest 0=3 rest 0=1 rest 1=0 rest 124:2=12 rest
012:2=6 rest 06:2=3 rest 03:2=1 rest 11:2=0 rest 1

Scriem resturile în ordine inversă și obținem: 24(10) = 11000(2). Să observăm că


am obținut cifrele în ordinea inversă față de poziția lor în număr!!

Cu folosim această transformare într-un program? Dacă dorim să afișăm cifrele


reprezentării în baza b, le putem afișa în ordinea determinării. Pentru a le afișa în
ordinea din număr ar trebui să le stocăm pe toate într-o structură de date
convenabilă – de exemplu un tablou. De asemenea, le putem număra, aduna,
determina maximul, etc.

Secvența C++ de mai jos determină cea mai mare cifră a reprezentării în baza b a
numărului n.

cmax = 0;
cin >> n >> b;
while(n)
{
int cif= n % b;
if(cif > cmax)
cmax = cif;
n /= b;
}
Transformarea din baza b în baza 10
Pentru transformarea numărului ckck−1…c1c0(b)ckck−1…c1c0(b) din baza b în
baza 10 folosim
formula ckck−1…c1c0(b)=ck⋅bk+ck−1⋅bk−1+…+c1⋅b+c0ckck−1…c1c0(b)=ck⋅bk+c
k−1⋅bk−1+…+c1⋅b+c0, în care operațiile de adunare, înmulțire și ridicare la putere
se fac în baza 10. De fapt această formulă este echivalentă cu reprezentarea
zecimală a numerelor, de
exemplu: 253(10)=2⋅102+5⋅101+3⋅100253(10)=2⋅102+5⋅101+3⋅100.
Exemplu

11000(2)=1⋅24+1⋅23+0⋅22+0⋅21+0⋅20=1⋅16+1⋅8=16+8=2411000(2)=1⋅24+1⋅23+
0⋅22+0⋅21+0⋅20=1⋅16+1⋅8=16+8=24

Algoritmul: Să presupunem că se citește baza b, numărul n de cifre ale


numărului în baza b, apoi cifrele. Determinarea reprezentării în baza 10 este:

cin >> b >> n;


int rez = 0;
for(int i =1 ; i <= n ; i ++)
{
int x;
cin >> x;
rez = rez * b + x;
}
cout << rez;

S-ar putea să vă placă și