矩阵快速幂

poj3070
题意:让求斐波那契数列,其n更是高达10亿。
直接递推的局限性:
(1)本题让你递推的斐波那契数n高达10亿。测试时间仅1秒的时间,for循环用递推公式递归导致超时。
(2)想要打表实现随机访问根本不可能,先把斐波那契数列求到10亿,然后想去进行随机访问。题目未给出那么多内存,数组也开不到10亿。




请基于对线性代数的学习后,了解了矩阵的概念及基本运算再继续下面的阅读。

引入: 整数快速幂

为了引出矩阵的快速幂,以及说明快速幂算法的好处,我们可以先求整数的幂。
比如要求x8,如果直接xxx……要进行7次的乘法运算;
但是如果先对它*x,变成x2,那就只需要1+3次;

现在要考虑应该怎么分让计算比较快。接下来计算整数快速幂。例如:x19次方。

19的二进制:10011

就可以变成x16× x2× x

int QuickPow(int x,int N)
{
    int res = x;
    int ans = 1;
    while(N)
    {
        if(N&1)
        {
            ans = ans * res;
        }
        res = res*res;
        N = N>>1;
    }
    return ans;
}

矩阵快速幂

矩阵的乘法

const int N=100;
int c[N][N];
void multi(int a[][N],int b[][N],int n)//n是矩阵大小,n<N
{
    memset(c,0,sizeof c);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++)
        c[i][j]+=a[i][k]*b[k][j];
}

快速幂

板子1

struct Matrix {//结构体,矩阵类型
	int m[1000][1000];
}ans,res;
/*计算矩阵乘法的函数,参数是矩阵a,矩阵b,和一个n代表两个矩阵是几阶方阵*/
Matrix Mul(Matrix A, Matrix B, int n) {
	Matrix tmp;//临时矩阵,存放A*B的结果
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			tmp.m[i][j] = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			for (int k = 1; k <= n; k++)
				tmp.m[i][j] += A.m[i][k] * B.m[k][j];
	return tmp;
}
//快速幂算法,求矩阵的res的n次幂
void QuickPower(int N, int n) {
	/*上面整数的幂的ans初始化是1,对于矩阵来说,ans应该初始化为单位矩阵*/
	for(int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
			if (i == j)ans.m[i][j] = 1;
			else ans.m[i][j] = 0;
		}
	while (N) {
		if (N & 1) 
			ans = Mul(ans, res, n);
		res = Mul(res, res, n);
		N = N >> 1;
	}
}

板子2


const int N=10;
int tmp[N][N];
void multi(int a[][N],int b[][N],int n)
{
    memset(tmp,0,sizeof tmp);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        for(int k=0;k<n;k++)
        tmp[i][j]+=a[i][k]*b[k][j];
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        a[i][j]=tmp[i][j];
}
int res[N][N];
void Pow(int a[][N],int n)
{
    memset(res,0,sizeof res);//n是幂,N是矩阵大小
    for(int i=0;i<N;i++) res[i][i]=1;
    while(n)
    {
        if(n&1)
            multi(res,a,N);//res=res*a;复制直接在multi里面实现了;
        multi(a,a,N);//a=a*a
        n>>=1;
    }
}

这些代码看看就好,模板建议还是自己写的好,自己怎么写顺溜就怎么写呗,弄清楚原理就好。

运用

那我们上面没解决的斐波那契的问题就有了方法:

观察f[n] = f[n-1]+f[n-2] 第n相是由第n-1项和第n-2项递推而来。
同理,第n+1项由第n项和第n-1项递推而来。
因此可以用矩阵递推式表示:
在这里插入图片描述
简写成T * A(n-1)=A(n),T矩阵就是那个2*2的常数矩阵(转移矩阵)
A(n)就是在这里插入图片描述

构建矩阵递推的大致套路

一般An与A(n-1)都是按照原始递推式来构建的,当然可以先猜一个An,主要是利用矩阵乘法凑出矩阵T,第一行一般就是递推式,后面的行就是不需要的项就让相乘为0。矩阵T能把A(n-1)转移到A(n);然后这就是个等比数列,直接写出通项:此处A1叫初始矩阵。所以用一下矩阵快速幂然后乘上初始矩阵就能得到An,这里An就两个元素(两个位置),根据自己设置的A(n)对应位置就是对应的值,按照上面矩阵快速幂写法,res[1][1]=f(n)就是我们要求的。
在这里插入图片描述

实践

poj3233

hdu1757

hdu1575

hdu2276

hdu5015

hdu3483

hdu2855

hdu3658

hdu4565

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值