草庐IT

「学习笔记」矩阵乘法与矩阵快速幂

Keven-He 2023-03-28 原文

「学习笔记」矩阵乘法与矩阵快速幂

点击查看目录

矩阵乘

算法

矩阵 \(A\) 规模为 \(n\times m\),矩阵 \(B\) 规模为 \(m\times q\),设 \(C=A\times B\),则:

\[C_{i,j}=\sum_{k=1}^{m}A_{i,k}*B_{k,j} \]

代码

点击查看代码
const ll N=110,inf=1ll<<40;
ll n,m,p;
class Matrix{
public:
	ll mat[N][N];
	inline ll* operator[](ll x){return mat[x];}
	inline Matrix operator*(Matrix ma)const{
		Matrix mt;
		_for(i,1,n){
			_for(j,1,p){
				mt[i][j]=0;
				_for(k,1,m)mt[i][j]+=mat[i][k]*ma[k][j];
			}
		}
		return mt;
	}
}a,b;
inline void print(Matrix ma){
	_for(i,1,n){
		_for(j,1,p)printf("%lld ",ma[i][j]);
		puts("");
	}
	return;
}
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline void In(){
		n=rnt(),m=rnt();
		_for(i,1,n)_for(j,1,m)a[i][j]=rnt();
		p=rnt();
		_for(i,1,m)_for(j,1,p)b[i][j]=rnt();
		print(a*b);
		return;
	}
}

矩阵快速幂

算法

没啥好说的吧(

重载一下运算符然后冲一个矩阵快速幂就行了。

用处

代码(模板题

点击查看代码
const ll N=110,P=1e9+7,inf=1ll<<40;
ll n,k;
class Mat{
public:
	ll a[N][N];
	inline ll* operator[](ll x){return a[x];}
	inline void one(){_for(i,1,n)a[i][i]=1;}
	inline Mat operator*(Mat ma){
		Mat mt;
		_for(i,1,n){
			_for(j,1,n){
				mt[i][j]=0;
				_for(k,1,n)mt[i][j]=(mt[i][j]+a[i][k]*ma[k][j]%P)%P;
			}
		}
		return mt;
	}
}a;
inline void printf(Mat ma){
	_for(i,1,n){
		_for(j,1,n)printf("%lld ",ma[i][j]);
		puts("");
	}
	return;
}
inline Mat fpow(Mat ma,ll b){
	Mat ans;ans.one();
	while(b){
		if(b&1)ans=ans*ma;
		ma=ma*ma,b>>=1;
	}
	return ans;
}
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline void In(){
		n=rnt(),k=rnt();
		_for(i,1,n)_for(j,1,n)a[i][j]=rnt();
		printf(fpow(a,k));
		return;
	}
}

练习题

斐波那契数列

思路

众所周知斐波那契数列的递推式是 \(Fib_n=Fib_{n-1}+Fib_{n-2}\)\(\Theta(n)\) 本可以解决,但本题 \(n<2^{63}\),显然需要 \(\log_2n\) 算法,考虑优化。

我们设 \(F(n)\) 表示矩阵 \(\left[Fib_n,Fib_{n-1}\right]\),如果我们要把它变成 \(F(n+1)=\left[Fib_{n+1},Fib_n\right]\),则需要把 \(F(n)_{1,1}\) 挪到 \(F(n+1)_{1,2}\) ,把 \(F(n)_{1,1}+F(n)_{1,2}\) 挪到 \(F(n+1)_{1,1}\)

尝试用矩阵优化这个东西。

我们可以发现:

\[\begin{aligned} F(n+1)&=\left[\begin{matrix}Fib_n+1&Fib_n\end{matrix}\right]\\ &=\left[\begin{matrix}Fib_n*1+Fib_{n-1}*1&Fib_n*1+Fib_{n-1}*0\end{matrix}\right]\\ &=\left[\begin{matrix}F(n)_{1,1}*1+F(n)_{1,2}*1&F(n)_{1,1}*1+F(n)_{1,2}*0\end{matrix}\right]\\ \end{aligned} \]

那么设 \(M=\left[\begin{matrix}1&1\\1&0\end{matrix}\right]\),则:

\[\begin{aligned} F(n+1)&=\left[\begin{matrix}F(n)_{1,1}\times M_{1,1}+F(n)_{1,2}\times M_{2,1}&F(n)_{1,1}\times M_{1,2}+F(n)_{1,2}\times M_{2,2}\end{matrix}\right]\\ &=F(n)\times M \end{aligned} \]

然后冲一个矩阵快速幂就行了。

代码

点击查看代码
const ll N=5,inf=1ll<<40;
ll n,m;
class Mat{
public:
	ll a[N][N];
	inline ll* operator[](ll x){return a[x];}
	friend Mat Mul(Mat m1,Mat m2){
		Mat an;
		_for(i,1,2){
			_for(j,1,2){
				an[i][j]=0;
				_for(k,1,2)an[i][j]=(an[i][j]+m1[i][k]*m2[k][j]%m)%m;
			}
		}
		return an;
	}
};
inline Mat fpow(ll b){
	Mat ans;ans[1][1]=ans[1][2]=1,ans[2][1]=ans[2][2]=0;
	Mat ma;ma[1][1]=ma[1][2]=ma[2][1]=1,ma[2][2]=0;
	while(b>0){
		if(b&1)ans=Mul(ans,ma);
		ma=Mul(ma,ma),b>>=1;
	}
	return ans;
}
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline void In(){
		n=rnt(),m=rnt();
		Mat ans=fpow(n-2);
		printf("%lld\n",ans[1][1]);
		return;
	}
}

