Ridicarea la putere în timp logaritmic


de Popa Sebastian


Enunț

Se dau trei numere naturale \(a\), \(b\) și \(\text{mod}\). Să se calculeze restul împărțirii lui \(a^b\) la \(\text{mod}\).


Observații

Desigur, această problemă se poate rezolva în timp liniar, efectuând \(b\) înmulțiri, însă acest procedeu este de cele mai multe ori prea lent. Pentru a rezolva această problemă eficient vom folosi următoarele proprietăți ale ridicării la putere:


Rezolvare

Ne vom folosi de scrierea în baza \(2\) a exponentului. Astfel, pentru \(b = p_1 + p_2 + \ldots + p_t\) vom obține: \(a^b = a^{p_1 + p_2 + \ldots + p_t} = a^{p_1} \cdot a^{p_2} \cdot \ldots \cdot a^{p_t}\) unde \(p_i\) pentru \(1 ≤ i ≤ t\) sunt puteri distincte ale lui \(2\).
Valorile \(a^{p_i}\) pot fi calculate în complexitate logaritmică astfel: \(a^2 = {(a^1)}^2 , a^4 = {(a^2)}^2 , a^8 = {(a^4)}^2, \ldots, a^{(2^n)} = {(a^{(2^{n - 1})})}^2\).

Exemplu

\(\text{baza} = 3\), \(\text{exponent} = 5_{10} = 101_2\)

\(i\) \(a = \text{baza}^{(2^i)}\) \(\text{exponent}\) \(\text{rez}\) \(\text{explicații}\)
\(0\) \(3\) \(101\) \(3\) bitul actual (cel mai din dreapta) este setat, deci inmultim rezultatul cu \(3\)
\(1\) \(9\) \(10\) \(3\) numarul cu care ar trebui sa inmultim devine \(9\) (\(3^2\)), dar ultimul bit nu este setat, deci nu modificam rezultatul
\(2\) \(81\) \(1\) \(243\) numarul cu care trebuie sa inmultim devine \(81\) (\(3^{(2^2)} = 92\)), ultimul bit este setat, deci inmultim rezultatul cu \(81\), acesta devenind \(243\)

\(a = 2, b = 10_{10} = 1010_2\)

\(a\) \(b\) \(rez\)
\(2\) \(1010\) \(1\)
\(4\) \(101\) \(4\)
\(16\) \(10\) \(4\)
\(256\) \(1\) \(1024\)

Algoritmul va consta în scrierea în baza 2 a exponentului simultan cu efectuarea înmulțirilor necesare:

// rez = (a ^ b) % mod 
rez = 1; // initializam rezultatul cu 1, acesta fiind rezultatul pt a^0, dar si elementul neutru pentru inmultire
a %= mod; //pentru a evita overflow
while (b) //cat timp b este diferit de 0, adică mai are biți setați, adică mai există termeni care trebuie puși la produs
{
    if (b % 2 == 1) // dacă ultimul bit este setat înseamnă că termenul la care am ajuns trebuie adaugat la rezultat
        rez = (1ll * rez * a) % mod; // rez = rez * (a^p[i])
    a = (1ll * a * a) % mod; // p[i] = p[i - 1] ^ 2
    b /= 2; // trecem la urmatorul bit; ex:  1010 devine 101 ; 1101 devine 110
}    

Aplicații