线性代数学习笔记
矩阵(Matrix)
矩阵简介及矩阵加速
简介
在数学中,矩阵是一个按照长方阵列排列的复数或实数集合,最早来自于方程组的系数及常数所构成的方阵——百度百科
通俗的来讲,把集合里的一些数填入到一个矩形中即得到一个矩阵
定义
由$m\times n$个数$a_{i,j}$排成的数表称为$m$行$n$列的矩阵简称$m\times n$矩阵。
$$
A=\begin{bmatrix}a_{1,1}&a_{1,2}&...&a_{1,n}\a_{2,1}&a_{2,2}&...&a_{2,n}\...&...&...&...\a_{m,1}&a_{m,2}&...&a_{m,n}\end{bmatrix}
$$
这$n\times m$个数称为矩阵$A$的元素,简称为元。。。(剩下的都是百度百科的废话
有$m$行$n$列的矩阵也记作$A_{mn}$
特别的,两个$n,m$都相同的矩阵称为同型矩阵
$n=m$的矩阵称为$n$阶矩阵或者$n$阶方阵
基本运算
加法
$$
\begin{bmatrix}a_{1,1}&...&a_{1,n}\...&...&...\a_{m,1}&...&a_{m,n}\end{bmatrix}+\begin{bmatrix}b_{1,1}&...&b_{1,n}\...&...&...\b_{m,1}&...&b_{m,n}\end{bmatrix}=\begin{bmatrix}a_{1,1}+b_{1,1}&...&a_{1,n}+b_{1,n}\...&...&...\a_{m,1}+b_{m,1}&...&a_{m,n}+b_{m,n}\end{bmatrix}
$$
同时,矩阵的加法和实数的加法一样,满足交换律和结合律
即
$$
A+B=B+A
$$
$$
(A+B)+C=A+(B+C)
$$
应当注意,只有同型矩阵之间才可以进行加法
减法
在数集中,减法作为加法的逆运算
在矩阵中也是一样的
$$
\begin{bmatrix}a_{1,1}&...&a_{1,n}\...&...&...\a_{m,1}&...&a_{m,n}\end{bmatrix}-\begin{bmatrix}b_{1,1}&...&b_{1,n}\...&...&...\b_{m,1}&...&b_{m,n}\end{bmatrix}=\begin{bmatrix}a_{1,1}-b_{1,1}&...&a_{1,n}-b_{1,n}\...&...&...\a_{m,1}-b_{m,1}&...&a_{m,n}-b_{m,n}\end{bmatrix}
$$
它也满足在实数集上的规律
数乘
在矩阵中引入了数乘的概念,即为一个数乘一个矩阵
$$
\mu·\begin{bmatrix}a_{1,1}&...&a_{1,n}\...&...&...\a_{m,1}&...&a_{m,n}\end{bmatrix}=\begin{bmatrix}\mu· a_{1,1}&...&\mu· a_{1,n}\...&...&...\\mu· a_{m,1}&...&\mu· a_{m,n}\end{bmatrix}
$$
矩阵的数乘满足以下规律:
$$
\lambda(\mu A)=\mu(\lambda A)
$$
$$
\lambda(\mu A)=(\lambda\mu A)
$$
$$
(\lambda +\mu)A=\lambda A+\mu A
$$
$$
\lambda(A+B)=\lambda A+\lambda B
$$
乘法
两个矩阵的乘法仅当第一个矩阵$A$的列数和另一个矩阵$B$的行数相等时才能定义。
记作
$$
A_{mn} B_{np}=C_{mp}
$$
也作
$$
AB=C
$$
$C$的一个元素$c_{i,j}$的值为
$$
c_{i,j}=\sum_{k=1}^na_{i,k}\times b_{k,j}
$$
例如
$$
\begin{bmatrix}1 &0 &2\-1&3&1\end{bmatrix}\times\begin{bmatrix}3&1\2&1\1&0\end{bmatrix}=\begin{bmatrix}(1\times 3+0\times2+2\times1)&(1\times1+0\times1+2\times0)\(-1\times3+3\times2+1\times1)&(-1\times1+3\times1+1\times0)\end{bmatrix}=\begin{bmatrix}5&1\4&2\end{bmatrix}
$$
同时,矩阵的乘法满足以下运算律:
结合律:
$$
(AB)C=A(BC)
$$
左分配律:
$$
(A+B)C=AC+BC
$$
右分配律:
$$
C(A+B)=CA+CB
$$
矩阵加速
快速幂
这个还需要多说么?
int base=a,ans=1;
while(b>0){
if(b&1){
ans*=base
ans%=c;
}
base*=base
base%=c;
b=b>>1;
}
return ans;
就是很简单的按位运算
所以我们对于一个矩阵的幂也可以这么来运算,于是时间复杂度从$O(n)$降到了$O(log_2n)$
这就很舒服
但是这个有什么用呢?
例题一
a[1]=a[2]=a[3]=1
a[x]=a[x-3]+a[x-1] (x>3)
求a数列的第n项对1000000007(10^9+7)取余的值。
第一眼看到这个题
?
这不是来送分的么
我一项一项去推不就好了???
对于100%的数据 T<=100,n<=2*10^9;
笑容逐渐凝固
所以现在就体现出矩阵加速的用处了
对于一个数列$f(n)=f(n-x)+f(n-y)+...$
我们可以构造出一个$k\times1$的矩阵表示当前的状态,即用$f(x),f(y)...$来表示我们当前已经知道的
然后我们再构造一个转移矩阵,使我们的原始矩阵乘上这个转移矩阵后可以变成一个新的矩阵,得到全新的$f(x)$,从而一步步的向前推进,得到答案
显而易见,由于我们需要用到$f(n-1)$和$f(n-3)$,所以我们需要保留住这两个状态,$f(n-3)$是由上个状态的$f(n-2)$继承过来的,所以我们也需要保留,所以原始矩阵要构造成这个样子
$$
\begin{bmatrix}f(3)\f(2)\f(1)\end{bmatrix}
$$
即为
$$
\begin{bmatrix}1\1\1\end{bmatrix}
$$
然后思考我们怎么由这个矩阵得到下一个矩阵
设我们当前矩阵为
$$
\begin{bmatrix}f(n-1)\f(n-2)\f(n-3)\end{bmatrix}
$$
我们希望得到的矩阵为
$$
\begin{bmatrix}f(n)\f(n-1)\f(n-2)\end{bmatrix}
$$
即为
$$
\begin{bmatrix}f(n-1)\times1+f(n-2)\times 0+f(n-3)\times1\f(n-1)\times1+f(n-2)\times0+f(n-3)\times0\f(n-1)\times0+f(n-2)\times1+f(n-3)\times0\end{bmatrix}
$$
显而易见我们的转移矩阵为
$$
\begin{bmatrix}1&0&1\1&0&0\0&1&0\end{bmatrix}
$$
由于我们原始矩阵的第一项为$f(3)$,所以我们只需要乘$n-3$次这个转移矩阵就可以得到了!
但是问题来了,这样不还是一个$O(n)$的算法么?而且还慢了好多
当然不是!想一下我们矩阵乘法的结合律
我们设原始矩阵为$A$,转移矩阵为$B$,那么我们最终答案的矩阵就是
$$
\underbrace{B(B(B(B(B(B(B...(B}_{n-3}A))))))
$$
根据矩阵乘法的结合律,这个式子可以简化为
$$
B^{n-3}A
$$
到了这里,我们就可以用矩阵快速幂来求出最终答案了!
$O(log_2n)$是真的快(
贴上我的巨丑无比的代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define mod 1000000007
using namespace std;
struct Ju{
long long p[5][5];
Ju operator *(const Ju &a)const{
Ju c;
for(long long i=1;i<=3;i++){
for(long long j=1;j<=3;j++){
c.p[i][j]=0;
}
}
for(long long i=1;i<=3;i++){
for(long long j=1;j<=3;j++){
for(long long k=1;k<=3;k++){
c.p[i][j]+=(a.p[i][k]*p[k][j])%mod;
c.p[i][j]%=mod;
}
}
}
return c;
}
}ans,base;
void build(){
for(long long i=1;i<=3;i++){
for(long long j=1;j<=3;j++){
base.p[i][j]=0;
}
}
base.p[1][1]=base.p[1][3]=base.p[2][1]=base.p[3][2]=1;
ans.p[1][1]=ans.p[2][1]=ans.p[3][1]=1;
}
void qsm(long long p){
while(p!=0){
if(p&1){
ans=ans*base;
}
base=base*base;
p>>=1;
}
}
long long T,n;
int main(){
cin>>T;
for(long long i=1;i<=T;i++){
cin>>n;
if(n<=3){
printf("1\n");
continue;
}
build();
qsm(n-3);
cout<<ans.p[1][1]<<endl;
}
return 0;
}
例题二
让你求斐波那契数列的第n项,数据范围是longlong级别的
很明显的矩阵加速
构造原始矩阵:
$$
\begin{bmatrix}f(2)\f(1)\end{bmatrix}=\begin{bmatrix}1\1\end{bmatrix}
$$
思考答案怎么由上一个状态转移过来
$$
\begin{bmatrix}f(n)\f(n-1)\end{bmatrix}=\begin{bmatrix}f(n-1)\times1+f(n-2)\times1\f(n-1)\times1+f(n-2)\times0\end{bmatrix}
$$
所以转移矩阵
$$
\begin{bmatrix}1&1\1&0\end{bmatrix}
$$
下面就是简单的矩阵快速幂了