[SCOI2009] 迷路

思路

\(f_{i,j}\) 表示 \(j\) 时刻到点 \(i\) 的方案数,转移方程:

\[f_{i,j}=\sum_{(i,k)\in\mathbb{E}}f_{k,j-w_{i,k}} \]

不是很可做,那我们先考虑边权只有 \(0\)\(1\)的情况,可以发现转移方程能直接这样写:

\[f_{i,j}=\sum_{1\le j\le n}w_{i,k}\times f_{k,j-1} \]

发现又是个矩阵乘法,直接冲一个矩阵快速幂就行了。

但是本题边权不只有 \(0\)\(1\),不能直接冲。

那么我们对一个点进行拆点,如下图:

拆成

看起来有点复杂,但懂了之后好理解的。

拆完之后直接冲一个矩阵快速幂就行了。

开做发现冲一个矩阵快速幂就行了。

然而有一个边权不一定为一的限制,所以暴力把点拆进一个矩阵,就可以只用矩阵快速幂来做这道题了。

代码

点击查看代码
const ll N=110,P=2009,inf=1ll<<40;
ll n,m,t,g[N][N];
class Mat{
public:
	ll a[N][N];
	inline ll* operator[](ll x){return a[x];}
	inline void one(){_for(i,1,m)a[i][i]=1;return;}
	inline Mat operator*(Mat ma){
		Mat ans;
		_for(i,1,m){
			_for(j,1,m){
				ans[i][j]=0;
				_for(k,1,m)ans[i][j]=(ans[i][j]+a[i][k]*ma[k][j]%P)%P;
			}
		}
		return ans;
	}
}tu;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline ll rsnt(){
		char c=getchar();
		while(!isdigit(c))c=getchar();
		return (c^48);
	}
	inline Mat fpow(Mat ma,ll b){
		Mat ans;ans.one();
		while(b){
			if(b&1)ans=ans*ma;
			ma=ma*ma,b>>=1;
		}
		return ans;
	}
	inline void Zhuan(){
		_for(i,1,n){
			_for(j,1,9){
				ll k=10*(i-1)+j;
				tu[k][k+1]=1;
			}
			_for(j,1,n){
				ll k1=10*(i-1);
				ll k2=10*(j-1);
				if(g[i][j])tu[k1+g[i][j]][k2+1]=1;
			}
		}
		return;
	}
	inline void In(){
		n=rnt(),t=rnt();
		_for(i,1,n)_for(j,1,n)g[i][j]=rsnt();
		m=10*n,Zhuan();
		Mat ans=fpow(tu,t);
		printf("%lld\n",ans[1][m-9]);
		return;
	}
}

佳佳的 Fibonacci

思路

题目背景有时候不是白给的。

这道题中,联系题目背景可以发现原式可以化简为:

\[\begin{aligned} T(n)&=(F_1+F_2+\cdots+F_n)+(F_2+F_3\cdots+F_n)+(F_3+F_4+\cdots+F_n)+\cdots+(F_{n-1}+F_n)+F_n\\ &=(S(n)-S(0))+(S(n)-S(1))+(S(n)-S(3))+\cdots+(S(n)-S(n-2))+(S(n)-S(n-1))\\ &=\sum_{i=1}^{n}S(n)-S(i-1) \end{aligned} \]

