欢迎来到 @2021zjhs005 的博客园!

【笔记】矩阵快速幂

前置芝士

快速幂

什么是矩阵?

矩阵,是由 [] 组成的一个方阵(就这么理解好啦)。

比如:[1234] 是一个 2×2 的矩阵。

矩阵乘法

矩阵乘法的条件:仅当第 1 个矩阵的列数 =2 个矩阵的行数才有意义。

U401562 矩阵乘法(站外题)

矩阵乘法的计算方法:

ci,j=k=1nai,k×bk,j

了解了这些以后,代码就迎刃而解:

第一层循环枚举 k,第二、三层循环分别枚举 ij

for(k=1;k<=n;k++)
    for(i=1;i<=m;i++)
        for(j=1;j<=p;j++)
            c[i][j]+=a[i][k]*b[k][j];

就是 A 的第 i 行分别乘以 B 的第 j 列的和就是 Ci,j

这样就可以得到一个 m×p 的矩阵(第一个矩阵的行 × 第二个矩阵的列)。


矩阵快速幂

不懂快速幂的可以见顶部。

矩阵快速幂,用于解决 n 很大的情况下求解某些东西。

比如求斐波那契数列的第 n 项。我们知道,斐波那契数列有一个通项公式:

{fi=1i3fi=fi1+fi2i3

  • 首先特判 n=1,2 的情况。

将上面的递推公式变为矩阵:

[????]×[fi1fi2]=[fifi1]

矩阵 2 是一个 2×1 的矩阵,矩阵 3 也是一个 2×1 的矩阵,因此矩阵 1 是一个 2×2 的矩阵。

然后通过递推公式 fi=fi1×1+fi2×1fi1=fi1×1,可构造矩阵:

[1110]×[fi1fi2]=[fifi1]

通过如上公式,不停迭代,如果求 fn,就是乘以 n2[1110]

因此可以得到如下公式:

[1110]n2×[fi1fi2]=[fnfn1]

咦,这不就是快速幂吗?只不过把乘法改成了矩阵乘法而已嘛。

我们可以定义一个结构体,用重载操作符代替矩阵乘法,于是代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll MOD=1e9+7;
const ll N=3;
ll n,k;
struct node{ 
    ll x[N][N];
    node(){
        memset(x,0,sizeof(x));
    }
    inline void bases(){
        memset(x,0,sizeof(x));
        x[1][1]=x[1][2]=x[2][1]=1;
    }
    inline void anses(){
        memset(x,0,sizeof(x));
        x[1][1]=x[2][1]=1;      
    }
    node operator *(const node &b)const{//重载操作符。
        node anss;
        for(ll k=1;k<=2;k++)//矩阵乘法模板。
            for(ll i=1;i<=2;i++)
                for(ll j=1;j<=2;j++)
                    anss.x[i][j]=(anss.x[i][j]+x[i][k]*b.x[k][j])%MOD;
        return anss;
    }
}base,ans;
inline void init(){
    base.bases();
    ans.anses();
}
inline void quick_pow(ll n){//矩阵快速幂。
    while(n){
        if(n&1) ans=base*ans;
        base=base*base;
        n>>=1;
    }
}
int main(){
	cin>>n;
	if(n<=2){
		cout<<1<<endl;
		exit(0);
	}
    init();
    quick_pow(n-2);
    cout<<ans.x[1][1]<<endl;
	return 0;
}

经典例题

就是一道矩阵快速幂模板啦。

base 矩阵就是读入的 a 矩阵,ans 矩阵在图中已经给出,为 [100010001],也就是第 i 行第 i 列为 1,其余都是
0

code

#define ll long long
const ll MOD=1e9+7;
const ll N=1e2+10;
ll n,k;
struct node{ 
  ll x[N][N];
  node(){memset(x,0,sizeof(x));} 
  inline void init(){//定义 init() 函数初始化,只有该结构体才允许该初始化。
      for(ll i=1;i<=n;i++) x[i][i]=1;
  }
//说句闲话:还可以把矩阵乘法也搬进来。
}a;
node operator * (const node &a,const node &b){//矩阵乘法。
    node ans;
    for(ll k=1;k<=n;k++)
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=n;j++){
                ans.x[i][j]=(ans.x[i][j]+a.x[i][k]*b.x[k][j]%MOD)%MOD;
            }
    return ans;
}
int main(){
	cin>>n>>k;
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=n;j++) cin>>a.x[i][j];
    node ans;
    ans.init();//初始化。
    while(k){//矩阵快速幂。
        if(k&1) ans=ans*a;
        a=a*a;
        k>>=1;
    }
    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=n;j++)
            cout<<ans.x[i][j]<<" ";
        cout<<endl;
    }
	return 0;
}

题目给出 a1a2,以及递推公式 ai=p×ai1+q×ai2,i3

让我们求 anmodm 的值。

我们平时的斐波那契数列里,p=q=1

这里,pq 是个不定值。

我们试着规划矩阵:

[????]×[fi1fi2]=[fifi1]

明显,矩阵为 [pq10]

验证一下,p×fi1+q×fi2=fi1×fi2+0×fi2=fi1,矩阵规划正确。

PS:有些题解是 1×2 的矩阵乘以 2×2 的矩阵,而我是 2×2 的矩阵乘以 2×1 的矩阵,因此规划结果可能不一样,不过没关系。

最后注意取余,初始化不要弄错位置。

代码就不展示了,反正和上面的几乎一样,改一下就行啦。

posted @   2021zjhs005  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
记住你学习信息学奥赛的目的,以及梦想。朝着它坚持,这,也是一种美。
点击右上角即可分享
微信分享提示