矩阵快速幂

矩阵快速幂

1. 算法简介

矩阵快速幂是一种用于高效计算矩阵的 n 次幂的算法,其核心思想是基于 快速幂(前置芝士)
其时间复杂度为 O(log n),远快于朴素的 O(n)方法。

矩阵乘法满足结合律:(A×B)×C=A×(B×C) 这使得矩阵乘方可以用相同的“折半指数”的思路快速计算

矩阵快速幂常用于:

  • 斐波那契数列求第 n项
  • 动态规划转移优化
  • 图论(如状态转移)

2. 基础公式

给定一个矩阵 A,我们要求出:

\[A^n = \underbrace{A \times A \times \cdots \times A}_{n\text{ 次}} \]

矩阵乘法公式:

\[C_{i,j} = \sum_{k=1}^{r} A_{i,k} \cdot B_{k,j} \]


矩阵乘法演示

我们来看一个矩阵乘法的具体计算过程:

设有两个 矩阵:

\[A = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \quad B = \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix} \]

它们的乘积:

\[C = A \times B = \begin{bmatrix} 19 & 22 \\ 43 & 50 \end{bmatrix} \]


计算过程

\[每个元素 C_{i,j} 是 A 的第 i 行 与 B 的第 j 列 做点积得到的。 \]


第 1 行 × 第 1 列:

\[C_{1,1} = 1 \times 5 + 2 \times 7 = 5 + 14 = 19 \]


第 1 行 × 第 2 列:

\[C_{1,2} = 1 \times 6 + 2 \times 8 = 6 + 16 = 22 \]


第 2 行 × 第 1 列:

\[C_{2,1} = 3 \times 5 + 4 \times 7 = 15 + 28 = 43 \]


第 2 行 × 第 2 列:

\[C_{2,2} = 3 \times 6 + 4 \times 8 = 18 + 32 = 50 \]


最终结果矩阵:

\[A \times B = \begin{bmatrix} 19 & 22 \\ 43 & 50 \end{bmatrix} \]

构造例题(斐波那契数列)

目标

我们要求的是斐波那契数列第 f(n) 项。

斐波那契定义:

\[f(n) = f(n-1) + f(n-2) \]

我们希望使用 矩阵快速幂 来快速求解 f(n),将时间复杂度降为 O(log n)。


1. 构造基本转移矩阵

从斐波那契递推式中,我们可以构造如下矩阵关系:

\[\begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \times \begin{bmatrix} f(n-1) \\ f(n-2) \end{bmatrix} \]

这就是核心的状态转移矩阵(记作 B):

\[B = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \]


2. 迭代构造过程(推理)

根据上面的定义,我们令:

\[C(n) = \begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix} \]

由于:

\[C(n) = B \times C(n-1) \]

我们可以不断迭代推理:

\[\begin{align*} C(n) &= B \cdot C(n-1) \\ &= B \cdot (B \cdot C(n-2)) \\ &= B^2 \cdot C(n-2) \\ &= B^3 \cdot C(n-3) \\ &\cdots \\ &= B^{n-1} \cdot C(1) \end{align*} \]

而初始向量为:

\[C(1) = \begin{bmatrix} f(1) \\ f(0) \end{bmatrix} = \begin{bmatrix} 1 \\ 0 \end{bmatrix} \]


3. 最终公式总结

\[C(n) = B^{n-1} \cdot \begin{bmatrix} f(1) \\ f(0) \end{bmatrix} \]

因此,我们只需要快速计算矩阵 B 的 n - 1 次幂即可!

Code

void solve() {
  i64 n;
  cin >> n;
  if (n <= 2) {
    cout << 1 << '\n';
    return;
  }
  Matrix T(2);
  T.mat[0][0] = 1;
  T.mat[0][1] = 1;
  T.mat[1][0] = 1;
  T.mat[1][1] = 0;
  Matrix V(2);
  V.mat[0][0] = 1;
  V.mat[1][0] = 0;
  V.mat[0][1] = 0;
  V.mat[1][1] = 0;
  Matrix ans = T.power(n - 1) * V;
  cout << (ans.mat[0][0] + MOD) % MOD << '\n';
}

Template

const i64 MOD = 1e9 + 7;
class Matrix {
 public:
  vector<vector<i64>> mat;

  Matrix(int N) { mat.resize(N, vector<i64>(N, 0)); }

