矩陣乘法與矩陣加速


矩陣乘法與矩陣加速

矩陣乘法

矩陣乘法比較簡單,就是兩個矩陣相乘得到一個新矩陣的運算.

乘法的過程就是:

第一個矩陣的每一行和第二個矩陣的每一列對應位置相乘相加,放入新矩陣.

不太顯然,矩陣乘法對於參與運算的矩陣是有限制的:

\[[n\times m] * [m\times k] = [n\times k] \]

即,一個 \(n\times m\) 的矩陣和一個 \(m\times k\) 的矩陣相乘得到一個 \(n \times k\) 的矩陣.

必須是形如上面那樣的矩陣才能進行乘法.

從這里可以看出,一般的矩陣乘法是不滿足交換律的.(個別情況除外)

顯然,一次矩陣乘法的復雜度是 \(\Theta(n\times m\times k)\) 的.

兩個矩陣相乘的代碼如下:

struct Matrix {
    LL e[N][M] , line , row ;

    inline void clear () { line = row = 0 ; MEM ( e , 0 ) ; return ; }

    friend Matrix operator * (Matrix a , Matrix b) {
        Matrix c ; c.clear () ; c.line = a.line ; c.row = b.row ;
        rep ( k , 0 , a.row - 1 ) rep ( i , 0 , a.line - 1 ) rep ( j , 0 , b.row - 1 )
            c.e[i][j] = ( c.e[i][j] + a.e[i][k] * b.e[k][j] % mod ) % mod ;
        return c ;
    }
} ;

上面的代碼采用了結構體的封裝形式,用起來更方便.

矩陣快速冪

有了矩陣乘法,我們再來考慮和乘法密切相關的一種運算:冪運算.

即一個矩陣的若干次冪如何計算,顯然,只有正方形的矩陣可以計算高次冪.

那么類比於實數冪次,一個矩陣的 \(p\) 次冪只需要把它自乘 \(p\) 次即可.

討論到了冪運算的問題,就不得不提快速冪.

眾所周知,快速冪的復雜度是 \(\Theta(log_2{P})\) 的,其中 \(P\) 是模數.

這樣的復雜度實在令人眼饞,相比於自乘若干次快了不知道多少倍.

那矩陣的冪次也可以像快速冪那樣分治處理嗎?

我們知道矩陣乘法是不滿足交換律的,那么它滿足結合律嗎?

如果它滿足結合律,那么它就可以像快速冪那樣運算.

事實上,矩陣乘法是滿足結合律的.

於是矩陣快速冪的代碼也出爐了:

inline Matrix quick (Matrix a , LL p) {
    Matrix c ; c.clear () ; c.line = a.line ; c.row = a.row ;
    rep ( i , 0 , c.line - 1 ) c.e[i][i] = 1 ;
    while ( p ) {
        if ( p & 1 ) c = c * a ;
        a = a * a ; p >>= 1 ;
    }
    return c ;
}

十分簡單!

矩陣加速

根據一些資料,我們知道矩乘的本質是高維向量卷積,方程代換和線性變換.

由此得到啟發,能否用矩陣來轉移遞推方程? 答案是肯定的.

\(Fibonacci\) 數列的遞推為例,我們來考慮構造轉移矩陣.

眾所周知, \(Fibonacci\) 數列的遞推式是 \(f_n=f_{n-1}+f_{n-2}\).

可以發現, \(Fibonacci\) 的遞推只和某一項的前兩項相關.

所以我們考慮的矩陣應該是 \(2\times 2\) 的.

我們的初始矩陣是這樣的:

\[\left[\begin{array}{ll}{f_i}&{f_{i-1}}\end{array}\right] \]

而目標矩陣是這樣的:

\[\left[\begin{array}{ll}{f_{i+1}}&{f_{i}}\end{array}\right] \]

所以轉移矩陣長這樣:

\[\left[\begin{array}{ll}{a} & {b} \\ {c} & {d} \end{array}\right] \]

