This page looks best with JavaScript enabled

Matrix Fast Pow

 ·   ·  โ˜• 5 min read  ·  ๐Ÿฆ‚ Kyle · ๐Ÿ‘€... views

Use matrix and fast pow together can make some problems much easier.

Fast Pow

old friend fast pow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
template <typename T>
T pow(T x, int n) {
  T ret = 1;
  while (n) {
    if (n & 1) ret *= x;
    x *= x;
    n >>= 1;
  }
  return ret;
}
Matrix<ll, 10, 10, MOD> ret = 1 will use constructor Matrix(T x, bool isMainDiagonal = true).

A Matrix Template

 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
template <typename T, std::size_t R, std::size_t C = R,
          std::size_t M = INT32_MAX>
class Matrix {
 public:
  T m[R][C];

  Matrix() { memset(m, 0, sizeof(m)); }
  /**
   * construct a matrix whose diagonal (fill at most min(R, C) number as x) is
   * filled with number x, and the rest filled with 0's
   * @param x number to be filled at the diagonal
   * @param isMainDiagonal fill main diagonal if true, else fill the
   * antidiagonal
   */
  Matrix(T x, bool isMainDiagonal = true) : Matrix() {
    if (isMainDiagonal)
      for (std::size_t i = 0; i < R && i < C; ++i) m[i][i] = x;
    else
      for (std::size_t i = 0, j = C - 1; i < R && j >= 0; --j, ++i) m[i][j] = x;
  }

  template <std::size_t C2>
  Matrix<T, R, C2, M> operator*(const Matrix<T, C, C2, M> &other) const {
    Matrix<T, R, C2, M> res;
    for (std::size_t i = 0; i < R; ++i)
      for (std::size_t k = 0; k < C; ++k)
        for (std::size_t j = 0; j < C2; ++j)
          res.m[i][j] = (res.m[i][j] + m[i][k] * other.m[k][j] % M) % M;
    return res;
  }

  Matrix<T, R, C, M> &operator*=(const Matrix<T, C, C, M> &other) {
    return *this = *this * other;
  }

  void fill(T x) {
    for (std::size_t i = 0; i < R; ++i)
      for (std::size_t j = 0; j < C; ++j) m[i][j] = x;
  }

  T sum() const {
    T res = 0;
    for (std::size_t i = 0; i < R; ++i)
      for (std::size_t j = 0; j < C; ++j) res = (res + m[i][j]) % M;
    return res;
  }
};

Examples

leetcode: 509 Fibonacci Number

link: 509 Fibonacci Number
time: $O(log(n))$, space: $O(1)$

description

$$
\begin{align*}
F(0) &= 0 \\
F(1) &= 1 \\
F(n) &= F(n - 1) + F(n - 2),\;for\; n > 1.
\end{align*}
$$
now given n, compute F(n);

idea

keep track of previous two numbers F(i-2), F(i-1), and update them at each increase of i, till we get to n.

the transition can be represented by a matrix, mulitply all transition matrixes together (use fast pow here) then transit once from initial state F(0) = 0, F(1) = 1.

solution

code on github: sky-bro/AC/leetcode.com/0509 Fibonacci Number/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution {
 public:
  int fib(int N) {
    Matrix<ll, 1, 2, MOD> res;
    res.m[0][1] = 1;
    if (N < 2) return N;
    Matrix<ll, 2, 2, MOD> x;  // transition matrix
    x.fill(1);
    x.m[0][0] = 0;
    return (res * pow(x, N - 1)).m[0][1];
  }
};

leetcode: 935 Knight Dialer

link: 935 Knight Dialer
time: $O(log(n))$, space: $O(1)$

description

1
2
3
4
5
6
/*
1 2 3
4 5 6
7 8 9
* 0 #
*/

how many different sequence of numbers can a chess knight dial (% 109 + 7). the knight can only stand on numeric cells, and may start from any digit.

idea

This is a simple dp problem, at every move, knight standing at 1 can go to 6 and 8 (or knight standing at 6 and 8 can go to 1), so dp[1] = dp[6] + dp[8], but we need to update all dp[0..9] same time (we don’t want to overwrite dp values in the last step), so the time and space complexity would be $O(n)$ and $O(1)$.

Another way to update dp values is by multiplying a transition matrix, and in every transition, this matrix is gonna be the same, so we just multiply all transitions together (where can use fast pow) and transit from the starting point (when n=1: [1,1,1,1,1,1,1,1,1,1]) only once.
The time complexity would be reduced to $O(log(n))$.

solution

code on github: sky-bro/AC/leetcode.com/0935 Knight Dialer/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution {
 public:
  int knightDialer(int n) {
    if (n == 1) return 10;
    Matrix<ll, 1, 10, MOD> res;
    res.fill(1);
    /*
    1 2 3
    4 5 6
    7 8 9
    * 0 #
    */
    Matrix<ll, 10, 10, MOD> x;
    vector<vector<int>> trans = { {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} };
    for (int i = 0; i < 10; ++i)
      for (int j : trans[i]) x.m[i][j] = 1;
    return (res * pow(x, n - 1)).sum();
  }
};

more for exercise

  • todo…

refs

Share on