矩阵快速幂 快速求解递推公式
文章目录
- 习题
- 790.多米诺和托米诺平铺
- 对于一个给定的递推公式,例如
dp[i] = dp[i-1] * a + dp[i-2] * b
,那么常用的做法,肯定是使用o(n)
的时间复杂度进行线性求解,但是如果 n = 10 18 n={10}^{18} n=1018,那么肯定超时的,这个时候就需要使用到矩阵快速幂
,其实也就是使用矩阵运算+快速幂
进行求解递归结果
矩阵乘法
- 咱们先手搓一下
矩阵乘法
! - 这是一个 o ( n 3 ) o(n^3) o(n3)时间复杂度的暴力做法,那么如何快速记忆?
- 矩阵
A
和B
的形状分别是a*b
和b*c
,结果矩阵C
的形状是a*c
,所以最外层循环是rang(a)
,中间一层的循环是range(c)
,最内层循环是range(b)
,最内层循环用于将矩阵A和B对应位置的元素相乘再进行求和
- 矩阵
# 矩阵乘法 A @ B
def matrix_multiply(A, B):m, n = len(A), len(B[0])C = [[0] * n for _ in range(m)]for i in range(m):for j in range(n):for k in range(len(B)):C[i][j] += A[i][k] * B[k][j]return C
快速幂
- 快速幂是一种思想!将
幂次的指数进行二分拆解,在o(logn)时间复杂度内求解出幂次
,而不在乎底的形式(正常来说就是数,在这里就替换成矩阵!不过无所谓只要能乘起来即可)
# 矩阵快速幂,A ^ n @ B ,用于求解 矩阵 A 的 n 次幂,然后再乘上 矩阵 B
# 矩阵快速幂 求解A ^ n * B
def matrix_power(A, n, B):res = B while n > 0:if n & 1:res = matrix_multiply(res, A)A = matrix_multiply(A, A)n >>= 1return res
习题
对于习题,其实只要是递推公式,都可以使用
矩阵快速幂
来快速求解,当然这也是当n>10^7
左右的时候,使用线性时间会超时的情况
790.多米诺和托米诺平铺
790.多米诺和托米诺平铺
- 思路分析:上面的题目的递推公式比较难想,建议可以去看灵神的题解,这里主要是使用这个题目作为例子,说明
矩阵快速幂
的使用
灵神题解
- 递推公式
f[i] = f[i - 1] * 2 + f[i - 3]
- 根据等式右边,涉及到
f[i-1]和f[i-3],虽然没有涉及f[i-2]
,所以右边设置为3*1
的矩阵,左边的话,为了对应也设置一个3*1
的矩阵
- 然后根据这个
递推公式确定权重矩阵A
MOD = 1_000_000_007# a @ b,其中 @ 是矩阵乘法
def mul(a: List[List[int]], b: List[List[int]]) -> List[List[int]]:return [[sum(x * y for x, y in zip(row, col)) % MOD for col in zip(*b)]for row in a]# a^n @ f
def pow_mul(a: List[List[int]], n: int, f: List[List[int]]) -> List[List[int]]:res = fwhile n:if n & 1:res = mul(a, res)a = mul(a, a)n >>= 1return resclass Solution:def numTilings(self, n: int) -> int:if n == 1:return 1f2 = [[2], [1], [1]]m = [[2, 0, 1], [1, 0, 0], [0, 1, 0]]fn = pow_mul(m, n - 2, f2)return fn[0][0]