复习笔记之矩阵快速幂(不定时更新)
2016-04-17 18:11
337 查看
摘要 初识矩阵快速幂已是很久之前的事情了,我的忘性有那么好,现在自然是忘得一干二净啦,简要写一下矩阵快速幂相关,以备后用.
事情要从很久很久以前说起,我记得那是第一节算法课,老师说了这样一个题目:某人下楼梯可以一次走两阶,也可以一次走一阶,现在他在第 k 阶楼梯,问题是他要走到第0阶有多少种不同的走法?显然,这是fibonacci数列,即:
$$ f(n) = \begin{cases} 1& \text{n=1}\\ 1& \text{n=2}\\ f(n-1) + f(n-2)& \text(n \geq 3) \end{cases} $$
这个问题并不复杂,我们甚至能求出它的通项公式:
$$ \frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^{n}- (\frac{1-\sqrt{5}}{2})^{n}] $$
不过,我们知道斐波那契数列的数字总是整数,这个通项公式并没有显式的表明这是一个整数,而且,即使利用这个通项公式求解 $ f(n) $ 依然要计算至少 $ n $次,反倒不如直接递推!
事实上我们能在 log 级的时间复杂度内解决这个问题。
首先,我们构造矩阵:
$$ \begin{equation} \left| \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right| \end{equation} $$
$$ \begin{equation} \left| \begin{array}{c} f(2) \\ f(1) \end{array} \right| \end{equation} $$
矩阵一和矩阵二相乘会得到一个新的矩阵:
$$ \begin{equation} \left| \begin{array}{c} f(2) + f(1) \\ f(2) \end{array} \right| \end{equation} $$
显然,矩阵三等价于:
$$ \begin{equation} \left| \begin{array}{c} f(3) \\ f(2) \end{array} \right| \end{equation} $$
如果把得到的矩阵三再和矩阵一相乘呢?我们会得到含有f(4)和f(3)的矩阵,依此类推,我们总能得到这样的矩阵:
$$ \begin{equation} \left| \begin{array}{c} f(n) \\ f(n-1) \end{array} \right| \end{equation} $$
记这个矩阵为$ M_{n} $ ,矩阵一为$ M_{0} $,矩阵二为$ M_{2} $则有:
$$ M_{n} = M_{0}*M_{n-1} = M_{0}*M_{0}*M_{n-2} = ... = M_{0}^{n-2}*M_{2} $$
作为一个"伪ACMer"我还是知道快速幂取模这个东西的:
快速幂取模能在log级的时间复杂度内解决 $ A^{n} mod p $的问题, A , p , n 均为正整数
新得到的式子和快速幂取模能解决的问题竟如此相似,事实上是完全一样的,如果你了解快速幂取模的过程的话,唔---还是简要的说一下吧!
代码是这样的:
#define LL long long LL Quick_Mod(LL a,LL k,LL p) { LL ans = 1 ; a %= p ; while (k) { if (k&1) ans = ans*a%p; a = a*a%p ; k = k>>1 ; } return ans ; }
思路是这样的:如果 n 是奇数,那么$ answer = A^{n-1}*A mod p $,如果 n 是偶数,那么$ answer = (A^{2})^{(n/2)} mod p $。这样,就会把时间复杂度降到log级别
咦><整数和矩阵的区别很大么?把快速幂取模代码中的乘号重载成矩阵的乘法不就ok了吗?没错,就是这样!!!现在,已经成功的解决了这个问题!!
顺带说一下昨天BestCoder Round 80中的一个题目:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <cstring> #include <algorithm> using namespace std ; #define LL long long #define rep(i,n) for (int i = 1 ; i <= n ; ++ i) const int n = 3 ; struct Matrix { LL mat[n<<1][n<<1] ; }e; void InitMatrix() { rep(i,n) rep(j,n) e.mat[i][j] = (i == j) ; } Matrix mul(Matrix a,Matrix b,LL Mod) { Matrix c ; rep(i,n) { rep(j,n) { c.mat[i][j] = 0 ; rep(k,n) { c.mat[i][j] += (a.mat[i][k]*b.mat[k][j])%Mod ; while (c.mat[i][j] >= Mod) c.mat[i][j] -= Mod ; } } } return c ; } Matrix Quick_Mod(Matrix a,LL k,LL Mod) { Matrix c = e ; while (k) { if (k&1) c = mul(c,a,Mod) ; a = mul(a,a,Mod) ; k = k/2 ; } return c ; } LL Quick_Mod(LL a,LL k,LL Mod) { LL ans = 1 ; a %= Mod ; while (k) { if (k&1) ans = ans*a%Mod ; a = a*a%Mod ; k = k>>1 ; } return ans ; } int main() { // freopen("in.txt","r",stdin) ; LL N , A , B , C , Mod ; int T ; Matrix c ; InitMatrix() ; scanf("%d",&T) ; while (T --) { scanf("%I64d%I64d%I64d%I64d%I64d",&N,&A,&B,&C,&Mod) ; if (A%Mod == 0) { if (N == 1) puts("1") ; else puts("0") ; continue ; } if (N < 3) { if (N == 1) printf("1\n") ; else printf("%I64d\n",Quick_Mod(A,B,Mod)) ; continue ; } memset(c.mat,0,sizeof(c.mat)) ; c.mat[1][1] = C ; c.mat[1][2] = c.mat[1][3] = c.mat[2][1] = c.mat[3][3] = 1 ; c = Quick_Mod(c,N-2,Mod-1) ; LL k = c.mat[1][1] + c.mat[1][3] ; k %= (Mod-1) ; k = k*B%(Mod - 1) ; printf("%I64d\n",Quick_Mod(A,k,Mod)) ; } return 0 ; }
HDU 5667
待学习链接:
matrix67的 十个利用矩阵乘法解决的经典题目
~end~
相关文章推荐
- char*,string,char a[], const char *,之间的转换
- java程序员面试中的多线程问题
- Android 多状态按钮ToggleButton
- 根据字节码探讨java自增运算符的原理
- 数据库范式通俗解释
- Caffe实战系列:如何将CRFAsRNN移植到caffe-windows上去
- Android-Tips(实用Android开发技巧)
- 十五、图的算法之无向图
- Activity生命周期
- linux 软件安装提示错误
- 立项说明书之可行性分析
- Matlab隐函数求导作图
- 遍历list集合删除指定元素方法
- 南京理工大学第八届程序设计大赛(校外镜像)
- jQuery选择器探究:语法汇总
- Java中的多态
- 20145110《Java程序设计》第七周学习总结
- mongo入门--java连接mongo数据库
- 2016-4-11
- python随机数计算并输出