我們要的矩陣轉移式就是這樣的:

\[\left[\begin{array}{ll}{f_{i}} \\ {f_{i-1}} \end{array}\right] \times \left[\begin{array}{ll}{a} & {b} \\ {c} & {d} \end{array}\right] = \left[\begin{array}{l}{f_{i+1}} \\ {f_{i}}\end{array}\right] \]

根據矩陣乘法的過程,可以得到:

\[a\times f_i + c \times f_{i-1} = f_{i+1} \]

\[b\times f_i + d \times f_{i-1} = f_{i} \]

顯然,\(a=1,c=1,b=1,d=0\).

於是,轉移矩陣為:

\[\left[\begin{array}{ll}{1} & {1} \\ {1} & {0} \end{array}\right] \]

初始矩陣\(emmmm...\),不就是這個嘛

\[\left[\begin{array}{l} {1} & {1} \end{array}\right] \]

一次轉移就乘一次轉移矩陣,自行判斷冪次即可.

矩陣加速遞推的擴展

\(updated:\)

擴展情況並不多,一般只是兩個遞推的組合,常數項的累加以及前綴和,其實本質都是遞推組合.

就都以 \(Fibonacci\) 數列為例好了.

擴展 1 帶常數項

定義數列 \(F_n\) 的遞推式為 \(F_n=F_{n-1}+F_{n-2}+1\) , 其中 \(F_1 = F_2 = 1\).

沒有常數項的部分就是一個普通的 \(Fibonacci\) 數列.

我們令 \(g_n\) 表示常數項的遞推式,顯然, \(g_n=g_{n-1}\).

於是我們的初始矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n} & F_{n-1} & g_n \end{array}\right] \]

目標矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n+1} & F_n & g_{n+1}\end{array}\right] \]

那么我們要的轉移矩陣應該滿足 \(:\)

\[\left[\begin{array}{llll} F_{n} \\ F_{n-1} \\ g_n \end{array}\right] \times \left[\begin{array}{llll} ? & ? & ? \\ ? & ? & ? \\ ? & ? & ? \end{array}\right] = \left[\begin{array}{llll} F_{n+1} \\ F_n \\ g_{n+1}\end{array}\right] \]

根據上面構造矩陣的方法,可以得到,轉移矩陣長這個樣子 \(:\)

\[\left[\begin{array}{llll} 1 & 1 & 0 \\ 1 & 0 & 0 \\ 1 & 0 & 1 \end{array}\right] \]

(因為這些擴展的意義就在於構造不同的矩陣,所以只講如何構造矩陣).

擴展 2 帶未知項遞推

定義數列 \(F_n\) 的遞推式為 \(F_n=F_{n-1}+F_{n-2}+n\) , 其中 \(F_1=F_2=1\).

沒有常數項的部分就是一個普通的 \(Fibonacci\) 數列.

\(g_n\) 表示未知項的遞推式,顯然, \(g_n=g_{n-1}+1\).

於是我們的初始矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n} & F_{n-1} & g_n & 1 \end{array}\right] \]

目標矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n+1} & F_n & g_{n+1} & 1 \end{array}\right] \]

那么我們要的轉移矩陣應該滿足 \(:\)

\[\left[\begin{array}{llll} F_{n} \\ F_{n-1} \\ g_n \\ 1 \end{array}\right] \times \left[\begin{array}{llll} ? & ? & ? & ? \\ ? & ? & ? & ? \\ ? & ? & ? & ? \\ ? & ? & ? & ? \end{array}\right] = \left[\begin{array}{llll} F_{n+1} \\ F_n \\ g_{n+1} \\ 1 \end{array}\right] \]

根據上面構造矩陣的方法,可以得到,轉移矩陣長這個樣子 \(:\)

\[\left[\begin{array}{llll} 1 & 1 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 1 & 0 & 1 & 0 \\ 1 & 0 & 1 & 1 \end{array}\right] \]

