Fast exponentiation¶
Compute \(a^n\) with about \(\log_2 n\) multiplications instead of \(n - 1\), by squaring: \(a, a^2, a^4, a^8, \dots\) and multiplying together the ones that the binary digits of \(n\) ask for. With remainders taken at each step, it computes \(a^n \bmod m\) for numbers hundreds of digits long.
| Time | Extra space | Output |
|---|---|---|
| \(O(\log n)\) multiplications | \(O(1)\) | \(a^n\), \(a^n \bmod m\), matrix powers |
The idea¶
\(3^{13}\) the slow way is \(3 \cdot 3 \cdot \ldots \cdot 3\), twelve multiplications. But \(13 = 8 + 4 + 1\) (binary
1101), so
and \(3^2, 3^4, 3^8\) come from squaring: \(3^2 = 3 \cdot 3\), \(3^4 = 3^2 \cdot 3^2\), \(3^8 = 3^4 \cdot 3^4\). That is 3 squarings and 2 more multiplications: 5 instead of 12. For \(n\) around a billion it is about 60 instead of a billion.
The same idea works for anything you can multiply: numbers modulo \(m\) (reduce after every multiplication so nothing grows), matrices (which gives Fibonacci numbers in \(O(\log n)\)), even polynomials.
How it works¶
result = 1.- While \(n > 0\): if the lowest binary digit of \(n\) is 1,
result *= a. Thena *= aand \(n \leftarrow \lfloor n / 2 \rfloor\). - For \(a^n \bmod m\): take
% mafter each multiplication.
Code¶
def power(a, n):
"""a to the power n with O(log n) multiplications (square and multiply)."""
result = 1
while n > 0:
if n & 1: # this binary digit of n is 1: include the current square
result *= a
a *= a # a, a^2, a^4, a^8, ...
n >>= 1
return result
def power_mod(a, n, m):
"""a^n mod m, keeping every number below m."""
result, a = 1 % m, a % m
while n > 0:
if n & 1:
result = result * a % m
a = a * a % m
n >>= 1
return result
def mat_mult(X, Y):
return [[X[0][0] * Y[0][0] + X[0][1] * Y[1][0], X[0][0] * Y[0][1] + X[0][1] * Y[1][1]],
[X[1][0] * Y[0][0] + X[1][1] * Y[1][0], X[1][0] * Y[0][1] + X[1][1] * Y[1][1]]]
def fib(n):
"""The nth Fibonacci number from the matrix [[1, 1], [1, 0]] to the power n."""
result, M = [[1, 0], [0, 1]], [[1, 1], [1, 0]]
while n > 0:
if n & 1:
result = mat_mult(result, M)
M = mat_mult(M, M)
n >>= 1
return result[0][1]
if __name__ == "__main__":
print(power(2, 10), power(3, 13), power(7, 0))
MOD = 1_000_000_007
print(power_mod(2, 1_000_000, MOD), power_mod(3, MOD - 2, MOD) * 3 % MOD)
print(power_mod(5, 117, 19), fib(10), fib(90))
public class FastPower {
/** a to the power n with O(log n) multiplications (square and multiply). */
static long power(long a, long n) {
long result = 1;
while (n > 0) {
if ((n & 1) == 1) result *= a; // this binary digit of n is 1: include the current square
a *= a; // a, a^2, a^4, a^8, ...
n >>= 1;
}
return result;
}
/** a^n mod m, keeping every number below m. */
static long powerMod(long a, long n, long m) {
long result = 1 % m;
a %= m;
while (n > 0) {
if ((n & 1) == 1) result = result * a % m;
a = a * a % m;
n >>= 1;
}
return result;
}
static long[][] matMult(long[][] X, long[][] Y) {
return new long[][]{
{X[0][0] * Y[0][0] + X[0][1] * Y[1][0], X[0][0] * Y[0][1] + X[0][1] * Y[1][1]},
{X[1][0] * Y[0][0] + X[1][1] * Y[1][0], X[1][0] * Y[0][1] + X[1][1] * Y[1][1]}};
}
/** The nth Fibonacci number from the matrix [[1, 1], [1, 0]] to the power n. */
static long fib(int n) {
long[][] result = {{1, 0}, {0, 1}}, M = {{1, 1}, {1, 0}};
while (n > 0) {
if ((n & 1) == 1) result = matMult(result, M);
n >>= 1;
if (n > 0) M = matMult(M, M); // skip the last squaring, which would overflow
}
return result[0][1];
}
public static void main(String[] args) {
System.out.println(power(2, 10) + " " + power(3, 13) + " " + power(7, 0));
final long MOD = 1_000_000_007L;
System.out.println(powerMod(2, 1_000_000, MOD) + " " + powerMod(3, MOD - 2, MOD) * 3 % MOD);
System.out.println(powerMod(5, 117, 19) + " " + fib(10) + " " + fib(90));
}
}
#include <array>
#include <iostream>
using namespace std;
// a to the power n with O(log n) multiplications (square and multiply).
long long power(long long a, long long n) {
long long result = 1;
while (n > 0) {
if (n & 1) result *= a; // this binary digit of n is 1: include the current square
a *= a; // a, a^2, a^4, a^8, ...
n >>= 1;
}
return result;
}
// a^n mod m, keeping every number below m.
long long powerMod(long long a, long long n, long long m) {
long long result = 1 % m;
a %= m;
while (n > 0) {
if (n & 1) result = result * a % m;
a = a * a % m;
n >>= 1;
}
return result;
}
using Mat = array<array<long long, 2>, 2>;
Mat matMult(const Mat& X, const Mat& Y) {
return {{{X[0][0] * Y[0][0] + X[0][1] * Y[1][0], X[0][0] * Y[0][1] + X[0][1] * Y[1][1]},
{X[1][0] * Y[0][0] + X[1][1] * Y[1][0], X[1][0] * Y[0][1] + X[1][1] * Y[1][1]}}};
}
// The nth Fibonacci number from the matrix [[1, 1], [1, 0]] to the power n.
long long fib(int n) {
Mat result = {{{1, 0}, {0, 1}}}, M = {{{1, 1}, {1, 0}}};
while (n > 0) {
if (n & 1) result = matMult(result, M);
n >>= 1;
if (n > 0) M = matMult(M, M); // skip the last squaring, which would overflow
}
return result[0][1];
}
int main() {
cout << power(2, 10) << " " << power(3, 13) << " " << power(7, 0) << "\n";
const long long MOD = 1000000007LL;
cout << powerMod(2, 1000000, MOD) << " " << powerMod(3, MOD - 2, MOD) * 3 % MOD << "\n";
cout << powerMod(5, 117, 19) << " " << fib(10) << " " << fib(90) << "\n";
}
Output:
1024 1594323 1: \(2^{10}\), \(3^{13}\) and \(7^0\).235042059 1: \(2^{1{,}000{,}000} \bmod (10^9 + 7)\), a number with 301,030 digits reduced to 9 digits, in about 26 multiplications; then \(3 \cdot 3^{p-2} \bmod p = 1\), showing that \(3^{p-2}\) is the inverse of 3 modulo the prime \(p\).1 55 2880067194370816120: \(5^{117} \bmod 19 = 1\), and the Fibonacci numbers \(F(10)\) and \(F(90)\) from matrix powers.
When to use it¶
- Cryptography: RSA, Diffie–Hellman and digital signatures all compute \(a^n \bmod m\) with numbers of 600 or more digits; see cryptography.akadian.ca.
- Modular inverses modulo a prime: \(a^{-1} \equiv a^{p-2} \pmod p\) (Fermat).
- Primality tests such as Fermat and Miller–Rabin, which check \(a^{n-1} \bmod n\).
- Linear recurrences (Fibonacci and others) and counting paths of length \(n\) in a graph, through matrix powers.
- Hashing: the base powers in Rabin-Karp.
Common mistakes¶
- Overflow in
a * a % m: with \(m\) near \(10^9\) the product fits in 64 bits; with \(m\) near \(10^{18}\) it does not; use 128-bit multiplication (__int128in C++,Math.multiplyHighorBigIntegerin Java). 1 % m: for \(m = 1\) the answer is 0, not 1; starting withresult = 1 % mhandles it.- Negative exponents: they need an inverse, which exists only when \(\gcd(a, m) = 1\).
- Squaring once too many: without a modulus, the last
a *= acan overflow even though it is never used; the Java and C++fibskip it.
Going deeper¶
Each part below starts closed; open the ones you want.
Sample walkthrough: 3^13
\(13 = 1101_2\). Reading bits from the lowest:
| Bit | Value | Current square | Result |
|---|---|---|---|
| 0 | 1 | \(3^1 = 3\) | \(1 \cdot 3 = 3\) |
| 1 | 0 | \(3^2 = 9\) | 3 (skip) |
| 2 | 1 | \(3^4 = 81\) | \(3 \cdot 81 = 243\) |
| 3 | 1 | \(3^8 = 6561\) | \(243 \cdot 6561 = 1{,}594{,}323\) |
Modular example, \(5^{117} \bmod 19\): by Fermat's little theorem \(5^{18} \equiv 1\), and \(117 = 6 \cdot 18 + 9\), so it equals \(5^9 \bmod 19\). Squares: \(5^2 = 25 \equiv 6\), \(5^4 \equiv 36 \equiv 17\), \(5^8 \equiv 289 \equiv 4\); then \(5^9 \equiv 4 \cdot 5 = 20 \equiv 1\).
The math: the number of multiplications, and Fermat
Count. Let \(n\) have \(k = \lfloor \log_2 n \rfloor + 1\) binary digits, \(s\) of them equal to 1. The loop does \(k - 1\) useful squarings and \(s\) multiplications into the result: at most \(2 \lfloor \log_2 n \rfloor + 1\). For \(n = 1{,}000{,}000\) (20 binary digits, 7 of them 1) that is 19 squarings and 7 multiplications.
Invariant. At the start of each loop, \(\text{result} \cdot a^n\) equals the original \(a_0^{n_0}\). If \(n\) is even, \((a^2)^{n/2} = a^n\); if odd, \(\text{result} \cdot a \cdot (a^2)^{(n-1)/2} = \text{result} \cdot a^n\). When \(n = 0\), the result is the answer.
Not always optimal. The fewest multiplications for \(a^n\) is the length of the shortest addition chain for \(n\); for \(n = 15\), square-and-multiply uses 6 but 5 suffice (\(a^2, a^3, a^6, a^{12}, a^{15}\)). Finding the shortest chain is hard in general, and square-and-multiply is within a factor 2 of it.
Fermat's little theorem. If \(p\) is prime and \(p \nmid a\), then \(a^{p-1} \equiv 1 \pmod p\). So \(a \cdot a^{p-2} \equiv 1\): the inverse of \(a\) is \(a^{p-2} \bmod p\). Euler's generalisation: \(a^{\varphi(m)} \equiv 1 \pmod m\) when \(\gcd(a, m) = 1\).
Watch it step by step
Orange: the bit being read; red: the square that gets multiplied in; faded: a square that is skipped.
Variations and related ideas
- Left-to-right binary method: read bits from the top: square, then multiply by \(a\) if the bit is 1; the same count, and the multiplier is always the small \(a\).
- Sliding windows (precompute \(a^1, a^3, \dots, a^{2^w - 1}\)) use fewer multiplications for long exponents, such as those in RSA.
- Montgomery ladder: the same steps whatever the bits, so the time does not leak the secret exponent (a defence against timing attacks).
- Montgomery multiplication and Barrett reduction make each
% mfast for big numbers.
See also¶
- Euclid's GCD: inverses for any modulus
- Fibonacci: \(F(n)\) with matrix powers
- Rabin-Karp
References¶
- D. E. Knuth, The Art of Computer Programming, Vol. 2, 3rd ed., Section 4.6.3 (evaluation of powers).
- A. J. Menezes, P. C. van Oorschot and S. A. Vanstone, Handbook of Applied Cryptography, CRC Press, 1996, Chapter 14 (efficient implementation).