矩陣,快速冪以及矩陣快速冪終於到了最後一個重頭戲
矩陣快速冪
(矩陣與快速冪的無限基情)
矩陣快速冪基於矩乘和快速冪的一種算法
它的一個作用是加速遞推,例如快速求出斐波那契數列
關於矩陣乘法:http://blog.csdn.net/loi_peacefuldog/article/details/52783928
關於快速冪:http://blog.csdn.net/loi_peacefuldog/article/details/52783942
而矩陣快速冪,其實是一個很水的東西
就是將快速冪的數值替換爲一個矩陣,原理完全相同,但還是有些小地方需要注意
首先是一種特殊的矩陣——單位矩陣,這是一種除了主對角線上數值爲1,矩陣其餘各點,數值均爲0的正方(行列數相同)矩陣
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1
例如這一個矩陣,單位矩陣的大小是任意的,根據具體題意來定,例如在求斐波那契數列第n項時,行列數當等於爲2,不然不滿足矩陣乘法的先決條件
那麼這個單位矩陣有什麼用處呢,我們可以把它當做實數裏面的1,我們知道任意一個實數乘以1等於它本身,任意一個實數乘以它的倒數(1/該實數)等於1,矩陣也具有相似的性質,即任意一個矩陣乘以單位矩陣都等於原矩陣本身
所以我們那可以知道:
單位矩陣* 單位矩陣 * ….* 單位矩陣 == 單位矩陣
單位矩陣 => 單位矩陣 * 單位矩陣 => 單位矩陣的^2 * 單位矩陣^2 => 單位矩陣^4 * 單位矩陣^4…….
單位矩陣^n = 單位矩陣^(2^t1)* 單位矩陣^(2^t2) * …… * 單位矩陣^(2^tn)
友情鏈接(快速冪):http://blog.csdn.net/loi_peacefuldog/article/details/52783942
這樣一來便可以用單位矩陣來替代快速冪中進行快速連乘的xx,上面的博文解釋了這樣做的原理,這裏就不再贅述(其實就是懶得打字了)
另外一個需要注意就是遞推公式的求解和何如將遞推公式轉化爲矩陣
(遞推公式這需要自己根據題意推導,感覺沒法講…..)
拿斐波那契數列爲例
我們都知道
斐波那契數列的遞推公式爲
F[n] = {
0 (n = 0)
1 (n = 1)
F[n-1]+F[n-2] (n > 1)
};
那麼怎麼將這個公式轉化爲矩陣呢
這裏用到斐波那契數列的一個性質:即前n項fibonacci的平方和等於第fib[n]*fib[n+1]。
我們先來想F[ n ] = F[n-1]+F[n-2] (n > 1)時矩陣應當如何構造
咳咳,顯然我們很容易想到一個行數爲1,列數爲2的矩陣
| F[n-1] F[n-2]|
然後顯然:
已知F[n] = F[n-1]+F[n-2]
則可以寫成F[n] = F[n-1]*1+F[n-2]*1
= (F[n-1]+F[n-2])*1
因爲F[1] = F[2] = 1,所以 又可以寫成F[n] = F[n-1]*F[1]+F[n-2]*F[2]
???你是不是已經發現了什麼???
對呀這個對應關係是符合矩陣乘法運算法則的
所以我們便又可以寫出:
|F[n] = |1 1|*|F[n-1]
|F[n-1] |1 0| |F[n-2]
用數學表達式即是:
①F[n] = F[n-1]+F[n-2]
②F[n-1] = F[n-1]+0
但是我們想算某個F[n]但是不知道F[n-1]與F[n-2]的值怎麼辦呢?
因爲本身F[ n ] = F[n-1]+F[n-2] 便是一個遞推公式
所以我們可以將將這個式子化到最初狀態即:
F[3]=F[1]+F[2]
(2 = 1 + 1)
化爲矩陣則爲:
|F[3] = |1 1|*|F[2]
|F[2] |1 0| |F[1]
將F[3]和F[2]換爲F[ n ] 和 F[n-1]
由已知的遞推關係可得:
|F[n] = |1 1|^(n-2)*|F[2]
|F[n-1] |1 0| |F[1]
接下來直接遞推求得F[n]即可了
再利用快速冪加速遞推,使得計算步數大大減少,是不是非常exciting
在代碼實現上最好用一個結構體將數組都封裝起來
這樣做不僅能使代碼更加好看,也更加易讀更容易調試和挑出bug
並且封裝之後代碼量大大降低,寫起來更簡單
(一開始也是直接打的二維數組代碼很亂直到看見一篇神犇的博客)
(http://www.cnblogs.com/vongang/archive/2012/04/01/2429015.html orz)
附上模板水題代碼:CODE[VS] 1732(POJ 3070)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MOD 1000000007
using namespace std;
typedef long long LL;
LL n;
struct osu{
LL m[2][2];
}ans, csj;
osu jc(osu a, osu b){
osu tmp;
for(LL i = 0; i <= 1;i++){
for(LL j = 0; j <= 1;j++){
tmp.m[i][j] = 0;
for(LL k = 0; k <= 1;k++)
tmp.m[i][j] = (tmp.m[i][j]% MOD + (a.m[i][k]%MOD * b.m[k][j]%MOD)%MOD) % MOD;
//(a * b) % p = (a % p * b % p) % p,(a + b) % p = (a % p + b % p) % p
}
}
return tmp;
}
LL pow(LL n){
csj.m[0][0] = 1;
csj.m[0][1] = 1;
csj.m[1][0] = 1;
csj.m[1][1] = 0;
for (LL i = 0;i <= 1;i++)
for (LL j = 0;j <= 1;j++){
ans.m[i][j] = 0;
}
for (LL i = 0;i <= 1;i++)
ans.m[i][i] = 1;
while(n){
if(n & 1){
ans = jc(ans, csj);
}
n >>= 1;
csj = jc(csj, csj);
}
return ans.m[0][1];
}
int main(){
while(scanf("%lld",&n) != EOF){
printf("%lld\n",pow(n));
}
return 0;
}
THE END
蛤蛤,終於完了 準備開始搞幾個題,然後繼續搞數論
(預定再搞2天差不多了,明天還要考試求RP++)
By Peacefuldoge
http://blog.csdn.net/loi_peacefuldog