  // 输出矩阵
  void print() {
    for (i64 i = 0; i < mat.size(); i++) {
      for (i64 j = 0; j < mat[i].size(); j++) {
        cout << mat[i][j] << " ";
      }
      cout << endl;
    }
  }

  static Matrix identity(int N) {
    Matrix res(N);
    for (i64 i = 0; i < N; i++) {
      res.mat[i][i] = 1;
    }
    return res;
  }

  // 矩阵加法
  Matrix operator+(const Matrix &other) const {
    int N = mat.size();
    Matrix res(N);
    for (i64 i = 0; i < N; i++) {
      for (i64 j = 0; j < N; j++) {
        res.mat[i][j] = (mat[i][j] + other.mat[i][j] + MOD) % MOD;
      }
    }
    return res;
  }

  // 矩阵减法
  Matrix operator-(const Matrix &other) const {
    int N = mat.size();
    Matrix res(N);
    for (i64 i = 0; i < N; i++) {
      for (i64 j = 0; j < N; j++) {
        res.mat[i][j] = (mat[i][j] - other.mat[i][j] + MOD) % MOD;
      }
    }
    return res;
  }

  // 矩阵乘法
  Matrix operator*(const Matrix &other) const {
    int N = mat.size();
    Matrix res(N);
    for (i64 i = 0; i < N; i++) {
      for (i64 j = 0; j < N; j++) {
        res.mat[i][j] = 0;
        for (i64 k = 0; k < N; k++) {
          res.mat[i][j] =
              (res.mat[i][j] + mat[i][k] * other.mat[k][j] + MOD) % MOD;
        }
      }
    }
    return res;
  }

  // 矩阵与数相乘
  Matrix operator*(const i64 &x) const {
    int N = mat.size();
    Matrix res(N);
    for (i64 i = 0; i < N; i++) {
      for (i64 j = 0; j < N; j++) {
        res.mat[i][j] = mat[i][j] * x % MOD;
      }
    }
    return res;
  }
  Matrix power(i64 exp) const {
    int N = mat.size();
    Matrix res = identity(N);
    Matrix base = *this;
    while (exp) {
      if (exp & 1) res = res * base;
      base = base * base;
      exp >>= 1;
    }
    return res;
  }

  // 矩阵转置
  Matrix transpose() const {
    int N = mat.size();
    Matrix res(N);
    for (i64 i = 0; i < N; i++) {
      for (i64 j = 0; j < N; j++) {
        res.mat[j][i] = mat[i][j];
      }
    }
    return res;
  }

  bool invert(Matrix &out) const {
    int N = mat.size();
    Matrix res = *this;
    out = identity(N);
    for (i64 i = 0; i < N; i++) {
      i64 j = i;
      while (j < N && res.mat[j][i] == 0) j++;
      if (j == N) return false;  // 如果某列没有非零元素,矩阵不可逆

      if (i != j) {
        swap(res.mat[i], res.mat[j]);
        swap(out.mat[i], out.mat[j]);
      }
      i64 inv = modInverse(res.mat[i][i], MOD);
      for (i64 k = 0; k < N; k++) {
        res.mat[i][k] = res.mat[i][k] * inv % MOD;
        out.mat[i][k] = out.mat[i][k] * inv % MOD;
      }
      for (i64 j = 0; j < N; j++) {
        if (j == i) continue;
        i64 factor = res.mat[j][i];
        for (i64 k = 0; k < N; k++) {
          res.mat[j][k] =
              (res.mat[j][k] - res.mat[i][k] * factor % MOD + MOD) % MOD;
          out.mat[j][k] =
              (out.mat[j][k] - out.mat[i][k] * factor % MOD + MOD) % MOD;
        }
      }
    }
    return true;
  }

  // 求逆元
  i64 modInverse(i64 a, i64 m) const {
    i64 x, y;
    exgcd(a, m, x, y);
    return (x % m + m) % m;
  }

  static i64 exgcd(i64 a, i64 b, i64 &x, i64 &y) {
    if (b == 0) {
      x = 1;
      y = 0;
      return a;
    }
    i64 g = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return g;
  }
};

其他例题

模板·矩阵快速幂 (P3390)

HDU 1757 - A Simple Math Problem

Parking Lot

posted @ 2025-08-07 20:08  orzzzzz  阅读(36)  评论(0)    收藏  举报