用來求矩陣的n次方,方法與快速冪相似,所以我們先寫個快速冪
求Ab次方:
int powf(int a,int b){
int ans=1;
while(b){
if(b & 1) ans*=a;
a*=a;
b>>=1;
}
return ans;
}
矩陣快速冪只是把整數換成矩陣而已,其概念是一樣的,差別在於要寫一個矩陣相乘的函式。
求(矩陣a)b:
先副上Code後面再來解釋
#define matrix vector<vector<int>>
#define N 矩陣大小(N*N)
matrix matrix_multiply(matrix &a,matrix &b){ //矩陣相乘函式
matrix ans(N,vector<int>(N));
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
for(int k=0;k<N;k++){
ans[i][j]+=a[i][k]*b[k][j];
}
}
}
return ans;
}
matrix matrix_powf(matrix a,int b){
matrix ans(N,vector<int>(N));
for(int i=0;i<N;i++){
ans[i][i]=1;
}
while(b){
if(b & 1) ans=matrix_multiply(ans,a);
a=matrix_multiply(a,a);
b>>=1;
}
return ans;
}
在matrix_powf()中,有3點被更改,分別是
在快速冪中,ans初始化為1,因為要把b在二進位表示法中最後一位為1時,把a乘到答案裡,但在矩陣快速冪裡,初始化的ans就要換乘矩陣裡的1,也就是[[1,0];[0,1]] (以下都稱作A),驗算一下就可得知
1 2 1 0 1*1+2*0 1*0+2*1 1 2
x = =
3 4 0 1 3*1+4*0 3*0+4*1 3 4
所以任何2*2的矩陣乘上A都會是自己,藉由觀察A的1是由左上往右下斜的,所以在初始化可先把A設為0,再用for跑一遍把斜線上給的點都設成1。
matrix ans(N,vector<int>(N,0));
for(int i=0;i<N;i++){
ans[i][i]=1;
}
在原本快速冪的if(b & 1) ans*=a中,要把a乘到答案裡很簡單,但在矩陣中,要寫一個函式把矩陣相乘的結果傳回來,再把它賦予給原本的矩陣。
if(b & 1) ans=matrix_multiply(ans,a);
同上的概念,也是把矩陣丟到matrix_multiply,再把回傳的結果賦予給b。
b=matrix_multiply(b,b);
\(f(0)=0,f(1)=1,f(2)=1,f(3)=2,f(4)=3,f(5)=5\)
如果要求費式數列有很多方法:
top down
int f(int n){
if(n==0 || n==1) return 1;
return f(n-1)+f(n-2);
}
botton up
int f(int n){
vector<int> fi(n+1);
fi[0]=1;
fi[1]=1;
for(int i=2;i<=n;i++){
fi[i]=fi[i-1]+fi[i-2];
}
return fi[n]
}
但如果要求費式數列第1e9個mod m呢? 如果用上述兩種方法時間複雜度肯定會爆掉,這時候矩陣快速冪就派上用場了。
我們可以先把f1和f2寫成矩陣的形式(以下稱為初始矩陣)
[f(1) f(2)]
這時當我們乘上矩陣[[0,1];[1,1]] (以下都稱為A)的話
0 1
[f(1) f(2)] x = [f(1)*0+f(2)*1 f(1)*1+f(2)*1] = [f(2) f(3)]
1 1
如果再乘以A
0 1
[f(2) f(3)] x = [f(2)*0+f(3)*1 f(2)*1+f(3)*1] = [f(3) f(4)]
1 1
如果要得到[f(3) f(4)]就得把[f(1) f[2]]乘上A的2次方
0 1 0 1
[f(1) f(2)] x x = [f(3) f(4)]
1 1 1 1
每多乘A一次,數列都會往後移一格
結論 : 如果想得到[f(n) f(n+1)]就必須把[f(1) f(2)]乘上A(n-1)
最後就回到了上面的矩陣快速冪,但為了方便撰寫,初始矩陣可以從費式數列第0項開始,A就可以很直觀的乘上n次而非n-1,這樣最後初始矩陣乘上A的時候,A[1][0]就是答案了。
//R=A的n次方
R[0][0] R[0][1]
[f(n) f(n+1)] = [f(0) f(1)] x
R[1][0] R[1][1]
= [f(0)*R[0][0]+f(1)*R[1][0] f(0)*R[0][1]+f(1)*R[1][1]]
= [0*R[0][0]+1*R[1][0] 0*R[0][1]+1*R[1][1]]
= [R[1][0] R[1][1]]
#include <bits/stdc++.h>
#define matrix vector<vector<int>>
using namespace std;
matrix matrix_multiply(matrix &a,matrix &b){
matrix ans(2,vector<int>(2));
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
ans[i][j]+=a[i][k]*b[k][j];
}
}
}
return ans;
}
matrix matrix_powf(matrix a,int b){
matrix ans(2,vector<int>(2));
for(int i=0;i<2;i++){
ans[i][i]=1;
}
while(b){
if(b & 1) ans=matrix_multiply(ans,a);
a=matrix_multiply(a,a);
b>>=1;
}
return ans;
}
int f(int n){
//建立A模板
matrix A(2,vector<int>(2,1));
A[0][0]=0;
matrix result=matrix_powf(A,n);
return result[1][0];
}
int main(){
int n;
while(cin >> n){
cout << f(n) << '\n';
}
return 0;
}
當第n項沒有到很大時,矩陣快速冪反而比較慢
#include <bits/stdc++.h>
#define matrix vector<vector<string>>
using namespace std;
string add(string a,string b){
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
string ans="";
int rest=0;
for(int i=0;i<min(a.size(),b.size());i++){
int cnt=a[i]-'0'+b[i]-'0'+rest;
rest=0;
if(cnt>9){
rest=cnt/10;
cnt=cnt%10;
}
ans+=(cnt+'0');
}
for(int i=min(a.size(),b.size());i<max(a.size(),b.size());i++){
int cnt=rest;
if(a.size() > b.size()) cnt+=a[i]-'0';
else cnt+=b[i]-'0';
rest=0;
if(cnt>9){
rest=cnt/10;
cnt=cnt%10;
}
ans+=(cnt+'0');
}
if(rest!=0) ans+=(rest+'0');
reverse(ans.begin(),ans.end());
return ans;
}
string mul(string a,string b){
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
string ans;
vector<int> ad(a.size()+b.size()+1,0);
for(int i=0;i<a.size();i++){
for(int j=0;j<b.size();j++){
int cnt=(a[i]-'0')*(b[j]-'0');
ad[i+j]+=cnt;
}
}
for(int i=0,rest=0;i<ad.size();i++){
int cnt=ad[i]+rest;
rest=0;
if(cnt>9){
rest=cnt/10;
}
ans+=(cnt%10)+'0';
}
int i=ans.size()-1;
for(;i>=0;i--){
if(ans[i]!='0') break;
}
ans=ans.substr(0,i+1);
reverse(ans.begin(),ans.end());
return ans;
}
matrix maxtrix_mul(matrix& a,matrix& b){
matrix ans(2,vector<string>(2,"0"));
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
ans[i][j]=add(ans[i][j],mul(a[i][k],b[k][j]));
}
}
}
return ans;
}
matrix matrix_pow(matrix& A,int n){
matrix ans(2,vector<string>(2,"0"));
ans[0][0]="1";ans[1][1]="1";
while(n){
if(n & 1){
ans=maxtrix_mul(ans,A);
}
n >>= 1;
A=maxtrix_mul(A,A);
}
return ans;
}
string fi(int n){
if(n==0) return "0";
matrix A(2,vector<string>(2,"1"));
A[0][0]="0";
matrix result=matrix_pow(A,n);
return result[1][0];
}
int main(){
int n;
while(cin >> n){
cout << "The Fibonacci number for " << n << " is " << fi(n) << '\n';
}
return 0;
}
三階矩陣快速冪應用
根據公式:A(N)=P×A(N-1)+Q×A(N-2)+R
A2 0 0 P Q R A1 0 0
[ A1 0 0 ] = [ 1 0 0 ] * [ A0 0 0 ]
1 0 0 0 0 1 1 0 0
^(n-1)
An 0 0 P Q R A1 0 0
[ An-1 0 0 ] = [ 1 0 0 ] * [ A0 0 0 ]
1 0 0 0 0 1 1 0 0
Code:
#include <bits/stdc++.h>
#define int long long
#define matrix vector<vector<int>>
using namespace std;
matrix mul(matrix &a,matrix &b){
matrix ans(3,vector<int>(3));
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
for(int k=0;k<3;k++){
ans[i][j]=(ans[i][j]+a[i][k]*b[k][j])%(long long)1e9;
}
}
}
return ans;
}
signed main(){
int P,Q,R,A0,A1,N;
cin >> P >> Q >> R >> A0 >> A1 >> N;
matrix ans(3,vector<int>(3));
ans[0][0]=A1,ans[1][0]=A0,ans[2][0]=1;
matrix p(3,vector<int>(3));
p[0][0]=P,p[0][1]=Q,p[0][2]=R,p[1][0]=1,p[2][2]=1;
N--;
while(N){
if(N & 1){
ans=mul(p,ans);
}
p=mul(p,p);
N >>= 1;
}
cout << setw(4) << setfill('0') << ans[0][0] << '\n';
return 0;
}