算法分类整理+模板①:矩阵快速幂
一直有一個想法,感覺自己很多基礎算法不是很扎實,想要找個機會寫一些算法的整理,順便自己總結一些實用的模板。
最近偶然在訓練賽中連續做了2道思維+矩陣快速冪的題目,碰巧有時間,就以矩陣快速冪作為這個系列博客的開始吧。
?
如果想要了解矩陣快速冪,首先要了解什么叫做快速冪。
舉個例子,如果讓你求2^10的值,一個for循環可以輕松地解決問題,但是如果是2^10000000000000呢?
且不管這個值能否表示出來,單單說for循環的時間復雜度O(n)就注定不能直接暴力求解。
當然,為了求得這個解,我們一般要求答案對于某個數取模,常用的MOD值有10007,1000000007。
由此,我們可以看出,當問題存在超時+取模的限制時,我們需要一種新的算法,即快速冪。
快速冪是基于二分思想的一種時間復雜度為O(lgn)的算法。
我們可以考慮一個例子,如果要求2^10的值,我們能否這樣算:
首先把2^10分解成(2^5)*(2^5),其次把2^5分解成2*(2^4),然后將2^4分成(2^2)*(2^2),最后把2^2變成2*2。
這樣,我們就將2^10變成了:(2*(2*2)^2)^2。這樣我們只需要計算4次乘法就可以得到2^10的值,而線性的算法需要10次,快速冪進行了極大地優化。
一般地,對于a^b來說,當b為偶數時,我們可以寫成(a^(b/2))^2;當b為奇數時,可以寫成a*(a^(b-1))。
所以,經過快速冪算法優化后的quick_pow計算只需要log(b)次!b越大,這個優化就越明顯!
模板代碼如下:
?
#include <stdio.h> #include <iostream> #define MOD 1000000007 typedef long long int LL using namespace std; LL quick_pow(int a,int b){LL res=1;while(b){if(b&1)res=((a%MOD)*(res%MOD))%MOD;res=(res*res)%MOD;b>>=1;}return res; } 快速冪模板?
?
了解了快速冪的基本思想與代碼實現,我們就要來看看矩陣快速冪。其實矩陣快速冪的基本思想和普通快速冪是一樣的。
對于矩陣,我相信學過線性代數的同學應該對其深惡痛絕……不要怕,我們的矩陣快速冪只涉及到矩陣的乘法,是不是很簡單啊~(對于不會矩陣乘法的同學請自行百度)
對于矩陣乘法,模板如下:
?
#include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #define MOD 1000000007 typedef long long int LL; using namespace std; struct Matrix{int a[2][2];Matrix(){memset(a,0,sizeof(a));}Matrix operator* (const Matrix &p){Matrix res;for(int i=0;i<2;i++){for(int j=0;j<2;j++){for(int k=0;k<2;k++){res.a[i][j]+=(a[i][k]*p.a[k][j]%MOD);}res.a[i][j]%=MOD;}}return res;} }init,unit; 矩陣乘法?
首先來看一個例子:如果問題是求解斐波那契數列的第1000000000項對于MOD取模的值,我們應該如何去做?
從快速冪我們知道對于這個問題線性的計算是肯定超時的,所以我們依舊采用快速冪的思想。
對于斐波那契數列我們有如下規律(以下為矩陣):
f(n+1) f(n)??? 乘以??? 1???? 1???? 結果是???? f(n+1)+f(n)? f(n+1) 即:f(n+2)? f(n+1)
f(n)? f(n-1)???????????? 1???? 0 ? f(n)+f(n-1)? f(n) ? ? f(n+1)? f(n)
這是我們會驚奇的發現 當我們用斐波那契數列組成的矩陣乘以一個特定矩陣時會得到下一個斐波那契數的值。
所以不難想象,我們只要知道數列的前3項,用他們組成的矩陣乘以這個特定矩陣的k次冪就能得到任意項的斐波那契數,并且時間復雜度是O(lgn)的!
所以,到這里我們就要知道如何去找這個特定矩陣。
一般地,如果有通項公式:f(n)=a*f(n-1)+b*f(n-2)+c*f(n-3)……(這里我們以3個為例),若f(1)==p,f(2)==q,f(3)==r
我們設定一個init矩陣表示初始值:
r? 0? 0
q? 0? 0
p? 0? 0
一個unit矩陣表示那個特定矩陣:
a? b? c
1? 0? 0
0? 1? 0
這樣,unit矩陣左乘init矩陣等于:
ap+bq+cs? 0? 0
??????????? p? 0? 0
??????????? q? 0? 0
這樣我們就構造出了一個矩陣,表示出了整個數列的遞推關系:
?a? b? c f(3)? 0? 0 ? f(n+2)? 0? 0
(1? 0? 0) ? ? 的n次冪 ? 乘以 ? ? f(2)? 0? 0 等于 ? f(n+1)? 0? 0
?0? 1? 0 ? f(1)? 0? 0 ????? f(n)? 0? 0
?
當然,構造這樣的矩陣的方法不一,此方法只是較為通用的方法,對于某些通項公式可以找到更簡便的矩陣使得矩陣快速冪成立。
?
矩陣快速冪模板如下:
#include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #define MOD 1000000007 typedef long long int LL; using namespace std; struct Matrix{int a[2][2];Matrix(){memset(a,0,sizeof(a));}Matrix operator* (const Matrix &p){Matrix res;for(int i=0;i<2;i++){for(int j=0;j<2;j++){for(int k=0;k<2;k++){res.a[i][j]+=(a[i][k]*p.a[k][j]%MOD);}res.a[i][j]%=MOD;}}return res;} }init,unit; Matrix quick_pow(Matrix unit,Matrix init,int k){while(k){if(k&1)init=init*unit;unit=unit*unit;k>>=1;}return init; } void init_Matrix(){init.a[0][0]=1;init.a[0][1]=0;init.a[1][0]=0;init.a[1][1]=1;unit.a[0][0]=1;unit.a[0][1]=1;unit.a[1][0]=1;unit.a[1][1]=0; } 矩陣快速冪?
主函數中首先對init和unit矩陣進行初始化,然后再調用quick_pow()。
?
?
小Tips:關于如何識別矩陣快速冪的問題。
一般來說,題目中如果有”答案對于MOD取模“這句話時,并且操作次數巨大,我們就可以考慮使用快速冪或矩陣快速冪。
關于題目中有”取模“的說法時,一般來說有幾種可能。一是快速冪,二是dp,三是組合數學。
另外推薦兩道矩陣快速冪的題目和題解:
http://www.cnblogs.com/Torrance/p/5412802.html
http://www.cnblogs.com/Torrance/p/5410755.html
?
轉載于:https://www.cnblogs.com/Torrance/p/5420957.html
總結
以上是生活随笔為你收集整理的算法分类整理+模板①:矩阵快速幂的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 摄像头检测工具,检摄ap
- 下一篇: 二维码扫描利用ZBar实现