矩阵快速幂:将递推关系转化为矩阵乘法,时间复杂度优化至 O(log n)

这是一道经典的动态规划问题,其核心思路与斐波那契数列类似。

问题分析:

假设跳上第 n 级台阶的跳法总数为 f(n)

  • 基础情况
    • n = 1,只有1种跳法(跳1级),即 f(1) = 1
    • n = 2,有2种跳法(1+1或直接跳2级),即 f(2) = 2
  • 递推关系
    跳上第 n 级台阶的最后一步只有两种可能:
    1. 从第 n-1 级台阶跳1级;
    2. 从第 n-2 级台阶跳2级。
      因此,f(n) = f(n-1) + f(n-2)(与斐波那契数列递推公式一致)。

算法实现:

1. 递归法(时间复杂度高,不推荐)

直接按递推公式递归计算,但会产生大量重复计算(如 f(n-2) 被计算多次)。

  • 时间复杂度:O(2ⁿ)(指数级,效率极低)。
2. 动态规划(推荐)

用数组或变量存储中间结果,避免重复计算。

  • 优化空间的动态规划
    只需记录前两项 f(n-1)f(n-2),无需存储整个数组。

    python

    
    
    def climb_stairs(n):
    if n <= 2:
    return n
    a, b = 1, 2 # f(1)=1, f(2)=2
    for _ in range(3, n+1):
    a, b = b, a + b # 滚动更新前两项
    return b
    • 时间复杂度:O(n)(线性级,遍历一次即可)。
    • 空间复杂度:O(1)(仅用常数空间)。
3. 矩阵快速幂或通项公式(进阶优化)
  • 矩阵快速幂:将递推关系转化为矩阵乘法,时间复杂度优化至 O(log n)。
  • 通项公式:利用斐波那契数列的数学公式直接计算,时间复杂度 O(1),但实际中因浮点数精度问题较少使用。

总结:

  • 总跳法数量:符合斐波那契数列规律,即 f(n) 等于第 n+1 个斐波那契数(以 f(1)=1, f(2)=1 为基准的斐波那契数列)。
  • 最优算法:动态规划(O(n) 时间 + O(1) 空间),适合面试中快速实现;若追求极致效率,可使用矩阵快速幂(O(log n))。

这类问题考察对递归、动态规划的理解,以及优化时间/空间复杂度的能力,在算法面试中非常常见。

矩阵快速幂是一种通过矩阵乘法和快速幂技巧优化线性递推问题时间复杂度的方法,尤其适用于斐波那契数列、台阶跳法等具有线性递推关系的场景。以下是具体实现步骤和代码示例:

一、核心原理

对于台阶跳法问题,递推公式为 f(n) = f(n-1) + f(n-2),可转化为矩阵乘法形式:

[f(n)f(n−1)]=[1110]×[f(n−1)f(n−2)] \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} [f(n)f(n−1)​]=[11​10​]×[f(n−1)f(n−2)​]

记系数矩阵为 M=[1110] M = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} M=[11​10​],则有:

[f(n)f(n−1)]=Mn−2×[f(2)f(1)] \begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix} = M^{n-2} \times \begin{bmatrix} f(2) \\ f(1) \end{bmatrix} [f(n)f(n−1)​]=Mn−2×[f(2)f(1)​]

因此,计算 f(n) f(n) f(n) 等价于计算 Mn−2 M^{n-2} Mn−2,再与初始向量 [f(2)f(1)]=[21] \begin{bmatrix} f(2) \\ f(1) \end{bmatrix} = \begin{bmatrix} 2 \\ 1 \end{bmatrix} [f(2)f(1)​]=[21​] 相乘。

二、矩阵快速幂实现步骤

1. 矩阵乘法

定义两个 2x2 矩阵的乘法规则:

