浅谈矩阵快速幂的那些事
来源:互联网 发布:linux安全狗卸载 编辑:程序博客网 时间:2024/05/18 01:47
矩阵的快速幂是用来高效地计算矩阵的高次方的。将朴素的o(n)的时间复杂度,降到log(n)。
这里先对原理(主要运用了矩阵乘法的结合律)做下简单形象的介绍:
一般一个矩阵的n次方,我们会通过连乘n-1次来得到它的n次幂。
但做下简单的改进就能减少连乘的次数,方法如下:
把n个矩阵进行两两分组,比如:A*A*A*A*A*A => (A*A)(A*A)(A*A)
这样变的好处是,你只需要计算一次A*A,然后将结果(A*A)连乘自己两次就能得到A^6,即(A*A)^3=A^6。算一下发现这次一共乘了3次,少于原来的5次。
其实大家还可以取A^3作为一个基本单位。原理都一样:利用矩阵乘法的结合律,来减少重复计算的次数。
以上都是取一个具体的数来作为最小单位的长度,这样做虽然能够改进效率,但缺陷也是很明显的,取个极限的例子(可能有点不恰当,但基本能说明问题),当n无穷大的时候,你现在所取的长度其实和1没什么区别。所以就需要我们找到一种与n增长速度”相适应“的”单位长度“,那这个长度到底怎么去取呢???这点是我们要思考的问题。
有了以上的知识,我们现在再来看看,到底怎么迅速地求得矩阵的N次幂。
既然要减少重复计算,那么就要充分利用现有的计算结果咯!~怎么充分利用计算结果呢???这里考虑二分的思想。。
大家首先要认识到这一点:任何一个整数N,都能用二进制来表示。。这点大家都应该知道,但其中的内涵真的很深很深(这点笔者感触很深,在文章的最后,我将谈谈我对的感想)!!
计算机处理的是离散的信息,都是以0,1来作为信号的处理的。可想而知二进制在计算机上起着举足轻重的地位。它能将模拟信号转化成数字信号,将原来连续的实际模型,用一个离散的算法模型来解决。 好了,扯得有点多了,不过相信这写对下面的讲解还是有用的。
回头看看矩阵的快速幂问题,我们是不是也能把它离散化呢?比如A^19 => (A^16)(A^2)(A^1),显然采取这样的方式计算时因子数将是log(n)级别的(原来的因子数是n),不仅这样,因子间也是存在某种联系的,比如A^4能通过(A^2)(A^2)得到,A^8又能通过(A^4)(A^4)得到,这点也充分利用了现有的结果作为有利条件。下面举个例子进行说明:
现在要求A^156,而156(10)=10011100(2)
也就有A^156=>(A^4)(A^8)(A^16)*(A^128) 考虑到因子间的联系,我们从二进制10011100中的最右端开始计算到最左端。细节就说到这,下面给核心代码:
while(N) { if(N&1) res=res*A; n>>=1; A=A*A; }
里面的乘号,是矩阵乘的运算,res是结果矩阵。
第3行代码每进行一次,二进制数就少了最后面的一个1。二进制数有多少个1就第3行代码就执行多少次。
好吧,矩阵快速幂的讲解就到这里吧。在文章我最后给出我实现快速幂的具体代码(代码以3*3的矩阵为例)。
现在我就说下我对二进制的感想吧:
我们在做很多”连续“的问题的时候都会用到二进制将他们离散简化
1.多重背包问题
2.树状数组
3.状态压缩DP
……………还有很多。。。究其根本还是那句话:化连续为离散。。很多时候我们并不是为了解决一个问题而使用二进制,更多是时候是为了优化而使用它。所以如果你想让你的程序更加能适应大数据的情况,那么学习学习二进制及其算法思想将会对你有很大帮助。
代码如下:
#include <cstdlib>#include <cstring>#include <cstdio>#include <iostream> using namespace std;int N;struct matrix{ int a[3][3];}origin,res;matrix multiply(matrix x,matrix y){ matrix temp; memset(temp.a,0,sizeof(temp.a)); for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { for(int k=0;k<3;k++) { temp.a[i][j]+=x.a[i][k]*y.a[k][j]; } } } return temp;}void init(){ printf("随机数组如下:\n"); for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { origin.a[i][j]=rand()%10; printf("%8d",origin.a[i][j]); } printf("\n"); }// printf("\n"); memset(res.a,0,sizeof(res.a)); res.a[0][0]=res.a[1][1]=res.a[2][2]=1; //将res.a初始化为单位矩阵 }void calc(int n){ while(n) { if(n&1) res=multiply(res,origin); n>>=1; origin=multiply(origin,origin); } printf("%d次幂结果如下:\n",n); for(int i=0;i<3;i++) { for(int j=0;j<3;j++) printf("%8d",res.a[i][j]); printf("\n"); } printf("\n");}int main(){ while(scanf("%d",&N) { init(); calc(N); } return 0;}
三维的矩阵快速幂,适用于所有的快速幂,这里还有二维的矩阵,作用类似三维。但是在单位矩阵赋值的时候,必须赋成一样的数,造成的结果是只可以计算2的倍数的快速幂。(为什么呢?在二维的计算中,矩阵运算的基本算法造成了这一现象)
#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#include<cmath>#include<string>#include<queue>#include<map>using namespace std;const int MOD=10000;struct matrix{ int m[2][2];}base,one;matrix calculate(matrix a,matrix b){ matrix ans; for(int i=0;i<2;++i) for(int j=0;j<2;++j) { ans.m[i][j]=0;//初始赋值0 for(int k=0;k<2;++k) ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j])%MOD; } return ans;}//矩阵乘法运算函数 int fastpow(int n){ one.m[0][0]=one.m[0][1]=one.m[1][0]=1; one.m[1][1]=0; base.m[0][0]=base.m[1][1]=1; base.m[0][1]=base.m[1][0]=0;//base初始化为单位矩阵 while(n!=0) { if(n&1)//末位是否为一 { base=calculate(base,one); }//实现base*=t;其中要先把base赋值给ans,然后用base=ans*t one=calculate(one,one); n=n/2; } return base.m[0][1];}//求矩阵one的n次幂 int main(){ int n; while(true) { scanf("%d",&n); if (n==-1) break; else printf("%d\n",fastpow(n)); } return 0;}
这个快速幂还有些问题,以后更新吧。
2017.4.13
- 浅谈矩阵快速幂的那些事
- 矩阵的快速幂
- 矩阵的快速幂
- 矩阵的快速幂
- 矩阵的快速幂
- 矩阵的快速幂
- 矩阵的快速幂
- 矩阵的快速幂
- 矩阵的快速幂
- 矩阵的快速幂
- 快速排序的那些事
- 矩阵的乘法和矩阵快速幂
- OpenGL矩阵的那些事-1
- 矩阵快速幂---递推题的快速计算
- 快速幂求模+矩阵的快速幂
- 求矩阵的快速幂
- hdu2276(矩阵的快速幂)
- 求矩阵的快速幂
- redirect重定向
- 仿射变换
- 独立成分分析
- 文章标题
- 写论文时有用的指导汇总
- 浅谈矩阵快速幂的那些事
- 用jar命令将Web应用打包成war文件的简单方法
- poj 3295 Tautology (模拟栈操作+状压)
- Linux下的tar压缩解压缩命令详解
- Spring Filter过滤器,Spring拦截未登录用户权限限制
- 第八届蓝桥杯JAVA B组省赛题解
- thinkphp5不安装composer使用验证码
- Android将webp格式图片转换为png格式方法
- 矩阵-DirectX与OpenGL的不同