Fast Exponentiation
Compute x^n in O(log n) multiplications by squaring x and multiplying it in wherever the binary expansion of n has a 1 bit.
Overview
Binary exponentiation computes x^n with O(log n) multiplications instead of n - 1. Write n in binary; x^13 = x^8 · x^4 · x^1 because 13 = 1101b. Square x repeatedly to get x, x², x⁴, x⁸ and multiply together the ones whose bit is set.
With every multiplication reduced modulo m, the same loop gives modular exponentiation x^n mod m without ever forming a huge number — the core of Modular Inverse by Fermat, Miller–Rabin primality, RSA, and hashing. Python's pow(x, n, m) does exactly this.
Since the loop only uses associativity, it works for any associative operation: matrix exponentiation raises the 2×2 matrix [[1,1],[1,0]] to the n-th power to get the n-th Fibonacci number in O(log n), and in general computes the n-th term of any linear recurrence of order k in O(k³ log n). It also handles string repetition, permutation powers, and function composition.
Intuition
A mental model before the formal terms.
To fold a paper 13 times you would not fold it 13 separate times from one sheet; you notice that after doubling three times you have 8 layers. Fast exponentiation is repeated doubling: keep a "current power" that squares each step (x → x² → x⁴ → x⁸), and only "cash in" the powers you actually need — those matching 1-bits of n. Every step halves n, so 13 needs 4 steps rather than 12 multiplications.
How it works
- Initialize
result = 1,base = x mod m. - While
n > 0: ifnis odd (n & 1),result = result · base mod m. Thenbase = base · base mod mandn = n >> 1. - Return
result. The loop runs⌊log₂ n⌋ + 1times, each with at most two multiplications. - Matrix form: same loop, but
resultstarts as the identity matrix and·is matrix multiplication. For Fibonacci,M^n = [[F(n+1), F(n)], [F(n), F(n-1)]]. - Negative exponents:
x^-n = (x^-1)^n; for floats invertx, for modular arithmetic use the modular inverse.
Why it works
Invariant: at every iteration result · base^n equals the original x^n_original. Squaring base and halving n preserves it when n is even; when n is odd, moving one factor of base into result first makes n - 1 even. When n reaches 0, result holds the answer.
Modular reduction after every multiplication is valid because (a · b) mod m = ((a mod m) · (b mod m)) mod m — see Modular Arithmetic. Intermediates stay below m², which fits in 64 bits when m < 2^31.5 (e.g. 10^9 + 7).
Matrix version: [F(n+1), F(n)]^T = M · [F(n), F(n-1)]^T, so by induction [F(n+1), F(n)]^T = M^n · [F(1), F(0)]^T. Matrix multiplication is associative, so the same squaring scheme applies.
Recognition
How to tell a problem wants this.
- "Compute
x^n" withnup to10^9or10^18, especially "modulo10^9 + 7". - The
n-th term of a linear recurrence for astronomically largen— Fibonacci withn = 10^18, tiling counts, number of walks of lengthnin a graph (adjacency matrix power). - Any associative operation that must be applied
ntimes: apply a permutationktimes, repeat a string transformation.
Interactive visualization
Play, step, change the input. ← → and space work too.
| e | e (binary) | e & 1 | base | result |
|---|---|---|---|---|
| 13 | 1101 | · | 3 | 1 |
1result = 1; base = base mod M2while e > 0:3 if e & 1: result = result · base mod M # this bit is set4 base = base · base mod M5 e = e >> 16return resultPseudocode
1power(x, n, m):2 result = 1, base = x mod m3 while n > 0:4 if n & 1: result = result * base mod m5 base = base * base mod m6 n = n >> 17 return result8fib(n): return matpow([[1,1],[1,0]], n)[0][1]Implementations
1# Exponentiation by squaring: x^n needs O(log n) multiplications, not n.2# Read the exponent in binary; each 1-bit contributes the current square.3 4 51 · Iterative binary exponentiation, modular to keep values bounded6def pow_mod(base: int, exp: int, mod: int) -> int:7 result = 1 % mod8 base %= mod9 while exp > 0:10 if exp & 1:11 result = result * base % mod12 base = base * base % mod13 exp >>= 114 return result15 16 172 · The same recurrence written recursively: x^n = (x^(n/2))^2 * x^(n&1)18def pow_mod_rec(base: int, exp: int, mod: int) -> int:19 if exp == 0:20 return 1 % mod21 half = pow_mod_rec(base, exp // 2, mod)22 sq = half * half % mod23 return sq * (base % mod) % mod if exp & 1 else sq24 25 263 · The trick is not about numbers: any associative operation works27def mat_mul(a: list[list[int]], b: list[list[int]], mod: int) -> list[list[int]]:28 n = len(a)29 c = [[0] * n for _ in range(n)]30 for i in range(n):31 for k in range(n):32 if a[i][k] == 0:33 continue34 aik = a[i][k]35 row_b = b[k]36 row_c = c[i]37 for j in range(n):38 row_c[j] = (row_c[j] + aik * row_b[j]) % mod39 return c40 41 424 · Matrix power gives the nth Fibonacci number in O(log n)43def fibonacci(n: int, mod: int) -> int:44 if n == 0:45 return 046 result = [[1, 0], [0, 1]] # identity47 base = [[1, 1], [1, 0]]48 e = n49 while e > 0:50 if e & 1:51 result = mat_mul(result, base, mod)52 base = mat_mul(base, base, mod)53 e >>= 1545 · result is base^n, and its top-right entry is F(n)55 return result[0][1]- Python integers are arbitrary precision, so
base * base % modis exact with no cast, no__int128, and noBigInt— this is the shortest of the four versions for a reason. base %= modneeds no negative guard, because Python%always returns a result with the sign of the *divisor*, so a negative base normalises automatically.result = 1 % modhandlesmod == 1correctly.mat_mulhoistsa[i][k],b[k]andc[i]into locals inside the loops, which removes repeated indexing from the innermost loop — the standard CPython optimisation.- The
ikjloop order (rather thanijk) makes the inner loop a straight scan overb[k]andc[i], which is both faster and what allows thea[i][k] == 0skip.
The built-in pow(base, exp, mod) does exactly this in C and should always be preferred over the hand-written loop.
- The three-argument
pow(base, exp, mod)is built in, implemented in C, and since Python 3.8 also handles a negative exponent as a modular inverse. - Python
%returns a non-negative result for a positive modulus, unlike C++ and JavaScript — the negative-base normalisation is free. - Arbitrary-precision integers mean
2 ** 1000is exact; the only cost is that each operation scales with the number of digits. math.isqrt,math.combandpow(..., -1, m)cover most of this topic's neighbours in the standard library.
- Hand-writing the loop when
pow(base, exp, mod)is available, C-implemented, and faster. - Writing
base ** exp % mod, which computes the full unreduced power first — for a large exponent that exhausts memory rather than being merely slow. - Assuming
%behaves like C++ and adding a redundant (harmless but confusing) negative-base guard.
- Wide arithmetic is the whole story: Python integers are unbounded, JS/TS must use
BigInt(with a compile-time mixing check in TypeScript and a runtimeTypeErrorin JavaScript), and C++ needs the non-standard__int128extension for 64-bit modular multiplication. - Modulo sign: Python
%follows the divisor and is always non-negative for a positive modulus; C++ and JS/TS follow the dividend, so both need the((x % m) + m) % mnormalisation. - Only Python has this built in — three-argument
pow(base, exp, mod)— and it is faster than any hand-written loop. - The naive
base ** exp % modis a trap in every language, but it fails differently: an unbounded allocation in Python and JS/TS BigInt, and silent floating-point rounding withstd::poworMath.pow.
Complexity
Multiplications, each O(1) for fixed-width modular values. Matrix version is O(k^3 log n) for a k×k matrix. Recursive form uses O(log n) stack.
Compare growth rates in the Complexity Explorer →When to use — and when not to
- Powers with exponents beyond a few dozen, especially modular powers with
nup to10^18. - Modular inverse modulo a prime via
a^(p-2)— see Modular Inverse. - Linear recurrences (Fibonacci, tilings, counting walks) at huge
nvia matrix powers;n-fold application of a permutation or function.
- Tiny exponents where a loop or
x * x * xis clearer and just as fast. - Floating-point powers needing high precision — repeated squaring compounds rounding error; use
exp(n · log x)or a librarypow. - Recurrences where
nis small enough forO(n)DP — the matrix approach adds constant factors and code.
Alternatives
Common mistakes
- Forgetting to reduce
basemodulombefore the loop, or reducingresultbut notbase(or vice versa). - JavaScript:
result * basewith values near10^9exceeds2^53and loses precision — useBigInt(or split multiplication).Math.powwith a modulus is also wrong for large exponents. - Java/C++/Go:
base * baseoverflowsint64whenm > ~3·10^9; use 128-bit multiplication or a different approach. - Negative
nhandled as an infinite loop in the integer version; handle the sign first. - Matrix exponentiation off-by-one:
M^n[0][1] = F(n), andn = 0must be special-cased if the identity matrix is not used.
Interview patterns
- Pow(x, n) with negative exponents and
n = INT_MINedge case (negate as alongfirst). - Fibonacci / climbing stairs / tiling for
nup to10^18mod10^9 + 7. - Count the number of length-
nwalks between two vertices:A^nwhereAis the adjacency matrix. - Super Pow:
a^b mod 1337wherebis given as a digit array — process digit by digit withresult = result^10 · a^digit.
- Where does O(n log n) come from?Beginner
- Average case versus worst caseIntermediate
- Kth Largest Element in an ArrayIntermediate