[abcd]×[efgh]=[ae+bgaf+bhce+dgcf+dh] \begin{bmatrix} a & b \\ c & d \end{bmatrix} \times \begin{bmatrix} e & f \\ g & h \end{bmatrix} = \begin{bmatrix} ae+bg & af+bh \\ ce+dg & cf+dh \end{bmatrix} [ac​bd​]×[eg​fh​]=[ae+bgce+dg​af+bhcf+dh​]

2. 快速幂(二进制 exponentiation)

通过将指数 k k k 拆分为二进制(如 k=5=1012=4+1 k = 5 = 101_2 = 4 + 1 k=5=1012​=4+1),减少乘法次数:

  • 若 k k k 为偶数:Mk=(Mk/2)2 M^k = (M^{k/2})^2 Mk=(Mk/2)2
  • 若 k k k 为奇数:Mk=M×(M(k−1)/2)2 M^k = M \times (M^{(k-1)/2})^2 Mk=M×(M(k−1)/2)2
3. 矩阵与向量乘法

用计算得到的 Mn−2 M^{n-2} Mn−2 乘以初始向量 [21] \begin{bmatrix} 2 \\ 1 \end{bmatrix} [21​],结果的第一个元素即为 f(n) f(n) f(n)。

三、代码实现(Python)

python


def matrix_mult(a, b):
"""2x2矩阵乘法:a * b"""
return [
[a[0][0]*b[0][0] + a[0][1]*b[1][0], # 第1行第1列
a[0][0]*b[0][1] + a[0][1]*b[1][1]], # 第1行第2列
[a[1][0]*b[0][0] + a[1][1]*b[1][0], # 第2行第1列
a[1][0]*b[0][1] + a[1][1]*b[1][1]] # 第2行第2列
]
def matrix_pow(mat, power):
"""矩阵快速幂:计算 mat^power"""
# 初始化为单位矩阵(类比数字乘法中的1)
result = [[1, 0], [0, 1]]
while power > 0:
if power % 2 == 1:
result = matrix_mult(result, mat) # 若当前位为1,乘入结果
mat = matrix_mult(mat, mat) # 矩阵平方(对应二进制右移一位)
power //= 2
return result
def climb_stairs_matrix(n):
"""用矩阵快速幂计算台阶跳法"""
if n <= 2:
return n
# 系数矩阵 M
M = [[1, 1], [1, 0]]
# 计算 M^(n-2)
M_pow = matrix_pow(M, n-2)
# 初始向量 [f(2), f(1)] = [2, 1],结果为 M_pow * [2, 1]^T 的第一个元素
return M_pow[0][0] * 2 + M_pow[0][1] * 1

四、时间复杂度分析

  • 矩阵乘法:每次 2x2 矩阵乘法耗时 O(1) O(1) O(1)(固定 4 次乘法和 4 次加法)。
  • 快速幂迭代次数:指数 n−2 n-2 n−2 的二进制位数为 O(log⁡n) O(\log n) O(logn),因此矩阵乘法执行 O(log⁡n) O(\log n) O(logn) 次。
  • 总时间复杂度:O(log⁡n) O(\log n) O(logn),远优于递归法的 O(2n) O(2^n) O(2n) 和普通动态规划的 O(n) O(n) O(n)。

五、适用场景

矩阵快速幂适用于 递推关系固定且指数较大 的场景(如 n n n 高达 109 10^9 109 时),通过将线性递推转化为矩阵幂运算,实现对数级时间复杂度优化。

示例验证

  • 当 n=3 n=3 n=3 时,M1=[1110] M^{1} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} M1=[11​10​],结果为 1∗2+1∗1=3 1*2 + 1*1 = 3 1∗2+1∗1=3(正确,3级台阶有3种跳法)。
  • 当 n=5 n=5 n=5 时,M3=[3221] M^3 = \begin{bmatrix} 3 & 2 \\ 2 & 1 \end{bmatrix} M3=[32​21​],结果为 3∗2+2∗1=8 3*2 + 2*1 = 8 3∗2+2∗1=8(正确,5级台阶有8种跳法)。

通过矩阵快速幂,可高效解决大规模 n n n 的台阶跳法问题。