通过题目背景可知:

可这(指 \(S(n)\))对佳佳来说还是小菜一碟。

那么我们就去算 \(S(n)\)好像有点断章取义。

\[\begin{aligned} S(n)&=\sum_{i=1}^{n}F_i\\ &=\sum_{i=1}^{n}F_{i-1}+F_{i-2}\\ &=\sum_{i=0}^{n-1}F_{i}+\sum_{i=-1}^{n-2}F_{i}\\ &=\sum_{i=0}^{n-1}F_{i}+\sum_{i=0}^{n-2}F_{i}+F_{-1}\\ &=S(n-1)+S(n-2)+1 \end{aligned} \]

同时,\(S(n)=S(n-1)+F_n\),所以 \(S(n)=F_{n+2}-1\)

为啥 $F_{-1}=1$ 啊?

\[\begin{aligned} \because &F_n=F_{n-1}+F_{n-2}\\ \therefore &F_1=F_0+F_{-1}\\ &1=0+F_{-1}\\ \therefore &F_{-1}=1\\ &同理可得:\\ &F_{-2}=-1,F_{-3}=2,F_{-4}=-3,F_{-5}=5,\cdots,F_{-n}=(-1)^{n+1}F_n \end{aligned} \]

然后往原式子里带:

\[\begin{aligned} T(n)&=\sum_{i=1}^{n}S(n)-S(i-1)\\ &=\sum_{i=1}^{n}(F_{n+2}-1)-(F_{i+1}-1)\\ &=nF_{n+2}-\sum_{i=2}^{n+1}F_{i}\\ &=nF_{n+2}-(S(n+1)-1)\\ &=nF_{n+2}-F_{n+3}+2 \end{aligned} \]

然后冲一个矩阵快速幂就行了。

代码

点击查看代码
const ll N=110,inf=1ll<<40;
ll n,m;
class Mat{
public:
	ll a[N][N];
	inline ll* operator[](ll x){return a[x];}
	inline void one(){a[1][1]=a[1][2]=1;}
	inline Mat operator*(Mat ma){
		Mat ans;
		_for(i,1,2){
			_for(j,1,2){
				ans[i][j]=0;
				_for(k,1,2)ans[i][j]=(ans[i][j]+a[i][k]*ma[k][j]%m)%m;
			}
		}
		return ans;
	}
};
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline Mat fpow(Mat ma,ll b){
		Mat ans;ans.one();
		while(b){
			if(b&1)ans=ans*ma;
			ma=ma*ma,b>>=1;
		}
		return ans;
	}
	inline void In(){
		n=rnt(),m=rnt();
		Mat mat;mat[1][1]=mat[1][2]=mat[2][1]=1;
		Mat ans=fpow(mat,n+1);
		printf("%lld\n",(n*ans[1][2]%m-ans[1][1]+m+2)%m);
		return;
	}
}

选拔队员(不知道教练从哪里找的)

题意

选出若干个男生和若干多个女生(即男女生的数目随便定)安排到机房内的 \(N\) 个位置上去,要求任意两位女生不能相邻(即任意两个女生之间必须有至少一个男生),求方案数 \(\bmod{M}\)

思路

直接用排列推是不行的,尝试写个 \(\text{dp}\)

\(f_{i,0}\) 表示有 \(n\) 个人坐了上去,最后一个人是男; \(f_{i,1}\) 表示有 \(n\) 个人坐了上去,最后一个人是女。

初始状态为 \(f_{i,0}=f_{i,1}=1\),显然有递推式:

\[\begin{aligned} f_{i,0}&=f_{i-1,0}+f_{i-1,1}\\ f_{i,1}&=f_{i-1,0} \end{aligned} \]

用矩阵优化:

\[\left[\begin{matrix} f_{n,0}&f_{n,1} \end{matrix}\right] \times \left[\begin{matrix} 1&1\\ 1&0 \end{matrix}\right] = \left[\begin{matrix} f_{n+1,0}&f_{n+1,1} \end{matrix}\right] \]

诶这玩意儿不就是斐波那契数列吗?!

然后冲一个矩阵快速幂就行了。

代码

