Matrix Exponentiation
Overview
First and foremost, it is highly recommended to go through Binary Exponentiation, if you are not acquainted with it or don't remember it clearly. It is expected that reader knows Multiplication of two matrices.
Matrix exponentiation is very useful in solving problems that involve finding term of a linear recurrence with constant coefficients in order of .
Matrix exponentiation is all about finding a matrix such that where and are two matrices of appropriate order. Before we delve deeper let's us recall two things:
- Matrix Multiplication is associative ,
- A matrix of order can be multiplied with another matrix of order only.
Before we go any further, let us solve a problem. Find a matrix such that = , where
Answer
Verify the following yourself:
If you are struggling with matrix multiplication remember that comes from sum and product of row of and column of .
Find a matrix such that , where
Answer
Verify it yourself!
Let us now see a concrete problem that can be solved using Matrix Exponentiation.
Example #1: 509. Fibonacci Number
We can solve the problem using top-down dynamic programming or by bottom-up dynamic programming, but let us see how we can solve it using Matrix Exponentiation first. The recurrence for fibonacci series is:
Let us try to find a matrix such that
Finding T
We can rewrite the above equation as:
or even simpler by putting and
It is now easy to see that
Using equation and we get,
From above pattern, we can conclude that,
But this solution is not better than dynamic programming solution which runs in time, infact our matrix exponentiation runs in time. The extra factor of comes from matrix multiplication (Matrix multiplication of two matrix takes time). To optimise our solution further we can use binary exponentiation to calculate in time, where is the dimension of matrix. In this case .
Let us see the code for the above idea. Our code can be divided into three broad functions:
S.No. | Function Name | Parameter Description | What does the function do? |
---|---|---|---|
1. | multiply(A, B) | Matrix (2D array/list) | Returns a new matrix |
2. | power(A, exp) | A Square Matrix (2D array/list), integer | Returns a new matrix in time, where is order of matrix |
3. | fib(n) | integer | Returns term of fibonacci series |
Let us implement them one by one. First one is multiply(A, B)
.
- C++
vector<vector<int>> multiply(const vector<vector<int>> A,
const vector<vector<int>> B) {
int n1 = size(A), m1 = size(A[0]);
int n2 = size(B), m2 = size(B[0]);
// If m1 != n2, program will give runtime error
assert(m1 == n2);
vector<vector<int>> C(n1, vector<int>(m2));
for (int i = 0; i < n1; i++) {
for (int j = 0; j < m2; j++) {
for (int k = 0; k < m1; k++) C[i][j] += A[i][k] * B[k][j];
}
}
return C;
}
- C++
vector<vector<int>> power(vector<vector<int>> A, int exp) {
int n = size(A), m = size(A[0]);
assert(n == m);
// We need to start with identity matrix because (A^0 = Identity Matrix)
vector res(n, vector<int>(n));
for (int i = 0; i < n; i++) res[i][i] = 1;
while (exp > 0) {
if (exp & 1) res = multiply(res, A);
A = multiply(A, A);
exp >>= 1;
}
return res;
}
- C++
int fib(int n) {
vector<vector<int>> T {{1, 1}, {1, 0}};
vector<vector<int>> A {{1}, {0}};
/*
T^n * A = B = {{F(1 + n)},
{F(0 + n)}}
*/
auto B = multiply(power(T, n), A);
return B[1][0];
}
In the given problem it was guaranteed that answer would fit in signed integer range. But if instead of exact number, the problem asked for where then we will have to modify our multiply
function so it performs modulo operation while multiplying the numbers.
The only change is in the block inside innermost . The following line:
C[i][j] += A[i][k] * B[k][j];
changes to
// Multiplying A[i][k] with 1LL converts it into long long type,
// which prevents overflow during multiplication.
// Max Value of A[i][k] = P - 1 and max value of B[k][j] = P - 1. Both of them fit in integer range
// But there product doesn't fit in the integer range before we perform modulo operation.
C[i][j] += (1LL * A[i][k] * B[k][j]) % P;
C[i][j] %= P;
- Time Complexity:
- Space Complexity: as we only made matrices of constant dimension.
Example #2: 1220. Count Vowels Permutation
Let us try to solve this problem using dynamic programming.
, here
If we know the current character then we know which characters can come after it. For example, if our string ends at character then we know that the next character is bound to be . Simiarly, if our string ends at character then the next character must be either or
We try to see the constraints in reverse, i.e., if we want our string of length to have as its ending character then what all possible string of length are there which can be extended. Our final answer will be .
Time Complexity of above solution is , which can be reduced to by using matrix exponentiation. Let us see how we can do so. First let us form the recurrence.
One can easily verify the recurrence. For example, if we want our string of length to end at character then we can only extend strings which ended at character or and which have length .
Now, we have a linear recurrence. For Let us try to find the matrix such that
Finding T
If you are unable to see how we came up with matrix , then for simplicity let us write as only . Now looks as shown below:
Our multiply
function has one change that was mentioned at the end of last problem (taking modulo at each operation) and the power
function remains unchanged.
- C++
const int MOD = 1e9 + 7;
int countVowelPermutation(int n) {
vector T(5, vector<int>(5, 0));
T[0][1] = T[0][2] = T[0][4] = 1;
T[1][0] = T[1][2] = 1;
T[2][1] = T[2][3] = 1;
T[3][2] = 1;
T[4][2] = T[4][3] = 1;
// There is 1 way to make string of length-1 whose ending character is fixed.
vector A(5, vector(1, 1));
// Since our base case is string of length-1 so we need T^(n - 1) only to get answer for string of length n = (1 + (n - 1))
auto B = multiply(power(T, n - 1), A);
int ans = 0;
for (int i = 0; i < size(B); i++) {
ans = (ans + B[i][0]) % MOD;
}
return ans;
}
- Time Complexity:
- Space Complexity: as we have created 2D vector of constant dimension only.
General Advice:
- You can use matrix exponentiation only if the recurrence is linear and has constant coefficients.
- Matrix exponentiation can often be used as an optimisation for dynamic programming solutions. Again check if the recurrence formed is linear with constant coefficients or not!.
Suggested Problems
Problem Name | Difficulty | Solution Link |
---|---|---|
1137. N-th Tribonacci Number | Easy | View Solutions |