### 使用矩阵快速幂实现 Fibonacci 数列的算法设计与分析 #### 算法背景 Fibonacci 数列是一个经典的递推序列,其定义如下: \[ \text{fib}(0) = 0, \quad \text{fib}(1) = 1, \quad \text{fib}(n) = \text{fib}(n-1) + \text{fib}(n-2), \quad n \geq 2. \] 传统的递归方式时间复杂度较高 \(O(2^n)\),而动态规划可以优化到线性时间 \(O(n)\)。然而,在某些场景下(如大整数运算),仍需进一步降低时间复杂度。 利用 **矩阵快速幂** 的方法可以在对数时间内完成计算,即 \(O(\log n)\)[^1]。 --- #### 矩阵形式表达 Fibonacci 数列 可以通过构建一个转移矩阵来描述 Fibonacci 数列的关系: \[ \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^n = \begin{bmatrix} \text{fib}(n+1) & \text{fib}(n) \\ \text{fib}(n) & \text{fib}(n-1) \end{bmatrix}. \] 因此,问题转化为求解上述矩阵的 \(n\) 次方[^2]。 --- #### 快速幂原理 快速幂是一种高效的指数运算技术,基于分治的思想。对于任意正整数 \(a\) 和 \(b\),\(a^b\) 可以分解为以下两种情况之一: 1. 如果 \(b\) 是偶数,则 \(a^b = (a^{b/2})^2\); 2. 如果 \(b\) 是奇数,则 \(a^b = a \cdot a^{b-1}\). 这种分解使得原本需要 \(O(b)\) 时间的操作被压缩至 \(O(\log b)\)[^3]。 在本题中,我们将这一思想应用于矩阵乘法上。 --- #### 实现细节 以下是完整的 C++ 实现代码,用于计算 Fibonacci 数列中的某一项模 \(m\) 值: ```cpp #include <iostream> #include <vector> using namespace std; // 定义矩阵结构体 struct Matrix { vector<vector<long long>> data; Matrix(int size): data(size, vector<long long>(size)) {} }; // 矩阵相乘函数 Matrix multiply(const Matrix& A, const Matrix& B, long long mod) { int N = A.data.size(); Matrix result(N); for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { for (int k = 0; k < N; ++k) { result.data[i][j] = (result.data[i][j] + A.data[i][k] * B.data[k][j]) % mod; } } } return result; } // 矩阵快速幂函数 Matrix matrix_power(Matrix base, long long exp, long long mod) { int N = base.data.size(); Matrix result(N); // 初始化单位矩阵 for (int i = 0; i < N; ++i) result.data[i][i] = 1; while (exp > 0) { if (exp % 2 == 1) result = multiply(result, base, mod); base = multiply(base, base, mod); exp /= 2; } return result; } long long fibonacci(long long n, long long m) { if (n == 0) return 0; if (n == 1 || n == 2) return 1 % m; Matrix T(2); T.data[0][0] = T.data[0][1] = T.data[1][0] = 1; T.data[1][1] = 0; Matrix res = matrix_power(T, n - 1, m); return res.data[0][1]; } int main() { long long n, m; cin >> n >> m; cout << fibonacci(n, m) << endl; return 0; } ``` --- #### 复杂度分析 1. **空间复杂度**: 主要是存储两个大小固定的 \(2 \times 2\) 矩阵,故空间复杂度为 \(O(1)\). 2. **时间复杂度**: 矩阵乘法时间复杂度为 \(O(k^3)\),其中 \(k=2\) 表示矩阵维度;快速幂迭代次数为 \(O(\log n)\). 综合来看,总时间为 \(O(\log n)\)[^1]. --- #### 测试案例 为了验证程序的正确性和性能,可采用单元测试的方式。例如,针对小规模输入进行逐项对比,确保结果无误: | 输入 (\(n,m\)) | 预期输出 | |---------------|----------| | (0, 1e9+7) | 0 | | (1, 1e9+7) | 1 | | (2, 1e9+7) | 1 | | (10, 1e9+7) | 55 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值