矩阵乘法及应用(矩阵快速幂 等)

有关矩阵的深度学习,可见工程数学线性代数 。这里只简单说明一下,所谓矩阵,可看做把一堆数按一定规律写在一起的集合。

矩阵运算规则与性质

矩阵乘法:

对于一个n行q列的矩阵A和一个q行m列的矩阵B,A*B=C,C是一个n行m列的矩阵,且结果矩阵C第i行与第j列交叉位置的那个值,等于第一个矩阵A第i行与第二个矩阵B第j列,对应位置的每个值的乘积之和,即:Cij =Σaik*bkj(1<=i<=n,1<=j<=m,1<=k<=q)。有定义得,若矩阵A、B做A*B的乘法,则A的列数必须等于B的行数。且若A*B=C成立, B*A=C不一定成立,即:

    矩阵乘法不满足交换律,但满足结合律。

代码:

const int N=100;  
int c[N][N];  //c是最终的矩阵
void multi(int a[][N],int b[][N],int n)  
{  
    memset(c,0,sizeof c);  
    for(int i=1;i<=n;i++)  
        for(int j=1;j<=n;j++)  
          for(int k=1;k<=n;k++)  
            c[i][j]+=a[i][k]*b[k][j];  
} 

若调整一下for的ijk顺序,调为ikj,实际运行效率会比上文代码快3倍左右,这里涉及到计算机原理的一些知识。

优化代码:

const int N=100;  
int c[N][N];  //c是最终的矩阵
void multi(int a[][N],int b[][N],int n)  
{  
    memset(c,0,sizeof c);  
    for(int i=1;i<=n;i++)  
        for(int k=1;k<=n;k++)  
            for(int j=1;j<=n;j++)  
            c[i][j]+=a[i][k]*b[k][j];  
}     

简单来说,计算机里有一种叫高速缓存的东西,调用高速缓存中元素的时间花销远小于向内存查询元素,调用数组元素[i][j]时,它会把紧挨该数组元素之后的一些元素[i][j+1]...[i][j+k]放入高速缓存里,若下一个调用的数组元素正好在缓存里,则时间花销会很小;否则会花费更多的时间去在内存中寻找要调用的元素。简而言之,为了优化效率,对数组元素进行遍历时尽量按顺序遍历。整体越有序,时间效率就越高。

再说明为什么可以调整ijk的顺序。c[i][j]的结果实际上是所有a[i][k]*b[k][j]结果的积累值。不管ijk的顺序如何,都会将三元组(i,j,k)(1<=i<=n,1<=j<=m,1<=k<=q)都遍历一遍,即所有a[i][k]*b[k][j]的计算都被且只被计算了一次,那么最终的积累值就不会改变,故ijk的顺序不会影响答案的正确性。

口胡:两矩阵AB相乘,结果矩阵C的行数是第一个矩阵A的,列数是第二个矩阵B的,A的列数k要与B的行数相等,C的第i行j列的值为A矩阵第i行依次乘B矩阵第j列的积的和(也是A的列数要与B的行数相等的原因,要对应),也可见k等于多少都可以,两个狭窄的小矩阵(10*1 和 1*11 )乘起来也能是大矩阵(10*11),破除一下初学时不对的感觉/印象吧。

矩阵快速幂:

  数的快速幂可将O(n)的计算次数降低到O(logn)的规模,单次矩阵乘法的计算复杂度为O(n^3) n为矩阵大小规模。对于一个大小规模为n的矩阵求它的k次幂,暴力计算复杂度为O(n^3 k)。运用快速幂的知识,将矩阵作为快速幂的底数,可将复杂度降到O( n^3 log k)。代码方面只需在快速幂代码的基础上重载一下运算符就好。

应用:

  矩阵快速幂加速线性递推:

    例题见矩阵快速幂和矩阵乘法

    核心思想:对于一个有限的一次线性递推式(形如f(x)=a1*f(x-1)+a2*f(x-2)+...+ai*f(x-i)+b,例如斐波那契数列的递推式),总可以写成矩阵的形式。最坏的情况下,也存在这样一个写法:存结果矩阵U为一个1行i+1列的矩阵:[f(x)   f(x-1)  ……   f(x-i+1)   b],转移矩阵E(要进行快速幂的矩阵)为一个i+1行、i+1列的矩阵,如图的由a1~ai和若干0 、1组成的矩阵:

  

 

 

     这样U*E得到的矩阵和U大小一样,且对应位置的f项的下标都加了1,便是递推了一层,再配合快速幂,快速递推到首项为f(n)的矩阵,即求得答案。

  矩阵乘法和方程组之间的转化关系:

    方程组可用矩阵表示,例如:

    

 

    方程组数目可以更多,只要左矩阵列数等于右矩阵行数,结果矩阵行、列数等于左。右矩阵。

  矩阵乘法加速递推:

    其实不止线性递推式,只要能写出结果矩阵和转移矩阵,任何递推都能用矩阵加速。如:洛谷P3990[SHOI2013]超级跳马 。尽管递推式是二维的,但能写出来矩阵(不限于一次推一项),就能用矩阵加速做出来。详情可见: 洛谷P3990[SHOI2013]超级跳马——思维笔记  (怎么写矩阵的典例)

   *怎么写矩阵:结果矩阵要包括用的所有项,最好都写到一行或一列,方便根据矩阵乘法写转移矩阵。左矩阵管结果矩阵行,右矩阵管列。乘法中用的结果矩阵与求出的结果矩阵大小要一样,还要给转移矩阵留出空间。左矩阵乘的时候看一行中所有元素,右矩阵则看一列中所有元素,知道这一条,再写矩阵就方便多了。结果矩阵当左矩阵,是一横行;当右矩阵,是一竖列。乘完后的结果矩阵的每个项都要变成递推一层后的结果。矩阵推一次可以推很多个项,不止局限于一项,还可能是一列、两列,一个整体的奇怪的部分等。

 

    

 

posted @ 2021-01-25 11:25  千叶繁华  阅读(900)  评论(0编辑  收藏  举报