Cerc 6 2018 lectia 2

From Algopedia
Jump to navigationJump to search

Lectie

Descompunerea in factori primi

O implementare a acestei probleme de complexitate O(sqrt(n)):

#include <stdio.h>

int main() {
  int n, div, exp;

  scanf("%d", &n);
  div = 2; //Incepem cautarea factorilor primi de la primul numar prim
  while ( div * div <= n ) { //Cautam factorii primi pana la radicalul lui n
    exp = 0;
    while ( n % div == 0 ) {
      n /= div;
      exp++;
    }
    if (exp > 0)
      printf("%d^%d\n", div, exp);
    div++;
  }
  // daca n mai contine un factor, el este un numar prim la puterea unu
  if ( n > 1 )
    printf( "%d^1\n", n );

  return 0;
}

Numarul de divizori si suma divizorilor

Fie descompunerea unui numar n in factori primi:

Numarul de divizori va fi:

Suma divizorilor:

Vezi demonstratia matematica a formulelor in caiet

Studiu de caz Problema divmul - infoarena

In caiete

Ciurul lui Eratostene

Reamintim aici doar algoritmul studiat anul trecut:

char ciur[2000000];
...
fscanf( fin, "%d", &n );
for ( d = 2; d * d <= n; d++ )
  if ( ciur[d] == 0 ) // daca d este prim
    for ( i = d * d; i <= n; i = i + d ) // vom marca numerele din d in d
      ciur[i] = 1;

Varianta usor optimizata

Pentru un numar d prim, vom marca toti multiplii acestuia, nepari Ex: pentru d = 7, marcam multiplii: 7*7, 7*9, 7*11...sarind peste 7*8, &*10...

char ciur[2000000];
...
fscanf( fin, "%d", &n );
for ( d = 4; d <= n; d += 2 )
  ciur[d] = 1;
for ( d = 3; d * d <= n; d+= 2 )
  if ( ciur[d] == 0 ) // daca d este prim
    for ( i = d * d; i <= n; i = i + 2 * d ) // vom marca numerele din 2d in 2d
      ciur[i] = 1;

Putem de asemenea sa optimizam si memoria folosita, pastrand valorile ciurului doar pentru elementele impare.

ciur[1] va retine daca 3 e prim
ciur[2] va retine daca 5 e prim
ciur[3] va retine daca c7 e prim
ciur[d/2] va retine daca d e prim
Adica:
ciur[k] va retine daca 2k + 1 e prim
char ciur[2000000];
for ( d = 3; d * d <= n; d+= 2 )
  if ( ciur[d/2] == 0 ) // daca d este prim
    for ( i = d * d; i <= n; i = i + 2 * d ) 
      ciur[i/2] = 1;

Utilizarea Ciurului lui Eratostene

Calcului numarului de divizori

Putem modifica modul de parcurgere al vectorului pentru a calcula numarul de divizori pentru numerele de la 1 la n:

int num_div[2000000];
...
fscanf( fin, "%d", &n );
for ( d = 1; d <= n; d++ )
  for ( i = d; i <= n; i = i + d )
    num_div[i]++;

Calcului sumei divizorilor

De asemenea putem calcula similar suma divizorilor pentru numerele de la 1 la n:

int sum_div[2000000];
...
fscanf( fin, "%d", &n );
for ( d = 1; d <= n; d++ )
  for ( i = d; i <= n; i = i + d )
    sum_div[i] = sum_div[i] + d;

Nu uitați:

  • Vectorul ciur[] este de tip caracter și nu întreg!
  • În final vectorul ciur[i] este 0 dacă numărul este prim sau 1 în caz contrar.

Complexitate? Matematica este complexă, dar rețineți că metoda este aproximativ O(n) pentru valorile lui n cu care vom lucra noi. Pentru cei interesați, complexitatea este de fapt O(n∙log log n).

TEMA

Descompunere in factori primi si nr divizoril

Ciur