首页 > 其他分享 >【笔记】矩阵快速幂

【笔记】矩阵快速幂

时间:2024-02-12 21:23:45浏览次数:28  
标签:begin end ll 矩阵 笔记 times bmatrix 快速

前置芝士

快速幂

什么是矩阵?

矩阵,是由 \(\begin{bmatrix}\end{bmatrix}\) 组成的一个方阵(就这么理解好啦)。

比如:\(\begin{bmatrix}1& 2\\3&4\end{bmatrix}\) 是一个 \(2\times 2\) 的矩阵。

矩阵乘法

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

U401562 矩阵乘法(站外题)

矩阵乘法的计算方法:

\[c_{i,j} = \sum_{k=1}^{n} a_{i,k}\times b_{k,j} \]

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

第一层循环枚举 \(k\),第二、三层循环分别枚举 \(i\),\(j\)。

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\) 列的和就是 \(C_{i,j}\)。

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


矩阵快速幂

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

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

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

\[\begin{cases}f_i = 1&i\le 3\\f_i = f_{i-1} + f_{i-2}&i\ge 3\\\end{cases} \]

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

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

\[\begin{bmatrix}?&?\\?&?\end{bmatrix}\times \begin{bmatrix}f_{i-1}\\f_{i-2}\\\end{bmatrix} = \begin{bmatrix}f_i\\f_{i-1}\end{bmatrix} \]

矩阵 \(2\) 是一个 \(2\times 1\) 的矩阵,矩阵 \(3\) 也是一个 \(2\times 1\) 的矩阵,因此矩阵 \(1\) 是一个 \(2\times 2\) 的矩阵。

然后通过递推公式 \(f_i = f_{i-1}\times 1+f_{i-2}\times 1\) 和 \(f_{i-1} = f_{i-1}\times 1\),可构造矩阵:

\[\begin{bmatrix}1&1\\1&0\end{bmatrix}\times \begin{bmatrix}f_{i-1}\\f_{i-2}\\\end{bmatrix} = \begin{bmatrix}f_i\\f_{i-1}\end{bmatrix} \]

通过如上公式,不停迭代,如果求 \(f_n\),就是乘以 \(n-2\) 个 \(\begin{bmatrix}1&1\\1&0\end{bmatrix}\)。

因此可以得到如下公式:

\[\begin{bmatrix}1&1\\1&0\end{bmatrix}^{n-2}\times \begin{bmatrix}f_{i-1}\\f_{i-2}\\\end{bmatrix} = \begin{bmatrix}f_n\\f_{n-1}\end{bmatrix} \]

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

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

#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\) 矩阵在图中已经给出,为 \(\begin{bmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{bmatrix}\),也就是第 \(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;
}

题目给出 \(a_1\),\(a_2\),以及递推公式 \(a_i = p\times a_{i-1} + q\times a_{i-2},i\ge 3\)。

让我们求 \(a_n\bmod m\) 的值。

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

这里,\(p\)、\(q\) 是个不定值。

我们试着规划矩阵:

\[\begin{bmatrix}?&?\\?&?\end{bmatrix}\times \begin{bmatrix}f_{i-1}\\f_{i-2}\\\end{bmatrix} = \begin{bmatrix}f_i\\f_{i-1}\\\end{bmatrix} \]

明显,矩阵为 \(\begin{bmatrix}p&q\\1&0\\\end{bmatrix}\)。

验证一下,\(p\times f_{i-1} + q\times f_{i-2} = f_i\),\(1\times f_{i-2} + 0\times f_{i-2} = f_{i-1}\),矩阵规划正确。

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

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

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

标签:begin,end,ll,矩阵,笔记,times,bmatrix,快速
From: https://www.cnblogs.com/2021zjhs005/p/18014138

