Sunteți pe pagina 1din 20

Recapitulare

Ciurul lui Eratostene


Verificarea temei
1. Spuneti daca n citit de la tastatura este format din exact doua cifre repetate (n <
2.000.000.000). !! Rezolvare fara vectori !!
Exemple:
n = 90990900 → DA
n = 21323232 → NU
Dupa ce rezolvati, determinati complexitatea algoritmului
- se citeste n
- c1 = n%10
- n = n/10
- c2 = 100
- x = 1
- cat timp n > 0 executa
- daca n%10 != c1 && n%10 != c2 && c2 != 100 atunci
- x = 0
- daca n%10 != c1 && c2 == 100 atunci
- c2 = n%10
- n = n / 10
- daca x == 1 atunci
- se afiseaza “OK”
- altfel
- se afiseaza “Not OK”
→ Complexitatea = O(log(10)n)
Verificarea temei
2. Se citesc a si n, numere naturale, de la tastatura. Sa se calculeze cat mai eficient a la
puterea n si s se afiseze.

Exemple:
a = 2, n = 10 → 1024
a = 3, n = 7 → 2187
Dupa ce rezolvati, determinati complexitatea algoritmului
- se citesc a,n
- rez = 1
- cat timp n > 0 executa
- rez = rez*a
- n = n-1
- se afiseaza rez → Complexitate O(n)

- rez = 1
- cat timp (n>0) executa
- daca n%2 == 1 atunci
- rez = rez * a
- a = a*a
- n = n/2
- se afiseaza rez → complexitatea este acea putere a lui 2 care il acopera pe n → O(log(2)n)
Verificarea temei
3. Paranteze: Se citesc n si o secventa de 0-uri si 1-uri, unde 0 reprezinta o paranteza deschisa
‘(’ si 1 reprezinta o paranteza inchisa ‘)’. Sa se spuna daca secventa de paranteze este corecta
si, daca da, sa se spuna si factorul de imbricare (numarul de paranteze interioare). !! Rezolvare
fara vectori !!

Exemple:
n = 8 si secventa 0 1 0 0 1 0 1 1 se va afisa:
DA
2
Obs: Nivelul de imbricare este 2 pt ca, in traducere am avea ()(()())

n = 8 si secventa 0 0 0 1 1 1 0 0 se va afisa:
NU

Idei de rezolvare?
Rezolvare
#include <iostream> Ce complexitate avem in cazul acestei
using namespace std;
solutii?
int main() {
int n, a, i, nrdesc, max;
cin>>n; Complexitatea = O(n)
max = nrdesc = i = 0;
while ( i < n && nrdesc >= 0 ) {
cin>>a;
if ( a == 0 ) {
nrdesc++;
if ( nrdesc > max ) {
max = nrdesc;
}
} else {
nrdesc--;
}
i++;
}
if ( nrdesc == 0 ) {
cout<<"DA"<<endl<<max;
}
else {
cout<<"NU";
}
return 0;
}
Ciurul lui Eratostene - utilizare
1. sa presupunem ca vrem sa determinam toate numerele prime pana la un n dat;
2. se creeaza o lista a intregilor consecutivi de la 2 la n → 2 3 4 … n;
3. se cauta primul numar “netaiat” din lista (initial toate numerele sunt
netaiate);
4. sa presupunem ca primul numar netaiat este p;
5. se merge in lista din p in p incepand cu 2*p si se taie toate numerele
intalnite (unele vor fi deja taiate);
6. se reia pasul 3 pana ce se depaseste n.

In final, toate numerele ramase netaiate sunt prime.


Ciurul lui Eratostene - exemplu
De exemplu, pentru n = 9, vom avea vectorul de frecventa v cu indicii relevanti de la 2 la 9, unde:
- v[i] = 0, inseamna ca v[i] este netaiat, deci i este posibil numar prim;
- v[i] = 1, inseamna ca v[i] este taiat, deci i SIGUR nu este numar prim;
- la final, ne intereseaza acei indici i pt care v[i] = 0.

v = 0 0 0 0 0 0 0 0
2 3 4 5 6 7 8 9
- primul indice netaiat este 2 → vom taia 4,6,8, deci v devine:

v = 0 0 1 0 1 0 1 0
2 3 4 5 6 7 8 9
- al doilea indice netaiat este 3 → vom taia 6,9 deci v devine:

v = 0 0 1 0 1 0 1 1
2 3 4 5 6 7 8 9
- al treilea indice netaiat este 5 → nu avem ce sa taiem
- al patrulea indice netaiat este 7 → nu avem ce sa taiem
- nu mai exista indici netaiati dupa 7 → numerele prime mai mici sau egale cu 9 sunt 2 3 5 7

