矩阵快速幂
矩阵快速幂
一类经常出现在优化问题中的数学方法
快速幂
给定 \(a,b,p\) , 求出 \(a^{b} \mod {p}\) 的值
其中\(1≤a,b,p≤2×10^9\)
对于这种求极大数的乘方问题,我们都利用快速幂来解决
设\(b = \sum{b_i}\)
可以得到 \(a^{b} = a^{\sum{b_i}} = \prod{a^{b_i}}\)
对于\(b\)的拆分我们使用二进制拆位的思想,这样\(b\)会拆成\(2^{x_0} + 2^{x_1} + ... + 2^{x^n}\)的形式
那么对于计算有什么好处呢
对于连续的\(x_1 = x_0 + 1\)而言,\(a^{2^{x_1}} = a^{2^{x_0 + 1}} = a^{2 * 2^{x_0}} = (a^{2^{x_0}})^2\)
这样我们就可以迭代了
a = a * a;
\(n\)次幂的计算时间复杂度由\(O(n)\)降至\(O(logn)\)
LL qmi(int a ,int b ,int p)
{
LL res = 1 % p;
while(b)
{
if(b & 1) res = res * a % p;
a = (LL) a * a % p;
b >>= 1;
}
return res;
}
然后是矩阵快速幂的问题
我们知道幂运算不只是对实数域有用,复数域,矩阵同样适用
对于\(A^k\)的计算,优化思路是相同的,对于\(k\)进行二进制分解,只不过乘法面对的不再是数字,而是矩阵
所以运算符重载和定义乘法函数均可
void mul(int c[][N] , int a[][N] , int b[][N]) // c = a * b
{
static int t[N][N];
memset(t , 0 , sizeof t);
for(int i = 0 ; i < N ; i ++)
for(int j = 0 ; j < N ; j ++)
for(int k = 0 ; k < N ; k ++)
t[i][j] = (t[i][j] + (LL) a[i][k] * b[k][j]) % p;
memcpy(t , c , sizeof t);
}
void qmi(int a[][N] , int n)
{
int res[N][N];
for(int i = 0 ; i < N ; i ++)
res[i][i] = 1;
while (n)
{
if (n & 1) mul(res, res, w);
mul(w, w, w);
n >>= 1;
}
}
然后就是针对实际问题了
比如求\(Fibonacci\)数列
我们知道\(Fibonacci\)的递推式
\(f_n = f_{n - 1} + f_{n - 2}\)
那我们构造一个向量\(F_n = \begin{bmatrix} f_{n} & f_{n + 1} \end{bmatrix}\)
可知\(F_{n + 1} = \begin{bmatrix} f_{n + 1} & f_{n + 2} \end{bmatrix}\)
那么 \(F_{n + 1} = \begin{bmatrix} f_{n + 1} & f_{n} + f_{n + 1} \end{bmatrix}\)
改写成矩阵形式\(F_{n + 1} = \begin{bmatrix} f_{n} & f_{n + 1} \end{bmatrix} \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\)
即\(F_{n + 1} = F_n \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\)
可得\(F_{n} = F_0 \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}^n\)
剩下就可以交给快速幂了
这样由原来的\(O(n)\) 优化到了\(O(logn)\)