相关文章

  • boruvka 算法学习笔记
    boruvka算法就是最小生成树B算法。B算法的思路是每次对每个连通块,求出它能连出去的权值最小的边,然后再按边权从小到大合并。由于每次操作连通块数至少减半,所以复杂度是\(O(m\logn)\)。1.CF1305GKuroniandAntihype题意:长为\(n\)的数列\(a\),现在要选择全部数,每一次你......
  • C++——异常处理模块笔记
    异常处理是C++中的重要概念之一,用于处理在程序执行过程中可能发生的错误或异常情况。异常是指在程序执行过程中发生的一些不寻常的事件,例如除零错误、访问无效内存等。C++提供了一套异常处理机制,使得程序可以优雅地处理这些异常,提高程序的可靠性和健壮性。异常是一种程序......
  • 江禾:零散媒体笔记
    零基础绘画练习排线,整齐几何体进阶把想画的东西画像,不断观察修改提高自己审美,分析好看的画为什么好看眼睛和画面要平行明确线条才能进步混剪素材预告片欧美预告片YouTube、Bilibili搜电影搜电影的替代网站纪录片《电影史话》《出神入化:电影剪辑的魔力》《工......
  • 二十八、实践中前端的一些笔记
    display:flex/inline-flex使用了display:flex/inline-flex属性后,子元素横向排列使用了display:flex属性后,父元素不设置宽度,宽度就是100%;不会被子元素宽度撑开;使用了display:inline-flex属性后,父元素不设置宽度,宽度就是所有的子元素宽度之和,会被子元素宽度撑开,实现宽度自......
  • 2-SAT学习笔记
    2-SATk-SAT问题SAT是适定性(Satisfiability)问题的简称。一般形式为k−适定性问题,简称k−SAT。而当k>2时该问题为NP完全的。所以我们只研究k=2的情况。2−SAT,简单的说就是给出n个集合,每个集合有两个元素,已知若干个<a,b>,表示a与b矛盾(其中a与b属于不同的集合)。然后从每个集合选择......
  • Linux 中 使用awk数组根据基因的PAV矩阵计算基因的存在频率
     001、测试数据[b20223040323@admin1test]$lsx_gather_pav.txt[b20223040323@admin1test]$catx_gather_pav.txt##测试数据;每一行是一个个体;每一列是一个基因;矩阵中的0表示基因在这个个体中缺失,1表示基因在这个个体中存在01111......
  • 二十一、JS笔记
    JSONimportjson#对象转字符串str=json.dumps(dict,ensure_ascii=False)#ensure_ascii=True或不设置str=json.dumps(dict)#这时前端拿到的是未解码的数据:{"key1":"\u7528\u6237\u8f93...",...}obj=json.loads(str)#字符串转对象jsJSON.parse(str)#字符......
  • 拉格朗日插值学习笔记
    拉格朗日插值第一步:子函数\(f_i(x)=\begin{cases}1&x=x_i\\0&x=x_j(i\nej)\end{cases}\)由此可得:\(f(x)=\sum\limits_{i=1}^ny_if_i(x)\)第二步:对于\(f_i(x)\),要满足当\(x=x_i\)时,\(y=1\),而\(x\nex_i\)时,\(y=0\)故:\(f_i(x)=\dfrac{\prod\limits_{j=1,j\......
  • Eigen中变换矩阵Eigen::Isometry3d T的使用方法(左乘和右乘)
    https://zhuanlan.zhihu.com/p/610439768?utm_id=0 一、基本定义Eigen::Isometry3dT_imu_to_lidar=Eigen::Isometry3d::Identity()转换矩阵本质是一个4*4的矩阵二、操作方法.translation():无参数,返回当前变换平移部分的向量表示(可修改),可以索引[]获取各分量.rotation(......
  • Ubuntu 设置合上笔记本盖子不休眠的方法
    Ubuntu设置合上笔记本盖子不休眠的方法编辑下列文件:sudogedit/etc/systemd/logind.conf​​#HandlePowerKey按下电源键后的行为,默认poweroff​​#HandleSleepKey按下挂起键后的行为,默认suspend​​#HandleHibernateKey按下休眠键后的行为,默认hibernate​​#HandleLidS......