Obs: Pentru a optimiza spatiul de memorie folosit, vom folosi vectorul de frecventa ca fiind un vector de
caractere, preluand valorile intregi 0 si 1 ca in exemplu → de ce este “mai optim” asa?
Ciurul lui Eratostene - Solutie I
#include <iostream>
using namespace std;
char v[2000002];
int main()
{
int n,i,d;
cin>>n;
for (i = 2; i < n; i++) { // se parcurg toate valorile din vectorul de frecventa
if (v[i] == 0) { // daca elementul curent este netaiat
for (d = i + i; d <= n; d = d + i) { // se taie toate elementele din i in i, pornind de la 2*i
v[d] = 1;
}
}
}
for (i = 2; i <= n; i++) { // se parcur elementele si se afiseaza cele netaiate
if (v[i] == 0) {
cout<<i<<" ";
}
}
return 0;
}

Cum putem imbunatati si mai mult solutia noastra?


Ciurul lui Eratostene - Solutie II
Imbunatatirea celui de-al doilea for:
● in solutia I, mergem cu d de la 2*i, din i in i;
● adica d ia valorile 2*i, 3*i, 4*i … k*i;
● observam ca pentru toate valorile rezultate pt k<i, valorile de forma k*i au
fost deja taiate;
● asadar, d ar putea porni de la i*i.

Imbunatatirea primului for:


● in solutia I, mergem cu i de la 2 la n-1;
● avand in vedere ca in imbunatatirea anterioara pornim de la i*i, inseamna ca si
in primul for putem merge de la 2 pana la radical din n, deoarece nu vom mai
gasi niciun d intre sqrt(n)*sqrt(n);
● sa nu uitam sa includem libraria cmath pentru a folosi radicalul.
Ciurul lui Eratostene - Solutie II
#include <iostream>
using namespace std;
char v[2000002];
int main()
{
int n,i,d;
cin>>n;
for (i = 2; i*i <= n; i++) {
if (v[i] == 0) {
for (d = i * i; d <= n; d = d + i) {
v[d] = 1;
}
}
}
for (i = 2; i <= n; i++) {
if (v[i] == 0) {
cout<<i<<" ";
}
}
return 0;
}
Ciurul lui Eratostene - Adaptare I
Algoritmul ciurului lui Eratostene poate fi folosit si in cadrul altor probleme, folosind solutia I putin
modificata in functie de problema.

Numarul de divizori primi


Sa se calculeze numarul divizorilor primi (si proprii) al tuturor numerelor de la 1 la n.

De exemplu, daca n = 10, vom avea asocierile:


2 → 0
3 → 0
4 → 1 (il are pe 2)
5 → 0
6 → 2 (ii are pe 2 si pe 3)
7 → 0
8 → 1 (il are pe 2)
9 → 1 (il are pe 3)
10 → 2 (ii are pe 2 si pe 5)

Rezolvarea se bazeaza pe solutia I, modificand operatia v[d] = 1 in v[d]++. De asemenea, se va modifica si


afisarea.
Ciurul lui Eratostene - Adaptare I in C++
#include <iostream>
using namespace std;
char v[2000001];
int main()
{
int n,i,d;
cin>>n;
for (i = 2; i < n; i++) {
if (v[i] == 0) {
for (d = i + i; d <= n; d = d + i) {
v[d]++;
}
}
}
for (i = 2; i <= n; i++) {
cout<<i<<" --> "<<(int)v[i]<<endl;
}
return 0;
}
Ciurul lui Eratostene - Adaptare II
Numarul de divizori
Sa se calculeze numarul divizorilor proprii tuturor numerelor de la 1 la n.

De exemplu, daca n = 12, vom avea asocierile:


2 → 0
3 → 0
4 → 1 (il are pe 2)
5 → 0
6 → 2 (ii are pe 2 si pe 3)
7 → 0
8 → 2 (ii are pe 2 si pe 4)
9 → 1 (il are pe 3)
10 → 2 (ii are pe 2 si pe 5)
11 → 0
12 → 4 (ii are pe 2, 3, 4 si pe 6)
Ciurul lui Eratostene - Adaptare I in C++
#include <iostream>
using namespace std;
int v[2000001];
int main()
{
int n,i,d;
cin>>n;
for (i = 2; i < n; i++) {
for (d = i + i; d <= n; d = d + i) {
v[d]++;
}
}
for (i = 2; i <= n; i++) {
cout<<i<<" --> "<<v[i]<<endl;
}
return 0;
}
Tema
1. https://infoarena.ro/problema/prim

Logic de rezolvare:
- se construieste ciurul pentru 1300000 numere → vectorul va avea 13000001
- se va afisa al k+1-lea numar prim la puterea a doua (atentie la tipul de date)
In C++
Tema
2. http://varena.ro/problema/extraprime

Logic de rezolvare:
-
In C++
Tema
3. https://infoarena.ro/problema/cmmdc2

Logic de rezolvare:
-
In C++

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