矩阵运算
矩阵乘法
1.矩阵加减法
矩阵加减法就是将矩阵对应位置的数相加减。
设 \(A,B,C\) 均为 \(N\times M\) 矩阵,则 \(C=A\pm B \iff i\in [1,N],j\in [1,M]\):
例如:
\(\begin {pmatrix} 1&2 \\ 3&4 \end {pmatrix}+\begin{pmatrix} 4&3\\2&1\end {pmatrix}=\begin {pmatrix} 5&5\\5&5\end {pmatrix}\)
\(\begin{pmatrix} 3&2\\4&5\\1&2 \end{pmatrix}- \begin{pmatrix} 1&1\\1&1\\1&1 \end{pmatrix}=\begin{pmatrix} 2&1\\3&4\\0&1 \end{pmatrix}\)
矩阵加减法满足交换律,即 \(A+B=B+A\) 。满足结合律,即 \((A+B)+C=A+(B+C)\) 。满足减法的基本性质,即 \(A-B-C=A-(B+C)\) 。
2.矩阵乘法
矩阵乘法定义略微复杂。
设 \(A\) 是 \(n\times m\) 矩阵,\(B\) 是 \(m\times q\) 矩阵。
则 \(C=A\times B\) 是 \(n\times q\) 矩阵,且 \(i\in[1,n],j\in[i,p]\) :
例如:
\(\begin{pmatrix} 1&1&1\\1&1&1 \end{pmatrix}\times \begin{pmatrix} 1&1\\1&1\\1&1 \end{pmatrix}=\begin{pmatrix} 3&3\\3&3 \end{pmatrix}\)
\(\begin{pmatrix} 1&1\\1&1\\1&1 \end{pmatrix}\times \begin{pmatrix} 1&1&1\\1&1&1 \end{pmatrix}=\begin{pmatrix} 2&2&2\\2&2&2\\2&2&2 \end{pmatrix}\)
矩阵乘法满足结合律,即 \((A\times B)\times C=A\times (B\times C)\) 。满足分配律,即 \((A+B)\times C=A\times C+B\times C\) 。不满足交换律,即 \(A\times B\ne B\times A\;(A\ne B)\) 。
例题1、矩阵乘法
题意:给定两个矩阵 \(A,B\) ,计算 \(A\times B\) 的值。(数据保证 \(A\) 矩阵的行数等于 \(B\) 矩阵的列数)
分析:只需按照矩阵乘法的定义模拟即可:
#include<iostream>
using namespace std;
int a[101][101],b[101][101],ans[101][101],n,m,k;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=k;j++){
scanf("%d",&b[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=k;j++){
for(int k=1;k<=m;k++){
ans[i][j]+=a[i][k]*b[k][j];
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=k;j++){
printf("%d ",ans[i][j]);
}
puts("");
}
return 0;
}
例题二、矩阵快速幂
给定 \(n\times n\) 矩阵 \(A\) ,计算 \(A^k\) 的值。
只需重载运算符即可:
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
const int mo=1e9+7;
ll n,k;
struct bfs{
ll c[200][200];
};
bfs A,ans;
bfs operator*(const bfs &x,const bfs &y){
bfs a;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a.c[i][j]=0;
}
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a.c[i][j]+=x.c[i][k]*y.c[k][j]%mo;
a.c[i][j]%=mo;
}
}
}
return a;
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>A.c[i][j];
}
}
//memset(ans.c,1,sizeof(ans.c));
for(int i=1;i<=n;i++)ans.c[i][i]=1;
for(;k;k>>=1){
if(k&1)ans=ans*A;
A=A*A;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<ans.c[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
3.矩阵乘法加速递推
例题三、斐波那契数列
给定 \(n\) ,求斐波那契数列的第 \(n\) 项 \(mod\,10^9+7\) 的值,其中 \(n\le 2^{63}-1\) 。
本题的弱化版是一个经典的递推问题,但是本题的 \(n\) 过大,尽管如此,本题使用递推求解还是很稳定。
考虑矩阵优化,推出 \(fib_n\) 只需要知道 \(fib_{n-1}\) 以及 \(fib_{n-2}\) 。
因此,定义数组 \(a_{2,2}\) 为:
再定义数组 \(f_2\) 为:
将 \(f\times a^{n}\) 得:
只需快速幂计算上述式子即可,
时间复杂度 \(O(2^3log\,n)\) 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mo=1e9+7;
ll a[3][3]={{0,0,0},{0,0,1},{0,1,1}},f[3]={0,0,1},n;
void mul(){
ll c[3]={0};
for(int j=1;j<=2;j++){
for(int k=1;k<=2;++k){
c[j]=(c[j]+f[k]*a[k][j]%mo)%mo;
}
}
memcpy(f,c,sizeof(c));
}
void mul2(){
ll c[3][3]={0};
for(int i=1;i<=2;i++){
for(int j=1;j<=2;j++){
for(int k=1;k<=2;++k){
c[i][j]=(c[i][j]+a[i][k]*a[k][j]%mo)%mo;
}
}
}
memcpy(a,c,sizeof(c));
}
int main(){
cin>>n;
for(;n;n>>=1){
if(n&1) mul();
mul2();
}
printf("%lld\n",f[1]);
return 0;
}
一般来说,若一个问题满足下述条件:
-
可抽象成一个长度为 \(n\) 的一维向量。
-
变化形式属于线性递推。
-
递推轮数长,但 \(n\) 本身不大。
就能考虑使用矩阵乘法优化。