Fast Exponentiation
Introduction
Fast exponentiation, also known as binary exponentiation or exponentiation by squaring, is a technique for computing \(a^n\) in \(\Theta(\log n)\) time, whereas the naive approach requires \(\Theta(n)\) time.
This technique can be applied in any scenario where multiplication of \(a\) is associative, such as modular exponentiation, matrix exponentiation, etc. For more details, see the Applications section.
Process
Computing \(a\) raised to the \(n\)-th power means multiplying \(n\) copies of \(a\) together: \(a^{n} = \underbrace{a \times a \cdots \times a}_{n\text{ copies of a}}\). However, when \(n\) is too large or each multiplication is too expensive, this method becomes impractical. The idea of binary exponentiation is to break down the exponentiation task into smaller tasks based on the binary representation of the exponent.
Example
Suppose we want to compute \(3^{13}\). Expanding it as a product requires \(13-1=12\) multiplications. However, since
we can compute \(3^{13}\) with only \(2\) multiplications if we can quickly compute \(3^{1},3^{2},3^{4},3^{8}\). Thus, we only need a fast way to compute the sequence of \(3\) raised to powers of \(2^k\). This is easy because each element in the sequence (except the first) is the square of the previous element.
Based on this analysis, the computation of \(3^{13}\) proceeds as follows:
Only \(5\) multiplication operations were performed.
This is the basic idea of fast exponentiation. As for specific implementations, there are two common versions.
Iterative Version
Let the binary representation of \(n\) be \((n_tn_{t-1}\cdots n_1n_0)_2\), i.e.,
where \(n_i\in\{0,1\}\). Then we have
Note that only terms where \(n_i=1\) actually contribute to the product.
Based on this expression, we can first compute the \(\Theta(\log n)\) powers \(a^{2^k}\) in \(\Theta(\log n)\) time, then spend \(\Theta(\log n)\) time to multiply those corresponding to binary digits equal to \(1\) into the final result. This is the iterative version of fast exponentiation.
Pseudocode:
Using this method to compute fast exponentiation requires \(\Theta(\log n)\) multiplication operations.
Recursive Version
This process can also be implemented recursively. Note that the binary expansion of exponent \(n\) can be written recursively as
Therefore, the power \(a^n\) can be computed recursively as
This is the recursive version of fast exponentiation.
Pseudocode:
Using this method to compute fast exponentiation requires \(\Theta(\log n)\) recursive calls and also \(\Theta(\log n)\) multiplication operations. Although the complexity is the same, the iterative version is faster in practice due to the overhead of recursion.
Applications
Modular Exponentiation
Luogu P1226【Template】Fast Exponentiation
Given three integers \(a,b,p\), compute \(a^b\bmod p\), where \(p\ge 2\).
This is a very common application, for example, it can be used to compute modular multiplicative inverses. Since we know that the modulo operation does not interfere with multiplication, we only need to take modulo during the computation.
First, we can directly implement the recursive method described above:
Reference Implementation
1 2 3 4 5 6 7 8 | |
1 2 3 4 5 6 7 8 | |
The second implementation is non-recursive. During the loop, it accumulates the powers corresponding to binary digits that are 1 into the answer. Although the theoretical complexity of both is the same, the second one is faster in practice because recursion incurs some overhead.
Reference Implementation
1 2 3 4 5 6 7 8 9 | |
1 2 3 4 5 6 7 8 | |
Note
- The modulus is usually greater than \(1\). In very special cases, the modulus \(p\) may equal \(1\), in which case the situation \(b=0\) needs special consideration.
- When the exponent is very large, it is necessary to reduce the exponent using the Extended Euler's Theorem before computation.
Computing Fibonacci Numbers
According to the recurrence relation of the Fibonacci sequence \(F_n = F_{n-1} + F_{n-2}\), we can construct a \(2\times 2\) matrix to represent the transformation from \(F_i,F_{i+1}\) to \(F_{i+1},F_{i+2}\). Then, when computing the \(n\)-th power of this matrix, we use the idea of fast exponentiation to compute the result in \(\Theta(\log n)\) time. For more details, see Fibonacci Numbers. The implementation of matrix fast exponentiation can be found in Matrix Acceleration for Recurrences.
Multiple Permutations
Problem Description
Given a sequence of length \(n\) and a permutation, apply this permutation to the sequence \(k\) times.
Simply raise the permutation to the \(k\)-th power and apply it to the sequence. The time complexity is \(O(n \log k)\). For more details, see Composition of Permutations.
Note
Construct a graph from this permutation, then raise each cycle to the \(k\)-th power (which is equivalent to \(k\) modulo the cycle length) separately. This can solve the problem in \(O(n)\) time.
Accelerating Operations on Point Sets in Geometry
HDU 4087 A Letter to Programmers
Given \(n\) points \(p_i\) in three-dimensional space, apply \(m\) operations to these points. The operations include:
- Shift: Move points along a certain vector.
- Scale: Scale the coordinates of points by a certain factor.
- Rotate: Rotate points around a certain line.
There is also a special operation: repeat a sequence of operations \(k\) times (Repeat), and Repeat operations can be nested. Output the coordinates of each point after all operations.
Referring to Vectors and Matrices, each operation can be represented by a transformation matrix, and a series of consecutive transformations can be represented by the product of matrices. A Repeat operation is equivalent to raising a matrix to the \(k\)-th power. Thus, the final matrix representing the entire transformation sequence can be computed in \(O(m \log k)\) time. Finally, apply it to the \(n\) points, with total complexity \(O(n + m \log k)\).
Fixed-Length Path Counting
Problem Description
Given a directed graph (edge weights are 1), count the number of paths of length \(k\) from any vertex \(u\) to any vertex \(v\).
Raise the adjacency matrix \(M\) of the graph to the \(k\)-th power. Then \(M_{i,j}\) represents the number of paths of length \(k\) from \(i\) to \(j\). The complexity of this algorithm is \(O(n^3 \log k)\). For details of this algorithm, see the Matrix page.
Integer Multiplication Under Modulo
Problem Description
Given non-negative integers \(a,b\) and a positive integer \(m\), compute \(a\times b\bmod m\), where \(a,b\le m\le 10^{18}\).
Similar to the idea of binary exponentiation, this time we express one of the multiplicands as a sum of powers of 2. Because when multiplying a number by 2 and taking modulo, we can convert it to addition and subtraction to prevent integer overflow. This solves the problem in \(O (\log m)\) time. The recursive method is as follows:
However, in practice, this method introduces greater computational complexity and is not time-efficient. In actual programming, fast multiplication is often used for multiplication operations when the modulus range is within long long.
High-Precision Fast Exponentiation
Prerequisite skill: Big Integer Multiplication
Luogu P1045 [NOIP 2003 Popularization Group] Mersenne Number
Given an integer \(P\) (\(1000 < P < 3100000\)), compute the number of digits and the last \(500\) digits (in decimal representation) of \(2^P−1\), padding with leading zeros if fewer than \(500\) digits.
Code Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
Precomputed Fast Exponentiation with Fixed Base
When the base \(a\) is fixed, we can use the block decomposition idea to precompute for a certain amount of time and then answer each power query in \(O(1)\) time. This algorithm is also often called light-speed exponentiation. The process is as follows:
- Choose a number \(s\), precompute \(a^0,a^1,\cdots,a^{s-1}\) and \(a^0,a^s,\cdots,a^{\lfloor p/s\rfloor s}\) and store them in two arrays;
- For each query \(a^b\), split \(b\) into \(\lfloor b/s\rfloor s+(b\bmod s)\), then \(a^b=a^{\lfloor b/s\rfloor s}\cdot a^{b\bmod s}\), which can be answered in \(O(1)\).
Assuming the exponent \(b\) ranges in \([0,n]\), the block size \(s\) is often chosen as \(\sqrt{n}\) or a nearby power of \(2\). Choosing \(\sqrt{n}\) yields the optimal preprocessing complexity \(O(\sqrt{n})\), while choosing a power of \(2\) simplifies computation using bit operations.
In particular, for modular exponentiation, the same base \(a\) implies the same modulus \(m\). Due to the Extended Euler's Theorem, for any modulus \(m\), the upper bound for the exponent range in preprocessing is \(n = 2\varphi(m)\); for prime modulus \(p\), the upper bound is \(n = p - 1\). In both cases, the preprocessing complexity is \(O(\sqrt{m})\).
Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 | |
Exercises
- UVa 1230 - MODEX
- UVa 374 - Big Mod
- UVa 11029 - Leading and Trailing
- Codeforces - Parking Lot
- SPOJ - The last digit
- SPOJ - Locker
- SPOJ - Just add it
Parts of this page are translated from the blog post Бинарное возведение в степень and its English translation Binary Exponentiation. The Russian version is licensed under Public Domain + Leave a Link; the English version is licensed under CC-BY-SA 4.0.
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用