擴展 3 遞推式的組合

令數列 \(F_n\) 的遞推式為 \(F_n=F_{n-1}+F_{n-2}+f_{n-1}+f_{n-2}\),其中,\(F_1=F_2=f_1=f_2=1\).

這叫啥?雙重 \(Fibonacci\) 數列?算了,還是不亂叫了.

於是我們的初始矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n} & F_{n-1} & f_n & f_{n-1} \end{array}\right] \]

目標矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n+1} & F_n & f_{n+1} & f_n \end{array}\right] \]

那么我們要的轉移矩陣應該滿足 \(:\)

\[\left[\begin{array}{llll} F_{n} \\ F_{n-1} \\ f_n \\ f_{n-1} \end{array}\right] \times \left[\begin{array}{llll} ? & ? & ? & ? \\ ? & ? & ? & ? \\ ? & ? & ? & ? \\ ? & ? & ? & ? \end{array}\right] = \left[\begin{array}{llll} F_{n+1} \\ F_n \\ f_{n+1} \\ f_n \end{array}\right] \]

根據上面構造矩陣的方法,可以得到,轉移矩陣長這個樣子 \(:\)

\[\left[\begin{array}{llll} 1 & 1 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 1 & 0 & 1 & 1 \\ 1 & 0 & 1 & 0 \end{array}\right] \]

擴展 4 前綴和

令數列 \(F_n\) 的遞推式為 \(F_n=F_{n-1}+F_{n-2}\) , 求 \(\sum_{i=1}^{n}{F_i}\) , 其中 \(F_1=F_2=1\).

\(S_i\) 表示到 \(i\) 的前綴和.

則顯然有\(:\)

\[S_i=F_{i-1}+F_{i-2}+S_{i-1} \]

於是我們的初始矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n} & F_{n-1} & S_n \end{array}\right] \]

目標矩陣就是 \(:\)

\[\left[\begin{array}{llll} F_{n+1} & F_n & S_{n+1} \end{array}\right] \]

那么我們要的轉移矩陣應該滿足 \(:\)

\[\left[\begin{array}{llll} F_{n} \\ F_{n-1} \\ S_n\end{array}\right] \times \left[\begin{array}{llll} ? & ? & ? \\ ? & ? & ? \\ ? & ? & ? \end{array}\right] = \left[\begin{array}{llll} F_{n+1} \\ F_n \\ S_{n+1} \end{array}\right] \]

根據上面構造矩陣的方法,可以得到,轉移矩陣長這個樣子 \(:\)

\[\left[\begin{array}{llll} 1 & 1 & 1 \\ 1 & 0 & 1 \\ 0 & 0 & 1 \end{array}\right] \]

擴展 5 帶系數

令數列 \(F_n\) 的遞推式為 \(F_n = a\times F_{n-1} + b\times F_{n-2}\),其中 \(F_1,F_2\) 是給定的數.

初始矩陣仍然是 \(:\)

\[\left[\begin{array}{llll} F_n & F_{n-1} \end{array}\right] \]

目標矩陣仍然是 \(:\)

\[\left[\begin{array}{llll} F_{n+1} & F_{n} \end{array}\right] \]

那么我們要的轉移矩陣應該滿足 \(:\)

\[\left[\begin{array}{llll} F_{n} \\ F_{n-1} \end{array}\right] \times \left[\begin{array}{llll} ? & ? \\ ? & ? \end{array}\right] = \left[\begin{array}{llll} F_{n+1} \\ F_n \end{array}\right] \]

根據上面構造矩陣的方法,可以得到,轉移矩陣長這個樣子 \(:\)

\[\left[\begin{array}{llll} a & 1 \\ b & 0 \end{array}\right] \]

完結撒花 \(!\)

你說為什么這幾種擴展的框架一模一樣?因為我復制的啊...(懶

關於矩乘更有意思的應用,我還有另一篇博文矩陣乘法與鄰接矩陣


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM