1. 程式人生 > >C++ 矩陣快速冪

C++ 矩陣快速冪

在學這個之前學了好一陣子矩陣,仍然不太懂。。後來發現解斐波那契數列好像只需要方形矩陣就行了,就學了一下方形矩陣的乘法。

思路:首先,假定我們會快速冪。
那麼如何寫矩陣快速冪呢?
我們需要明確一件事,我們寫的快速冪實際上是:十進位制數字快速冪
十進位制數字的單位是1。而矩陣的單位是單位矩陣
那麼,是不是我們只要把十進位制數字的單位"1"換成"單位矩陣"就可以寫矩陣快速冪了?
答案是是的。不過需要注意一個細節:由於十進位制和矩陣的乘法規則不一樣,因此矩陣的乘法需要額外寫一個方法。

上程式碼吧

#include<cstdio>
#include<iostream>
#define ll long long
#define mod 1000000007
using namespace std;
struct Mat{
	ll m[101][101];
};
Mat initMat(int n);
Mat Mul(Mat a,Mat b,int n);
Mat poww(Mat a,ll b);
Mat a,e;//a:輸入 e:單位矩陣 
int n;//矩陣長度 (每行n個數 ) 
int main(){
	Mat a;
	int k;//k是Mat的指數 
	cin>>n>>k;//input 矩陣長度以及指數
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cin>>a.m[i][j];
		}
	}
	
	//初始化e
	for(int i=0;i<n;i++){
		e.m[i][i]=1;
	} 
	
	Mat c=poww(a,k);
	
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cout<<c.m[i][j]%mod<<" ";
		}
		cout<<endl;
	}
	
	return 0;
}
Mat poww(Mat a,ll b){
	Mat ans=e,base=a;
	while(b!=0){
		if(b&1){//如果是奇數 
			ans=Mul(ans,a,n);//DEBUG重點 原: Mul(ans,ans,n)
		}
		base=Mul(base,base,n);
		b=b/2;
	}
	return ans;
}
Mat Mul(Mat a,Mat b,int n){//相乘 
	//方形矩陣 n:矩陣長或寬 
	Mat c=initMat(n);
	for(int i=0;i<n;i++){//乘到的行數 
		for(int x=0;x<n;x++){//x座標
			for(int y=0;y<n;y++){//y座標 
				c.m[i][x]=c.m[i][x]%mod+a.m[i][y]*b.m[y][x]%mod;
				//c.m[i][x]=c.m[i][x]%mod+a.m[x][i]*b.m[x][y]%mod;
			}
		}
	}
	return c;
}
Mat initMat(int n){//初始化矩陣 n:方形矩陣長或寬 
	Mat c;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			c.m[i][j]=0;
		}
	}
	return c;
}

zxc大佬的指點
1.n,k應為long long型別
2.快速冪的過程中應為 ans=Mul(ans,a,n);

AC程式碼如下:

#include<cstdio>
#include<iostream>
#define ll long long
#define mod 1000000007
using namespace std;
struct Mat{
	ll m[101][101];
};
Mat initMat(int n);
Mat Mul(Mat a,Mat b,int n);
Mat poww(Mat a,ll b);
Mat a,e;//a:輸入 e:單位矩陣 
ll n;//矩陣長度 (每行n個數 ) 
int main(){
	Mat a;
	ll k;//k是Mat的指數 
	cin>>n>>k;//input 矩陣長度以及指數
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cin>>a.m[i][j];
		}
	}
	
	//初始化e
	for(int i=0;i<n;i++){
		e.m[i][i]=1;
	} 
	
	Mat c=poww(a,k);
	
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cout<<c.m[i][j]%mod<<" ";
		}
		cout<<endl;
	}
	
	return 0;
}
Mat poww(Mat a,ll b){
	Mat ans=e,base=a;
	while(b!=0){
		if(b&1){//如果是奇數 
			ans=Mul(ans,base,n);//DEBUG重點 原: Mul(ans,ans,n)
		}
		base=Mul(base,base,n);
		b=b/2;
	}
	return ans;
}
Mat Mul(Mat a,Mat b,int n){//相乘 
	//方形矩陣 n:矩陣長或寬 
	Mat c=initMat(n);
	for(int i=0;i<n;i++){//乘到的行數 
		for(int x=0;x<n;x++){//x座標
			for(int y=0;y<n;y++){//y座標 
				c.m[i][x]=c.m[i][x]%mod+a.m[i][y]*b.m[y][x]%mod;
				//c.m[i][x]=c.m[i][x]%mod+a.m[x][i]*b.m[x][y]%mod;
			}
		}
	}
	return c;
}
Mat initMat(int n){//初始化矩陣 n:方形矩陣長或寬 
	Mat c;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			c.m[i][j]=0;
		}
	}
	return c;
}

總結:

1.應該仔細看好題目的範圍要求
2.快速冪的 ans=Mul(ans,base,n); 應當理解透徹