矩阵的应用
一、用途
矩阵的一个重要的用途是进行递归是的计算,最明显的就是快速求数列的某一项的值。本文也是主要讲解这种算法的。
二、样例
这方面最简单的就是斐波那契问题了,这个相信是每一位程序员都熟知的,这里就不介绍了。
三、快速幂
既然是快速计算那肯定是不能去一步一步慢慢求,这里我们要用到二分的思想。求快速幂。在讲解矩阵中如何使用快速幂之前我们先讲解一下如何在计算普通的幂的时候使用快速幂:
比如我们要计算2^100,按照普通的做法我们是进行100次的乘法,当然是很慢的。这是我们可以采用二分的思想,这是我们就可以想要得到这个我们可以是2^50*2^50,然后又可以分成四个2^25……这样一直继续下去最终在将时间复杂度降低到 O(log₂N)。首先介绍一种比较朴素的快速幂实现版本。线上一段代码:
1 long long quickpow(long long m,long long n,int k)//取余 2 { 3 long long b = 1; 4 while (n > 0) 5 { 6 if (n & 1) 7 b = (b*m)%k; 8 n = n >> 1 ; 9 m = (m*m)%k; 10 } 11 return b; 12 }
大家可以看到这段代码的思路很简单,就是二分的思想。代码虽少却大大加快了运算的速度。
四、矩阵快速幂
矩阵快速幂的思路和一般整数是相同的,就像我们会把矩阵的运算类比到数的运算,这里面的也是一样,你只需要重载一下运算,其他的就和上面的二分快速幂完全一样了。在详细讲解之前我们先看一段实际代码:
1 const int MAX = 3; 2 typedef struct 3 { 4 int m[MAX][MAX]; 5 }Matrix; 6 Matrix P={5,-7,4, 7 1,0,0, 8 0,1,0, 9 }; 10 Matrix I={1,0,0, 11 0,1,0, 12 0,0,1, 13 }; 14 Matrix matrixmul(Matrix a,Matrix b) //矩阵乘法 15 { 16 int i,j,k; 17 Matrix c; 18 for(i=0;i<MAX;i++) 19 for (j = 0; j < MAX;j++) 20 { 21 c.m[i][j] = 0; 22 for (k = 0; k < MAX; k++) 23 c.m[i][j] += (a.m[i][k] * b.m[k][j])%9997; 24 c.m[i][j] %= 9997; 25 } 26 return c; 27 } 28 Matrix quickpow(long long n) 29 { 30 Matrix m=P,b=I; 31 while(n>=1) 32 { 33 if(n&1) 34 b=matrixmul(b,m); 35 n=n>>1; 36 m=matrixmul(m,m); 37 } 38 return b; 39 }
从这段代码中我们可以看出矩阵的快速幂与普通二分很相似,他们的不同就是数字的运算换成了矩阵的运算,说的详细点就是说:
1、数字二分中的1换成了单位矩阵;
2、普通算数运算换成了矩阵运算。
其他部分还基本是相同的。
五、用矩阵解决斐波那契问题以及浅谈矩阵求解递归的推到
当然这个矩阵的快速幂不仅运算很精巧,更加关键的是矩阵的用途很广泛(关于算法的Matrix67好像有篇列了十多种应用了,可以百度之。。。)这里我就斐波那契问题的矩阵解法为例简单介绍一下:
我们知道斐波那契的递归表示为
f(n)=1 (n=0,n=1)
f(n)=f(n-1)+f(n-2) (n>=2)
一般的方法都是dp或者公式法求解,效率一般,如果我们用矩阵求解就会发现速度的到了提升。那么究竟怎么把矩阵和一个递归式联系起来呢。这一点在网上也有一些资料。但是我个人觉得如果不是要用高次矩阵的话,完全没必要去按那些晕晕的公式求解。掌握一点门路之后按配凑就能很快的出结论。
我们的思路就是上面的第二个式子要能够在一个矩阵的乘式中体现出来,回忆一下矩阵乘法(弄图太麻烦了,直接扒了一张)
于是乎对照这看看我们觉得二位数组肯定就能解决这个递归式了,尝试着我们不难的到
到这就基本完了,余下的就是你要求那个就把后面的矩阵用快速幂连乘多少就好了,复杂度O(logn)(貌似是最快的方法求斐波那契了,直接村起来除外。。。),代码就省了。