点击查看代码
const ll N=110,inf=1ll<<40;
ll T,m,n;
class Mat{
public:
	ll a[5][5];
	inline ll* operator[](ll x){return a[x];}
	inline Mat operator*(Mat ma){
		Mat ans;
		_for(i,1,2){
			_for(j,1,2){
				ans[i][j]=0;
				_for(k,1,2)ans[i][j]=(ans[i][j]+a[i][k]*ma[k][j]%m)%m;
			}
		}
		return ans;
	}
};
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline Mat fpow(ll b){
		Mat ans;ans[1][1]=ans[1][2]=1,ans[2][1]=ans[2][2]=0;
		Mat ma;ma[1][1]=ma[1][2]=ma[2][1]=1,ma[2][2]=0;
		while(b){
			if(b&1)ans=ans*ma;
			ma=ma*ma,b>>=1;
		}
		return ans;
	}
	inline void In(){
		n=rnt();
		printf("%lld\n",fpow(n)[1][1]%m);
		return;
	}
}

Tr A

思路

不能理解这道题出的为什么这么靠后。

冲一个矩阵快速幂就行了。

代码

点击查看代码
const ll N=20,P=9973,inf=1ll<<40;
ll T,n,k;
class Mat{
public:
	ll a[N][N];
	inline ll* operator[](ll x){return a[x];}
	inline void one(){_for(i,1,n)a[i][i]=1;return;}
	inline void zero(){memset(a,0,sizeof(a));return;}
	inline Mat operator*(Mat ma){
		Mat ans;ans.zero();
		_for(i,1,n){
			_for(j,1,n){
				ans[i][j]=0;
				_for(k,1,n)ans[i][j]=(ans[i][j]+a[i][k]*ma[k][j]%P)%P;
			}
		}
		return ans;
	}
}a;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline Mat fpow(Mat ma,ll b){
		Mat ans;ans.zero();ans.one();
		while(b){
			if(b&1)ans=ans*ma;
			ma=ma*ma,b>>=1;
		}
		return ans;
	}
	inline ll GetAnswer(Mat ma){
		ll num=0;
		_for(i,1,n)num=(num+ma[i][i]);
		return num%P;
	}
	inline void In(){
		n=rnt(),k=rnt();
		a.zero();
		_for(i,1,n)_for(j,1,n)a[i][j]=rnt();
		Mat ans=fpow(a,k);
		printf("%lld\n",GetAnswer(ans));
		return;
	}
}

有关「学习笔记」矩阵乘法与矩阵快速幂的更多相关文章

  1. 旋转矩阵的几何意义 - 2

    点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度;     在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。

  2. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  3. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  4. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  5. ruby - ruby 乘法语句中星号中断语法前的空格 - 2

    在添加一些空格以使代码更具可读性时(与上面的代码对齐),我遇到了这个:classCdefx42endendm=C.new现在这将给出“错误数量的参数”:m.x*m.x这将给出“语法错误,意外的tSTAR,期待$end”:2/m.x*m.x这里的解析器到底发生了什么?我使用Ruby1.9.2和2.1.5进行了测试。 最佳答案 *用于运算符(42*42)和参数解包(myfun*[42,42])。当你这样做时:m.x*m.x2/m.x*m.xRuby将此解释为参数解包,而不是*运算符(即乘法)。如果您不熟悉它,参数解包(有时也称为“spl

  6. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  7. ruby-on-rails - 浮点乘法的 Ruby 奇怪问题 - 2

    有没有人用ruby​​解决这个问题:假设我们有:a=8.1999999我们想将它四舍五入为2位小数,即8.20,然后乘以1,000,000得到8,200,000我们是这样做的;(a.round(2)*1000000).to_i但是我们得到的是8199999,为什么?奇怪的是,如果我们乘以1000、100000或10000000而不是1000000,我们会得到正确的结果。有人知道为什么吗?我们正在使用ruby​​1.9.2并尝试使用1.9.3。谢谢! 最佳答案 每当你在计算中得到时髦的数字时使用bigdecimalrequire'bi

  8. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  9. ruby - 如何以表格格式快速打印 Ruby 哈希值? - 2

    有没有办法快速将表格格式的ruby​​哈希打印到文件中?如:keyAkeyBkeyC...1232343451253474456...其中散列的值是不同大小的数组。还是使用双循环是唯一的方法?谢谢 最佳答案 试试我写的这个gem(在表中打印散列、ruby对象、ActiveRecord对象):http://github.com/arches/table_print 关于ruby-如何以表格格式快速打印Ruby哈希值?,我们在StackOverflow上找到一个类似的问题:

  10. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

随机推荐