矩阵乘法优化递归式
序:
在OI比賽中,很多情況下我們可以能通過打表(找規律)或者某些方式發現一個遞歸式。
例如:f(n) = f(n - 1)+f(n - 2),(斐波那契數列)。
通常情況下,我們計算f(n)的時間復雜度就是O(n)(分別計算f(1), f(2) ... f(n - 1)).
但是當n很大又或者還有其他處理的復雜度一疊加便會超時。
如果不學習矩陣乘法優化的話,我們恐怕永遠不會想到計算遞推式還可以進行優化。
實際上利用矩陣乘法,我們可以將O(n)的計算遞歸式的復雜度降至O(logn)。
優化遞歸式的特征
形如f(n) = a1 * f(n - 1) + a2 * f(n - 2) + ... + ak * f(n - k)+c (c為常數)
本文討論的范疇
形如 f(n) = f(n-1) + f(n-2) + .. + f(n-k) 的遞歸式(1)
形如f(n) = a1 * f(n-1) + a2 * f(n-2) + .. + ak * f(n-k) 的遞歸式(2)
形如f(n) = a1 * f(n-1) + a2 * f(n-2) + .. + ak * f(n-k) + c 的遞歸式(3)
實際上理解了最簡單的第一個遞歸式的原理,就很容易理解后面的兩種情況。每個前式都是后式的一種特殊情況。
理論基礎
首先給出斐波那契數列求第n項的O(logn)的做法,由它引出原理。
已知 :
f(n) = f(n -1) + f(n - 2); -- (1)
f(n - 1) = f(n - 1); -- (2)
由(1)(2)可以得到這樣的一個式子:
[ f(n) ] = [1, 1] * [f(n - 1)]
[f(n - 1)] [1, 0] * [f(n - 2)]
這就核及到矩陣乘法的運算規則。矩陣乘法的計算方式如下所示:
A = X * Y。必須滿足row(X) = colom(Y)。
A[i][j] = sum{x[i][k] * x[k][j]}。
row(A) = row(X), colom(A) = colom(Y)..
對于上式,f(n) = f(n - 1) + f(n - 2),f(n - 1) = f(n - 1) + 0,滿足斐波那契數列的規則。
我們設右邊靠左的式子為A,靠右的為F。
那么計算f(n),我們只需要計算A^(n - 1) * F。復雜度O(n)。
但是做冪運算,可以通過快速冪將復雜度從O(n)降到O(logn),因此總復雜度科技降到O(logn)。
通過這個例子我們能發現什么?很顯然的便是A與Y(左式與右二式,一下皆簡稱為A,X,Y)本質上是一個東西,因此通過迭代,直接計算出第n項。
斐波那契數列是一個最簡單的例子,它也是(1)的典型例子。
廣義斐波那契數列
定義f(n) = a * f(n - 1) + b * f(n - 2).
與之前的區別僅僅在于前面的系數不是1,那么構造出等式只需要照葫蘆畫瓢即可。
[ f(n) ] = [a, b] [f(n - 1)]
[f(n - 1)] [1, 0] [f(n - 2)],本質沒有變化。
那么對于f(n) = a1 * f(n - 1) + .... + ak * f(n - k),我們只需要構造一個k * k的矩陣。
(*)式便是一般情況的式子,其實只需要使得第一行滿足數列公式,其他行滿足f(i) = f(i)即可。
因此只需要令f[i][i - 1] = 1(下標從0開始),其他都是0即可使等式成立。
帶常數與系數的遞歸式
通過之前的經驗我們不難看出,矩陣A,Y中的元素一定是每一次計算的時候必需的元素。這次多了一個常數c,因此c也要在A,Y中出現。
對此,我們在需要在最后一行加上c,構造成一個(k + 1) * (k + 1)的矩陣,如下。
觀察一下(**)式,第1和k+1行的最后都為1,其他新增的空位全是0,如此便構造完成了。
至于原理,再通過矩陣乘法驗證一下不就好了嗎?
最后給出代碼實現(NOI2012 d1t3)
這道題就是一個裸的(3)式情況。
至于一些更簡單的情況,以下有幾道練習。
luogu P1962 斐波那契數列
luogu P1349 廣義斐波那契數列
Vijos 1067 Warcraft III 守望者的煩惱
NOI2012 隨機數生成器
最后一題值得注意的是,最后幾個點乘法過程會炸出longlong,因此需要使用類快速冪的方法快速求和(積),邊加邊取模。
至此結束。
箜瑟_qi
** 7:58 2017.05.25**
轉載于:https://www.cnblogs.com/kongse-qi/p/6901951.html
總結
- 上一篇: 中国第二代身份证验证js代码
- 下一篇: LeetCode 102. Binary