我是上网自学了很久,看了那么多博文还是看不懂...
然鹅 天无绝人之路 今天受 wudongchao 大佬 点拨 终于有所顿悟
带着自学无果的痛苦,我觉得写一篇学习笔记帮助和我一样蠢萌痛苦的人(其实基本就是总结摘抄)
矩阵/矩阵乘法/矩阵快速幂/优化递推
的通俗版内容(专业定义啊术语啥的我也不会)
本文大量盗取图片和内容,链接都附进去了.
由于本人懒,麻烦各位dalao看到的话私我上车补票(版权意识强的dalao请轻喷)
矩阵
矩阵(Matrix)是把一堆数字填在矩形表格中(所以叫矩阵?),就像:
这是个m行n列的矩阵,这m×n 个数称为矩阵A的元素,简称为元,数aij位于矩阵A的第i行第j列,称为矩阵A的(i,j)元,以数 aij为(i,j)元的矩阵可记为(aij)或(aij)m × n,m×n矩阵A也记作Amn。
然后顺带提一下矩阵加法和减法,就对应元素加减就完事了...
矩阵乘法
矩阵乘法符合实数乘法的所有运算律 除了交换律 原因可以从运算方式看出
为了方便,我将三个矩阵如此区分:左边的因数矩阵叫作矩阵L (for left) 右边的因数矩阵叫作矩阵R (for right) 作为乘积的答案矩阵叫做矩阵A (for answer)
作为结果的矩阵A中的 (i,j)元 是左矩阵的第i行 和 右矩阵的第j列 对应元素分别相乘的和 (比较绕口: 对应元素的积的和)
左行右列
例:结果矩阵的(1,1)元=1*7+2*9+3*11=58
同理 具体每个元素如下:
容易得出,只有在左矩阵的列数和右矩阵的行数相等时,算式才有意义
所以矩阵乘法要求三个个矩阵形如:
Lxy * Ryz = Axz
结果矩阵的行列数取决于因数矩阵
矩阵乘法和矩阵快速幂
虽然挺简单的 但还是附一下矩阵乘法cpp代码
1 void _mat_mul(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int z) { 2 //L[x][y] * R[y][z] = A[x][z] 3 long long cmp[LEN][LEN] = {0}; 4 for (int i = 1; i <= x; ++i) 5 for (int j = 1; j <= z; ++j) 6 for (int k = 1; k <= y; ++k) 7 cmp[i][j] = (cmp[i][j] + L[i][k] * R[k][j]) % MOD; 8 for (int i = 1; i <= x; ++i)for (int j = 1; j <= z; ++j)A[i][j] = cmp[i][j]; 9 }
而矩阵快速幂只是在快速幂模板中 将乘法部分换成矩阵乘法(我调用了上面那个函数)
1 void _mat_mi(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int mi) { 2 // L[x][y] * R[y][y]^mi = A[x][y] 3 int cmp[LEN][LEN]; 4 for (int i = 1; i <= x; ++i)for (int j = 1; j <= y; ++j)cmp[i][j] = L[i][j]; 5 while (mi > 0) { 6 if (mi & 1) 7 _mat_mul(cmp, Mat, cmp, x, y, y); 8 mi >>= 1; 9 _mat_mul(R, R, R, y, y, y); 10 } 11 for (int i = 1; i <= x; ++i)for (int j = 1; j <= y; ++j)A[i][j] = cmp[i][j]; 12 }
矩阵快速幂优化递推
Fibonacci
终于来了!!! 虽然我也没学太好
总之慢慢写吧,欢迎交流
我们的目的是用乘法(快速幂)来优化加法
(不知道减法行不行?是不是系数写成负数就行了?感觉好像是的)
首先我们得有一个递推式
以经典的斐波那契数列为例
F(i) = F(i-1) +F(i-2)
我们把相邻的两项写在一行(其实一列也行)
像这样:
然后我们再构建一个能够自己乘自己很多次的矩阵
不难发现,符合这样条件的矩阵必须是"正方形"矩阵(好吧我好好说话:行数和列数一样的矩阵)
同时要能和我们上面列出来的矩阵相乘,
所以在这题中 必须是一个2x2的矩阵(想一想为什么不是1x1)
然后我们期望的 乘积矩阵 需要包含递推式的下一项,还要和我们前面构出来的矩阵长得差不多
原因是我们要反复类乘
比如:
根据 矩阵乘法的要求 我们的算式应该长这样
将F[i] 写成 F[i-1] + F[i-2] 后, 我们 根据矩阵乘法的运算法则 在矩阵中填上系数
因为1 x F[i-1] + 1 x F[i-2] = F[i-1] + F[i-2] = F[i] (对应两项相乘的和)
所以我们填成这样:
同理,我们可以把剩下的空格填完
( 1 x F[i-1] + 0 x F[i-2] = F[i-1] )
所以整个递推式是长这样的:
由于我们的 矩阵L 和 矩阵A 长得差不多,都可以写成
所以我们可以用这个式子递推
然鹅,我们用了矩阵这么高级的东西,可不只是把一个加法递推式变成一个乘法递推式而已
设想一下:我们已知 F[2] 和 F[1] 如果我们要得到 F[N] 是不是需要把矩阵
乘很多次矩阵
所以我们容易得到
然后再套矩阵快速幂就好了
fib的矩阵快速幂源码
1 #define LEN 100 2 #define N 123 3 #define MOD 10000 4 // #define int long long 5 /** 6 * LEN 矩阵边长 7 * N 第N项 8 * MOD 模数(因为太大会爆负) 9 */ 10 11 void _mat_mul(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int z); 12 void _mat_mi(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int mi); 13 14 int Fib[LEN][LEN], Mat[LEN][LEN] ; 15 16 signed main() { 17 18 Fib[1][1] = Fib[1][2] = 1; 19 20 Mat[1][1] = 1; Mat[1][2] = 1; 21 Mat[2][1] = 1; Mat[2][2] = 0; 22 23 if (N > 2) 24 _mat_mi(Fib, Mat, Fib, 1, 2, N - 2); 25 26 cout << Fib[1][1]; 27 28 return 0; 29 30 }