学习笔记 #3:矩阵加速
Matrix 好闪,拜谢 Matrix!
yysy,矩阵真的很戳我的xp。
引言
有的时候我们需要快速地递推,显然一位一位地递推,推到 \(1e18\) 这么多次肯定一下就超时了,这时我们就可以进行矩阵加速,具体就是借助矩阵乘法的结合律来辅助我们递推。
矩阵加速适用于求方案数、答案确定的数值的递推,不适用于求最值的递推,求最值用斜率优化就好了。
前置知识
- 快速幂
- 递推思想
- 初中数学知识
概念
先来一个斐波那契数列:
\(F_n = \left\{\begin{aligned} 1 \space (n \le 2) \\ F_{n-1}+F_{n-2} \space (n\ge 3) \end{aligned}\right.\)
求 \(F_n \bmod 10^9 + 7\) 的值。
数据范围大约 \(1e18\) ,一位一位递推显然不行,而矩阵乘法可以为我们提供思路:
先讲矩阵乘法:
\(A=\begin{bmatrix}a_1&a_2&a_3\\a_4&a_5&a_6\\a_7&a_8&a_9\end{bmatrix}\)
\(B=\begin{bmatrix}b_1&b_2&b_3\\b_4&b_5&b_6\\b_7&b_8&b_9\end{bmatrix}\)
\(A*B=\begin{bmatrix}a_1b_1+a_2b_4+a_3b_7&a_1b_2+a_2b_5+a_3b_8&a_1b_3+a_2b_6+a_3b_9\\a_4b_1+a_5b_4+a_6b_7&a_4b_2+a_5b_5+a_6b_8&a_4b_3+a_5b_6+a_6b_9\\a_7b_1+a_8b_4+a_9b_7&a_7b_2+a_8b_5+a_9b_8&a_7b_3+a_8b_6+a_9b_9\end{bmatrix}\)
有点晕?不急!我们慢慢来看:
我们先将 \(A\) 的第一列抽出:
\(\begin{bmatrix}a_1&a_2&a_3\end{bmatrix}\)
然后顺时针旋转 \(90°\):
\(\begin{bmatrix}a_1\\a_2\\a_3\end{bmatrix}^T\)
然后与 \(B\) 的第一列相乘:
\(\begin{bmatrix}a_1b_1\\a_2b_4\\a_3b_7\end{bmatrix}^T\)
将他们加起来,一看,就是新矩阵第一行第一列的值!
多试几次,便能得出结论:新矩阵的第 \(i\) 行 \(j\) 列,就是将 \(A\) 的第 \(i\) 行顺时针旋转 \(90°\),对应与 \(B\) 的第 \(j\) 列相乘,最后加起来的值。
容易发现:新矩阵的行数与 \(A\) 相等,列数与 \(B\) 相等,且只有 \(A\) 的列数与 \(B\) 的行数相等时才能进行相乘。
而进行推广,若矩阵是 \(n \times n\) 的,就可以进行自乘,进而可以推广出矩阵 \(N\) 的 \(k\) 次方 \(N^k\),即矩阵的幂!
而可以证得,矩阵的乘法虽然没有交换律,却有结合律,因此可以进行先后顺序的调换,那么快速幂的思想就可以得到应用了,即:
矩阵快速幂。
现在回到斐波那契数列上:
首先我们要有一个目标矩阵:
\(\begin{bmatrix}f_{n-1}&f_{n}\end{bmatrix}\)
它的上一个矩阵是
\(\begin{bmatrix}f_{n-2}&f_{n-1}\end{bmatrix}\)
怎么推过来呢?很简单:
\(\begin{bmatrix}f_{n-2}&f_{n-1}\end{bmatrix}\times\begin{bmatrix}0&1\\1&1\end{bmatrix}=\begin{bmatrix}f_{n-1}&f_{n}\end{bmatrix}\)
那么我们要求第 \(n\) 位,显然求出 \(\begin{bmatrix}f_1&f_2\end{bmatrix}\times\begin{bmatrix}0&1\\1&1\end{bmatrix}^{n-1}\) 就可以了,\(ans\) 就是目标矩阵的第一位。
现在我们的关键,就落到了求 \(\begin{bmatrix}0&1\\1&1\end{bmatrix}^k\) 上。
很显然,根据矩阵乘法具有的结合律,我们可以用快速幂的方式去求。
还有,补充一下单位矩阵:左上至右下的对角线都为 \(1\) 的矩阵,同大小的矩阵乘上它都是原矩阵:
\(\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}\)
这是一个边长为 \(4\) 的单位矩阵,相当于乘法里的 \(1\)。
实现
思路讲完了,接下来是代码实现:
矩阵乘法:
matrix operator * (const matrix &nl) {
matrix res; res.r = r; res.c = nl.c;
for(int k = 1; k <= c; k++) for(int i = 1; i <= res.r; i++) for(int j = 1; j <= res.c; j++) res.f[i][j] += f[i][k] * nl.f[k][j];
return res;
}
矩阵快速幂(就跟正常快速幂一样)
matrix unit() {
matrix res;
res.r = r, res.c = c;
for(int i = 1; i <= r; i++) res.f[i][i] = 1;
return res;
}//单位矩阵
matrix Mqpow(matrix a, int b) {
matrix ans = a.unit();
for(; b; b >>= 1) {
if(b & 1) ans = ans * a;
a = a * a;
}
return ans;
}
讲完了?对!就这么简单!去写几道例题实践一下吧~