Let’s say you are given two numbers \(a\), \(n\) and you have to compute \(a^n\).
Algorithm
Naive Approach: \(O(n)\)
The easiest approach to do this, if one knows how to use “for” loops or implement it (if it is not implemented already) in any given programming language is just a single \(O(n)\) iteration. Below is a sample implementation in C++.
1
2
3
4
5
6
7
8
9
10
11
12
| int a, n;
// a, n can be initialized either by taking input or
// by assigning hardcoded values a = 3, n = 4 let's say
// expo initialized to 1 as it is the multiplicative identity
int expo = 1;
for (int i = 0; i < n; ++i) {
expo = expo * a;
}
// now expo == a^n : true
|
Note
Here, the data type is “int”, assuming the value of \(expo = a^n\) comes under the constraint of “int”. So, any other data type can be used as per requirements.
Caveats
- It can be seen that this approach would time out if \(n > 10^8\).
- If the value of \(a^n\) is very large i.e. it can not be stored in “int” or any other provided data type, it will overflow.
Possible Solutions
- Algorithm of lesser complexity can be utilized in this case – we will see \(O(\log n)\) algorithm next.
- This is the reason why most of the time \((a^n)%(some\ number)\) is to be calculated. Most of the time that “some number” is \(10^9 + 7\) (which is a prime) in competitive programming problems.
Binary Exponentiation Approach: \(O(log n)\)
For achieving \(O(\log n)\) complexity, the mathematical fact that any number (in decimal) can be represented uniquely in binary can be utilized.
For example,
\(12 = 1 \times 8 + 1 \times 4 + 0 \times 2 + 0 \times 1 = 1100_{2}\)
\(15 = 1 \times 8 + 1 \times 4 + 1 \times 2 + 1 \times 1 = 1111_{2}\)
Now, also using the fact that \(a^{m + n} = a^m \times a^n\), we can calculate the values of \(a^1, a^2, a^4,\) and so on …
1
2
3
4
5
6
7
| int expo = a;
for (int i = 0; i < n; ++i) {
expo = (expo*expo);
}
// it's like
// expo: a -> a^2 -> a^4 -> a^8 -> a^16 ...
// i.e. with ith step the value will be a^(2*i)
|
Now, let’s say we need to calculate \(a^n\), then there will exist a unique representation of \(n\) in binary, whose \(i_{th}\) bit can be checked if it is set or not by using a simple boolean expression involving bitwise operator
1
| // (n >> i) & 1 == ith bit of n
|
for the proof for this refer to Link
now, it can be seen that \(a^n = a^{1 \times b_1} \times a^{2 \times b_2} \times a^{4 \times b_3} \times ...\) and we can find if the \(a^{2 \times i}\) have to multiply or not by using the fact we saw above. So, by a simple modification to the previous code we can calculate a^n.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| //init a, n
int expo = a;
// answer initialized to 1 as it is multiplicative identity
int answer = 1;
for (int i = 0; i < NUMBER_OF_BITS_IN_N; ++i) {
if((n >> i) & 1) {
// we check if the ith bit is set or not
// if it is set then, we multiply expo = a^(2*i)
answer = answer*expo;
}
expo = (expo*expo);
}
// answer now have the value a^n
|
See, there is only one \(O(\text{NUMBER_OF_BITS_IN_N})\) for loop, and it is easy to see that the number of bits in \(n = \log_{2}(n)\).
Hence, the overall complexity = \(O(\log{n})\)
If you are not sure of the number of bits in n, then just simply take MAXIMUM_POSSIBLE_NUMBER_OF_BITS instead which can be ~32 for the int datatype.
Modular Binary Exponentiation
Considering the second caveat described above, there can be cases where we need to find \(a^n % (some value)\) – note that % is the remainder operator (as used in C++).
For this, just an easy modification of the code will work,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| //init a, n, modvalue
long long expo = a;
// answer initialized to 1 as it is multiplicative identity
long long answer = 1;
for (int i = 0; i < NUMBER_OF_BITS_IN_N; ++i) {
if((n >> i) & 1) {
// we check if the ith bit is set or not
// if it is set then, we multiply expo = a^(2*i)
answer = (answer*expo) % modvalue;
}
expo = (expo*expo) % modvalue;
}
// answer now have the value (a^n)% modevalue
|
Conclusion
So, as we saw the binary exponentiation algorithm, it can be used for exponentiating a matrix too, just by changing “*” operator with implemented matrix multiplication and taking remainder with each number in the matrix.
Further Directions
For reading more about binary exponentiation and solving some related problems to get a grasp refer to CP-Algorithms Binary-Exp