动态规划的等效数学解决方法


从一道经典的动态规划题目 leetcode.70 爬楼梯说起,这道题目典型的动态规划解决,使用滚动数组,能做到时间复杂度O(N) 空间复杂度O(1) 程度上解决问题,这也是我的第一想法。
而官方题解给出的几种基于线性代数的解决方案,能将时间复杂度降低到O(logN),而且所涉及到的知识也是自己所学习过的,但却丝毫没有想到,所以这里稍作复习总结。

矩阵快速幂

矩阵快速幂用到的就是在现有通项的基础上,构建线性关系,把一维的数据变成多维,从而形成若干相同矩阵乘初始项,而这里问题就变成了,如何求某个矩阵的若干次幂
矩阵快速幂
矩阵快速幂的核心算法还是快速幂,只不过对于普通的快速幂算法,矩阵快速幂计算对象以及乘法运算对应的规则有所不同:

快速幂

求幂运算,一般的,比如 求 2 7 2^{7} 27,我们会计算 2 ∗ 2 ∗ 2 ∗ 2... 2*2*2*2... 2222...,这种计算方法是O(N)的,但利用快速幂我们可以将复杂度降低到 O(logN)
基本思想史,数字可以表示为2进制,比如 9(10)=1001(2),那么自然的,我就可以利用二进制参与幂的运算,即 2 9 = 2 1001 = 2 2 3 ∗ 1 + 2 2 ∗ 0 + 2 1 ∗ 0 + 2 0 ∗ 1 2^9=2^{1001}=2^{2^3*1+2^2*0+2^1*0+2^0*1} 29=21001=2231+220+210+201,而进行这样的二进制转换后,则只用进行较少的运算。
代码如下:

int pow_mod(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(n&1) ans*=a;   //&是与运算符,任何数&1都只会保留二进制最后一位
		a*=a
		b=b>>1;	
	}
	return ans;
}

矩阵乘法

一般的,我们用二阶数组来表示一个矩阵,而对于普通矩阵,我们有C=A·B C i j = ∑ k = 1 n A i k B k j C_{ij}=\sum_{k=1}^nA_{ik}B_{kj} Cij=k=1nAikBkj
所以矩阵乘法的代码如下:

Matrix operator*(const Matrix &B) const {//Matrix是二阶数组结构体
		Matrix ans;
		for(int i = 0; i < M; i++){
			for(int j = 0; j < M; j++){
				for(int k = 0; k < M; k++){
					ans.a[i][j] += a[i][k] * B.a[k][j];
				}
			}
		}
		return ans;
	} 

矩阵快速幂

Matrix pow_mod(Matrix A,int n)
{
	Matrix B;
	for(int i=0;i<2;i++)//初始化单位矩阵 
		for(int j=0;j<2;j++)//为什么单位矩阵?E*A=A,等效于数字 1*a=a 
			B.a[i][j]=i==j?1:0;
	while(n)//快速幂 
	{
		if(n&1)	B=mul(B,A);
		A=mul(A,A);
		n=n>>1;	
	}
	return B;
}

特别的,对于此道题目,我们最终需要得到的是

class Solution {
  public:
    int climbStairs(int n) {
        vector<vector<long long>> q = {{1, 1}, {1, 0}};
        vector<vector<long long>> res = matrixPow(q, n);
        return res[0][0];//是因为初始是[1,1]
    }
  private:
    vector<vector<long long>> matrixPow(vector<vector<long long>> &a, int n) {
        vector<vector<long long>> ret = {{1, 0}, {0, 1}}; // 初始状态
        while (n > 0) {
            if ((n & 1) == 1)   // 按位取值
                ret = matrixMultiply(ret, a);
            n >>= 1;
            a = matrixMultiply(a, a);   // 计算下一位的矩阵值,这里决定数据类型必须用longlong,否则越界风险
        }
        return ret;
    }
    vector<vector<long long>> matrixMultiply(vector<vector<long long>> &a, vector<vector<long long>> &b) {
        int i = 0, j = 0;
        vector<vector<long long>> c = {{0, 0}, {0, 0}};
        for (i = 0; i < 2; i++)
            for (j = 0; j < 2; j++)
                c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
        return c;
    }
};
//resources: leetcode评论区dl :ryl

通项公式

实际上是求矩阵的幂的一种其它想法,矩阵快速幂更多的是基于计算机计算特性,而通项公式则是基于矩阵分解来求矩阵的幂。
一般的,对于“好的”矩阵A,有 A = S − 1 Δ S A=S^{-1}\Delta S A=S1ΔS分解,而利用这种分解去求幂将简单得多,比如这道题目中的矩阵 [ 1 1 1 0 ] \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} [1110],得到特征方程:
λ ( λ − 1 ) = 1 λ 2 − λ − 1 = 0 Δ = 1 + 4 = 5 \lambda(\lambda-1)=1 \quad \lambda^2-\lambda-1=0 \quad \Delta=1+4=5 λ(λ1)=1λ2λ1=0Δ=1+4=5
可以借出,矩阵的特征值为 5 + 1 2 1 − 5 2 \frac{\sqrt5+1}{2} \quad \frac{1-\sqrt5}{2} 25 +1215
即矩阵 Δ = [ 5 + 1 2 0 0 1 − 5 2 ] \Delta=\begin{bmatrix} {\frac{\sqrt5+1}{2}} & 0 \\ 0 & \frac{1-\sqrt5}{2}\end{bmatrix} Δ=[25 +100215 ]
然后求出特征向量带入,或者直接解未知数都可以解决此类问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值