浅谈矩阵加速算法

本文介绍了如何使用矩阵加速算法高效解决复杂数列问题,如Fibonacci数列和TR数列等。通过矩阵快速幂运算,可在大规模数据下快速求解。此外,还拓展讲解了广义矩阵乘法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

(看此文章请先了解一下什么是矩阵)

矩阵加速是个很神奇的算法,它可以在极端数据下、级短时间内解决复杂问题,而且还很简单。我们从例题入手:

例题一:Fibonacci第n项

Fibonacci数列满足f(1)=f(2)=1,f(i)=f(i-1)+f(i-2),i>2,求f(n)。(n<=1e9)

这个数据很大,不能常规地用递推求解。那我们不拐弯路,直接开始公式推导吧:

因为f_{n}=f_{n-1}+f_{n-2}(关键递推式),所以可以把f_{n-1}*1+f_{n-2}*1放入一个矩阵乘法(n*m和m*q的矩阵)中。因为是2项乘2项,所以m=2,为了达到有规律的累乘,得保证结果也为n*2的矩阵,所以q=2。因为暂时没有其他要求的数,所以n=1。矩阵为,相乘后,(1,1)的位置上就是f_{n}。而要求到f_{n+1},则左矩阵得为\begin{bmatrix} f_{n} &f_{n-1} \end{bmatrix},所以(2,2)位置要通过f_{n-1}f_{n-2}和另两个数的“加乘”运算得到f_{n-1}。显然,f_{n-1}=f_{n-1}*1+f_{n-2}*0,所以整个乘法为\begin{bmatrix} f_{n-1} &f_{n-2} \end{bmatrix}*\begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix},可以得到\begin{bmatrix} f_{n} & f_{n-1} \end{bmatrix},再乘一个\begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}又得到\begin{bmatrix} f_{n+1} & f_{n} \end{bmatrix},以此类推。所以\begin{bmatrix} f_{2} &f_{1} \end{bmatrix}*\begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}^{n-2}=\begin{bmatrix} f_{n} & f_{n-1} \end{bmatrix},f2、f1已知,用一个矩阵快速幂就出来了。

(代码自己水)

例题二:TR的数列

TR非常喜欢数学,经常一个人拿出草稿纸研究奇奇怪怪的数学问题,最近,他突然对数列产生了兴趣,他找到一个数列,类似于斐波拉契,即:Tn=1*f1+2*f2+3*f3+……+n*fn    (fn为斐波拉契的第n项值),现在TR想请你帮忙求Tn%m的值(1≤n,m≤2^31-1)。

V_{n}=n*f_{n},则

V_{n}=n*f_{n-1}+n*f_{n-2}

V_{n}=(n-1)*f_{n-1}+f_{n-1}+(n-2)*f_{n-2}+2*f_{n-2}

V_{n}=V_{n-2}+V_{n-1}+2*f_{n-2}+f_{n-1}

所以T_{n}=T_{n-1}*1+V_{n-2}*1+V_{n-1}*1+f_{n-2}*2+f_{n-1}*1(关键递推式),左矩阵设置为\begin{bmatrix} T_{n-1} &V_{n-2} &V_{n-1} &f_{n-2} &f_{n-1} \end{bmatrix},然后推出

V_{n-1}=T_{n-1}*0+V_{n-2}*0+V_{n-1}*1+f_{n-2}*0+f_{n-1}*0

V_{n}=T_{n-1}*0+V_{n-2}*1+V_{n-1}*1+f_{n-2}*2+f_{n-1}*1

f_{n-1}=T_{n-1}*0+V_{n-2}*0+V_{n-1}*0+f_{n-2}*0+f_{n-1}*1

f_{n}=T_{n-1}*0+V_{n-2}*0+V_{n-1}*0+f_{n-2}*1+f_{n-1}*1

所以\begin{bmatrix} T_{n-1} &V_{n-2} &V_{n-1} &f_{n-2} &f_{n-1} \end{bmatrix}*\begin{bmatrix} 1 &0 &0 &0 &0 \\ 1 &0 &1 &0 &0 \\ 1 &1 &1 &0 &0 \\ 2 &0 &2 &0 &1 \\ 1 &0 &1 &1 &1 \end{bmatrix}=\begin{bmatrix} T_{n} &V_{n-1} &V_{n} &f_{n-1} &f_{n} \end{bmatrix}

明白了吗?最后\begin{bmatrix} T_{2} &V_{1} &V_{2} &f_{1} &f_{2} \end{bmatrix}* \begin{bmatrix} 1 &0 &0 &0 &0 \\ 1 &0 &1 &0 &0 \\ 1 &1 &1 &0 &0 \\ 2 &0 &2 &0 &1 \\ 1 &0 &1 &1 &1 \end{bmatrix}^{n-2} =\begin{bmatrix} T_{n} &V_{n-1} &V_{n} &f_{n-1} &f_{n} \end{bmatrix},还是用矩阵快速幂。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,mod;
struct matrix{
    int n,m;
    long long c[105][105];
    matrix(){memset(c,0,sizeof(c));}
    matrix operator*(const matrix&a){
        matrix r;r.n=n,r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j])%mod;
        return r;
    }
}a,b;
matrix mpow(matrix a,long long b){
    matrix res;res.n=res.m=a.n;
    for(int i=1;i<=res.n;i++)res.c[i][i]=1;
    for(;b;b>>=1){
        if(b&1)res=res*a;
        a=a*a;
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&n,&mod);
    if(n==1){
        printf("%lld",1%mod);
        putchar('\n');
        return 0;
    }
    a.n=1,a.m=5;
    a.c[1][1]=3,a.c[1][2]=a.c[1][4]=a.c[1][5]=1,a.c[1][3]=2;
    b.n=b.m=5;
    b.c[1][1]=1;
    b.c[2][1]=b.c[2][3]=b.c[3][1]=b.c[3][2]=b.c[3][3]=1;
    b.c[4][1]=b.c[4][3]=2,b.c[4][5]=1;
    b.c[5][1]=b.c[5][3]=b.c[5][4]=b.c[5][5]=1;
    a=a*mpow(b,n-2);
    printf("%lld",a.c[1][1]%mod);
    putchar('\n');
    return 0;
}

例题三:【山东省选】递归数列(版本2)

总结

(例题一、二的题解看懂一个就行)矩阵加速做法为先列出关键递推式(右边要为一次m项式),再根据式子列出1*m和m*m的矩阵,后面就简单了。

(update2021.3.13)扩展:广义矩乘

上面的方法看似只能解决由加减号构成的递推式,实际上由取max、取min符号连接的式子也可以用矩阵加速优化,这时就用到了广义的矩阵乘法:

C=A*B,C(i,j)=max/min(A(i,k)+B(k,j)),(这只是其中一种)

可以证明,广义矩阵乘法仍然满足结合律,但是不满足分配率,不过这就够了,因为矩阵加速只需要用到结合律。

由于摆脱了加减号的束缚,使得矩阵加速可以优化许多看似无法优化的题,动态DP就是在广义矩乘基础上建立的算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值