zoukankan      html  css  js  c++  java
  • 矩阵快速幂模板+常用变式

    矩阵乘法(矩阵乘矩阵)

    在向量乘向量的运算中,是将每个元素与它对应的元素相乘,求所有乘积之和

    那么矩阵乘矩阵是不是就是两个同型矩阵的对应元素相乘呢?

    图样图森破

    两个矩阵相乘的前提是前一个矩阵的列数等于后一个矩阵的行数

    举个栗子,AAA为n∗kn*kn∗k矩阵,BBB为k∗mk*mk∗m矩阵,CCC为m∗nm*nm∗n矩阵,那么AAA可以与BBB相乘,BBB可以与CCC相乘,CCC可以与AAA相乘,其他均不成立

    我们知道了什么情况下两个矩阵可以相乘,那么他们怎么相乘呢?不讲每个对应位置相乘还能怎么乘呢?

    设AAA为n∗kn*kn∗k矩阵,BBB为k∗mk*mk∗m矩阵,那么它们的乘积CCC则为一个n∗mn*mn∗m矩阵

    Ci,j=∑r=1kAi,r∗Br,jC_{i,j}=sum_{r=1}^kA_{i,r}*B_{r,j}Ci,j​=∑r=1k​Ai,r​∗Br,j​

    是不是不太好理解,没关系看看图就知道了

    在矩阵乘法中满足以下运算律:

    (AB)C=a(BC)(AB)C=a(BC)(AB)C=a(BC)

    (A+B)C=AC+BC(A+B)C=AC+BC(A+B)C=AC+BC

    C(A+B)=CA+CBC(A+B)=CA+CBC(A+B)=CA+CB


    了解了这么多,我们开始看题,矩阵快速幂,由于矩阵乘法满足结合律,所以我们只需要把它按照一般的快速幂打,再重载一下运算符就可以了,好了我们直接放一道模板题:

    题目描述

    给定n*n的矩阵A,求A^k

    输入输出格式

    输入格式:

    第一行,n,k

    第2至n+1行,每行n个数,第i+1行第j个数表示矩阵第i行第j列的元素

    输出格式:

    输出A^k

    共n行,每行n个数,第i行第j个数表示矩阵第i行第j列的元素,每个元素模10^9+7

    输入输出样例

    输入样例#1: 复制

    2 1
    1 1
    1 1

    输出样例#1: 复制

    1 1
    1 1

    说明

    n<=100, k<=10^12, |矩阵元素|<=1000

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define MOD 1000000007
    using namespace std;
    typedef long long int ll;
    struct mat
    {
    	ll a[105][105];
    }ans,res;//用二维数组来存储矩阵 
    ll k;
    ll n;
    mat Mul(mat x,mat y,ll n)//定义矩阵乘法,其中n是矩阵的阶数 
    {
    	mat t;//用来存储乘完之后的结果 
    	memset(t.a,0,sizeof(t.a));
    	for(int i=0;i<n;i++)
    	for(int j=0;j<n;j++)
    	{
    		for(int k=0;k<n;k++)
    		{
    			t.a[i][j]+=x.a[i][k]*y.a[k][j];
    			t.a[i][j]%=MOD;
    		}
    	}
    	return t;
    }
    void quickMod(ll N,ll n)//N是求幂的次数,n是阶数 
    {
    	for(int i=0;i<n;i++)
    	{
    		for(int j=0;j<n;j++)
    		{
    			if(i==j)
    			ans.a[i][j]=1;
    			else
    			{
    				ans.a[i][j]=0;
    			}
    		}
    	}//这个过程是求单位矩阵的过程,类似于矩阵快速幂里面的ans=1; 
    	while(N)
    	{
    		if(N&1)
    		{
    			ans=Mul(ans,res,n);//矩阵乘法不满足交换律,切记 
    		}
    		N>>=1;
    		res=Mul(res,res,n);
    	}
    }
    int main()
    {
    	scanf("%lld%lld",&n,&k);
    	for(int i=0;i<n;i++)
    	{
    		for(int j=0;j<n;j++)
    		{
    			scanf("%lld",&res.a[i][j]);
    		}
    	}//相当于把第一个矩阵赋初值 
    	quickMod(k,n);
    	for(int i=0;i<n;i++)//输出矩阵的过程 
    	{
    		for(int j=0;j<n;j++)
    		{   if(j==0)
    			printf("%lld",ans.a[i][j]);
    			else
    			printf(" %lld",ans.a[i][j]);
    		}
    		printf("
    ");
    	}
    	return 0;
    }

    想必大家对于矩阵快速幂有了一定的了解,那么矩阵快速幂可以用了做什么呢?

    可以去求解递推式的问题,比如斐波那契数列

    下列就附上求解斐波那契数列的代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #define MOD 1000000007
    using namespace std;
    typedef long long int ll;
    struct mat
    {
    	ll a[4][4];
    }ans,res;
    mat Mul(mat x,mat y)
    {
    	mat t;
        memset(t.a,0,sizeof(t.a));
        for(int i=0;i<2;i++)
        {
        	for(int j=0;j<2;j++)
        	{
        		for(int k=0;k<2;k++)
        		{
        			t.a[i][j]+=(x.a[i][k]*y.a[k][j]);
        			t.a[i][j]%=(MOD-1);
    			}
    		}
    	}
    	return t;
    }
    void quickMod(long long int N)
    {
    	ans.a[0][0]=1;
    	ans.a[0][1]=0;
    	ans.a[1][0]=0;
    	ans.a[1][0]=1;
    	while(N)
    	{
    		if(N&1)
    		{
    			ans=Mul(ans,res);
    		}
    		res=Mul(res,res);
    	    N>>=1;
    	}
     } 
    int main()
    {
    	ll n;
    	while(scanf("%lld",&n)!=EOF)
       {
    	res.a[0][0]=1;
    	res.a[0][1]=1;
    	res.a[1][0]=1;
    	res.a[1][1]=0;
       	quickMod(n);
       	printf("%lld
    ",ans.a[0][0]);
       }
    	return 0;
     } 

    斐波那契数列还有很多变式(摘自杨紫陌学长的博客,链接https://www.cnblogs.com/yzm10/

    矩阵快速幂求long long级斐波那契(变形)。

    f[n]=a*f[n-1]+b*f[n-2], f[1]=a1,f[2]=a2, MOD=...

    得:

    其他变形:

    1.f(n)=a*f(n-1)+b*f(n-2)+c;(a,b,c是常数)

    2.f(n)=c^n-f(n-1) ;(c是常数)

  • 相关阅读:
    chrome webkitappearance
    图片占用内存
    javascript性能优化repaint和reflow
    vim 系统剪切板
    CSS选择符的命名(转载)
    relative 内部 margin
    中国软件企业
    dom元素排序
    shell
    tips for asm
  • 原文地址:https://www.cnblogs.com/Staceyacm/p/10782108.html
Copyright © 2011-2022 走看看