浅谈矩阵加速算法

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43960287/article/details/88993370

前言

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

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

例题一:Fibonacci第n项

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

因为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的矩阵,后面就简单了。

展开阅读全文

没有更多推荐了,返回首页