矩阵乘法:
矩阵相乘最重要的方法是一般矩阵乘积。它只有在第一个矩阵的列数(column)和第二个矩阵的行数(row)相同时才有意义。一般单指矩阵乘积时,指的便是一般矩阵乘积。一个m×n的矩阵就是m×n个数排成m行n列的一个数阵。由于它把许多数据紧凑的集中到了一起,所以有时候可以简便地表示一些复杂的模型。
定义:
设A为 的矩阵,B为 的矩阵,那么称 的矩阵C为矩阵A与B的乘积,记作 ,其中矩阵C中的第行第 列元素可以表示为:
如下所示:
注意事项:
当矩阵A的列数等于矩阵B的行数时,A与B可以相乘。
① 矩阵C的行数等于矩阵A的行数,C的列数等于B的列数。
② 乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和。
想要掌握矩阵快速幂就要先知道什么是矩阵求法。
矩阵快速幂求法:
若求mⁿ的值,若用常用方法。即:m*m*m*m*m*m.......时间复杂度为O(n),若n的数值很大的话,会很耗时,若用快速幂求法就耗时少多了时间复杂度为O(㏒n)。
例:m^7=m^111(2)。思想就是把幂指数转化为二进制。例子只需要循环3就可以求出答案.
实现:
while(n)
{
if(n&1)//判断二进制末尾是否与1相同。
abs*=m;//ans用来记录结果初始为0.
m*=m;
n>>=1;(二进制数向右移一位,也可写为n/=2)
}
这行该很好理解就不做进一步讲解。若何活用矩阵快速幂才是最重要的。
例:
若 x < 10 f(x) = x.
I若x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10);
ai(0<=i<=9) 是0或者 1
输入k和m。
输出f(k)若大于m则=取其模。
大神说这是一道做基础的快速幂的题,因为他是一个裸题,不用推导公式。
我们可以用类似数字快速幂的算法来解决矩阵快速幂。
代码实现:
#include<iostream>
#include<string.h>
#include<cstdio>
#include<algorithm>
#define N 10
int arr[N],f[N];
int k,MOD;
struct Found
{
long long mat[N][N];
Found(){
memset(mat,0,sizeof(mat));
}
Found operator*(const Found& m)const{
Found tmp;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
tmp.mat[i][j]=0;
for(int k=0;k<N;k++)
{
tmp.mat[i][j]+=m.mat[i][k]*mat[k][j]%MOD;
tmp.mat[i][j]%=MOD;
}
}
}
return tmp;
}
};
long long pow(Found& m,int k)
{
Found ans;
memset(ans.mat,0,sizeof(ans.mat));
for(int i=0;i<N;i++)
{
ans.mat[i][i]=1;
}
k-=9;
while(k)
{
if(k&1)
ans=ans*m;
m=m*m;
k>>=1;
}
long long sum=0;
for(int i=0;i<N;i++)
{
sum+=(f[N-i-1]*ans.mat[0][i])%MOD;
sum%=MOD;
}
return sum;
}
void init(Found& m)
{
memset(m.mat,0,sizeof(m.mat));
for(int i=0;i<N;i++)
m.mat[0][i]=arr[i];
for(int i=0;i<N-1;i++)
m.mat[i+1][i]=1;
for(int i=0;i<N;i++)
f[i]=i;
}
int main()
{
Found m;
while(scanf("%d%d",&k,&MOD)!=EOF)
{
for(int i=0;i<N;i++)
scanf("%d",&arr[i]);
init(m);
if(k<10)
printf("%d",k);
else
printf("%lld\n",pow(m,k));
}
}
利用矩阵快速幂求:斐波那契数列(Fibonacci sequence)。
在斐波那契数列之中
fi[i] = 1*fi[i-1]+1*fi[i-2]
fi[i-1] = 1*f[i-1] + 0*f[i-2];
即
所以
具体代码实现:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
const int MOD=1e+7;
int f[2]={1,1};
using namespace std;
struct Found
{
long long mat[2][2];
Found(){
memset(mat,0,sizeof(mat));
}
Found operator*(const Found& m)const{
Found tmp;
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
tmp.mat[i][j]=0;
for(int k=0;k<2;k++)
{
tmp.mat[i][j]+=m.mat[i][k]*mat[k][j]%MOD;
tmp.mat[i][j]%=MOD;
}
}
}
return tmp;
}
};
long long pow(int k)
{
Found m,ans;
m.mat[0][0]=m.mat[0][1]=m.mat[1][0]=1;
m.mat[1][1]=0;
ans.mat[0][0]=ans.mat[1][1]=1;
k=k-2;
while(k)
{
if(k&1)
ans=ans*m;
m=m*m;
k>>=1;
}
return ans.mat[0][0]*f[1]+ans.mat[0][1]*f[0];
}
int main()
{
int k;
while(scanf("%d",&k)!=EOF)
{
if(k>2)
printf("%lld\n",pow(k));
else
printf("1\n");
}
}
不得不提下对于2*2矩阵的阶乘,仔细分析就是Fibonacci sequence数列,可以利用这点使上述代码简化。
弄了很久才搞明白,虽然消耗很长时间时间,但对矩阵快速幂理解更深了,矩阵快速幂的分享就到这里了,理解它是第一步,下一步做的就是独立练习这方面的题型,使知识更加的牢固。