矩陣快速冪小結

update in 9.17ide

矩陣

並不想扯什麼高端線代的內容由於我也不會函數

定義

由$n \times m$個數$a_{ij}$排成的$n$行$m$列的數表稱爲$n$行$m$列的矩陣,簡稱$n \times m$矩陣。學習

$$
A =
\begin{bmatrix}
a_{11} & a_{12} & \dots a_{1m} \\
a_{21}, & \dots & \dots \\
a_{31}, & \dots & \dots \\
a_{41} & \dots & a_{nm}
\end{bmatrix}
$$優化

運算

這裏只講加法減法和乘法,其餘的例如矩陣求逆等與本文內容出入較大,有興趣的能夠本身學習spa

加法

注意,只有行列均相同的矩陣纔有加法!.net

運算也比較簡單,把對應位置的數相加獲得一個新的矩陣,即爲答案code

例如blog

$$
\begin{bmatrix} 1 & 1 & 2 \\ 1 & 0 & 1 \end{bmatrix}
+
\begin{bmatrix} 2 & 3 & 3 \\ 3 & 3 & 2 \end{bmatrix}
=
\begin{bmatrix} 3 & 4 & 5 \\ 4 & 3 & 3 \end{bmatrix}
$$ip

加法知足如下運算律get

$A + B = B + A$

$(A + B) + C = A + (B + C)$

減法

與加法同理。

乘法

這纔是重點!!

兩個矩陣能進行乘法的前提條件是:一個矩陣的行數等於另外一個矩陣的列數

形式化的來講,若$A$是$i \times k$的矩陣,那麼$B$必須是$k \times j$的矩陣!

他們相乘獲得的$C$是$i \times j$的矩陣

其中$C_{ij} = \sum_{i = 1}^n A_{ik} * B_{kj}$

好比

$$
\begin{bmatrix} 1 & 2\\ 2 & 3 \end{bmatrix}
\times
\begin{bmatrix} 2 & 4 & 5 \\ 3 & 4 & 3 \end{bmatrix}
=
\begin{bmatrix} 8 & 12 & 11 \\ 13 & 20 & 19 \end{bmatrix}
$$

乘法知足結合律,左分配律,右分配律,即

$(A \times B) \times C = A \times (B \times C)$

$(A + B) \times C = A \times C + B \times C$

$C(A + B) = C \times A + C \times B$

千萬注意!矩陣乘法不知足交換律!(不少狀況下交換以後都不能相乘)

 

矩陣快速冪

由於矩陣有結合律,所以咱們能夠把整數的快速冪推廣的矩陣上面

題目連接

一樣是利用二進制倍增的思想,不可貴到如下代碼

其中的base,表明的是單位矩陣,也就是除了對角線全爲$1$,其餘位置都爲$0$的矩陣,能夠證實任意矩陣乘單位矩陣都等於自身

顯然矩陣快速冪的複雜度爲$O(n^3 log k)$

#include<cstdio>
#define LL long long 
using namespace std;
const int mod = 1e9 + 7;
int N;
LL K;
struct Matrix {
    int m[101][101];
    Matrix operator * (const Matrix &rhs) const {
        Matrix ans = {};
        for(int k = 1; k <= N; k++) 
            for(int i = 1; i <= N; i++) 
                for(int j = 1; j <= N; j++) 
                    (ans.m[i][j] += 1ll * m[i][k] * rhs.m[k][j] % mod) %= mod;
        return ans;
    }
};
Matrix fastpow(Matrix a, LL p) {
    Matrix base;
    for(int i = 1; i <= N; i++) base.m[i][i] = 1;//構造單位矩陣
    while(p) {
        if(p & 1) base = base * a;
        a = a * a; p >>= 1;
    }
    return base;
}
int main() {
    scanf("%d %lld", &N, &K);
    Matrix a; 
    for(int i = 1; i <= N; i++)    
        for(int j = 1; j <= N; j++)
            scanf("%d", &a.m[i][j]);
    a = fastpow(a, K);
    for(int i = 1; i <= N; i++, puts("")) 
        for(int j = 1; j <= N; j++)
            printf("%d ", a.m[i][j]);
    
    return 0;
}

 

應用

斐波那契數列第$n$項

矩陣快速冪最多見的應用就是優化遞推啦

仍是從最多見的斐波那契數列提及吧。

衆周所知,斐波那契數列的遞推公式爲$$f_{n} = f_{n - 1} + f_{n - 2}, f_1 = 1, f_2 = 1$$

通常來講,這種看起來長得很簡單,只與自身的函數值有關(可能帶幾個常數)的式子,通常均可以用矩陣快速冪來加速。

固然,若是你想找刺激,能夠學一下這玩意兒

矩陣快速冪具體是怎麼加速遞推的呢?

首先咱們把斐波那契數列寫成矩陣的形式,由於$f_n$的取值與$f_{n - 1}, f_{n - 2}$這兩項有關,所以咱們須要同時保留這兩項的值,咱們不可貴到一個$2 \times 1$的矩陣

$$
\begin{bmatrix}
f_{n} \\
f_{n - 1}
\end{bmatrix}
$$

如今咱們要進行遞推,也就是獲得這樣一個矩陣

$$
\begin{bmatrix}
f_{n + 1} \\
f_{n}
\end{bmatrix}
$$

展開

$$
\begin{bmatrix}
f_{n} + f_{n - 1} \\
f_{n}
\end{bmatrix}
$$

觀察一下,上面的一項須要用到$f_{n}$和$f_{n - 1}$,下面的一項只須要用到$f_n$

同時結合上面的矩陣乘法的定義,咱們不可貴到一個轉移矩陣

$$
\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix}
\begin{bmatrix} f_{n} \\ f_{n - 1}\\ \end{bmatrix}
=
\begin{bmatrix} f_{n} + f_{n - 1} \\ f_{n}\\ \end{bmatrix}
$$

這樣咱們乘一次便可遞推到下一項。

可是這樣好像並無什麼卵用啊,複雜度上還多了個矩陣相乘

嘿嘿

不要忘了,咱們前面能夠講過矩陣有結合律的!

這樣的話咱們只須要把轉移矩陣自乘$n - 1$次,而後再與初始矩陣相乘,就能獲得答案矩陣啦!

題目連接

// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#define LL long long 
using namespace std;
const int mod = 1e9 + 7;
LL K;
struct Matrix {
    int m[101][101], N;
    Matrix() {
        memset(m, 0, sizeof(m));
        N = 2;
    }
    Matrix operator * (const Matrix &rhs) const {
        Matrix ans;
        for(int k = 1; k <= N; k++) 
            for(int i = 1; i <= N; i++) 
                for(int j = 1; j <= N; j++) 
                    (ans.m[i][j] += 1ll * m[i][k] * rhs.m[k][j] % mod) %= mod;
        return ans;
    }
};
Matrix fastpow(Matrix a, LL p) {
    Matrix base; 
    for(int i = 1; i <= base.N; i++) base.m[i][i] = 1;//鏋勯€犲崟浣嶇煩闃?
    while(p) {
        if(p & 1) base = base * a;
        a = a * a; p >>= 1;
    }
    return base;
}
int main() {
    scanf("%lld", &K);
    Matrix a;
    a.m[1][1] = 1; a.m[1][2] = 1;
    a.m[2][1] = 1; a.m[2][2] = 0;
    a = fastpow(a, K - 1);
    printf("%d", a.m[1][1]);
    return 0;
}
代碼

路徑計數問題

https://www.nowcoder.com/acm/contest/185/B

給出一個 n * n 的鄰接矩陣A.
A是一個01矩陣 .
A[i][j]=1表示i號點和j號點之間有長度爲1的邊直接相連.
求出從 1 號點 到 n 號點長度爲k的路徑的數目.

作法:直接對給出的矩陣快速冪$k$次,輸出$A[1][N]$

解釋:考慮矩陣相乘的過程,咱們對於要計算的$(i, j)$位置,咱們至關因而枚舉了一箇中間節點$k$,來計算$(i, k) * (k, j)$

其餘的常見矩陣推導

$$G_i = a\times G_{i - 1} + b\times G_{i - 2}$$

\begin{equation*}
\begin{bmatrix}
a&b\\
1 & 0\\
\end{bmatrix}^{i - 1}\times
\begin{bmatrix}
G_{1}\\
G_{0}\\
\end{bmatrix}=
\begin{bmatrix}
a&b\\
1 & 0\\
\end{bmatrix}\times
\begin{bmatrix}
G_{i - 1}\\
G_{i - 2}\\
\end{bmatrix}=
\begin{bmatrix}
G_{i}\\
G_{i - 1}\\
\end{bmatrix}
\end{equation*}

 

$$G_i = a\times G_{i - 1} + i^2$$

\begin{equation*}
\begin{bmatrix}
a&1&0&0\\
0 & 1&2&1\\
0 & 0&1&1\\
0 & 0&0&1\\
\end{bmatrix}^{i}\times
\begin{bmatrix}
G_{0}\\
1\\
1\\
1\\
\end{bmatrix}=
\begin{bmatrix}
a&1&0&0\\
0 & 1&2&1\\
0 & 0&1&1\\
0 & 0&0&1\\
\end{bmatrix}\times
\begin{bmatrix}
G_{i - 1}\\
i^2\\
i\\
1
\end{bmatrix}=
\begin{bmatrix}
G_{i}\\
(i + 1)^2\\
i + 1\\
1
\end{bmatrix}
\end{equation*}

 

$$G_i = a\times G_{i - 1} + i^3$$

\begin{equation*}
\begin{bmatrix}
a&1&0&0&0\\
0 & 1&3&3&1\\
0 & 0&1&2&1\\
0 & 0&0&1&1\\
0 & 0&0&0&1\\
\end{bmatrix}^{i}*
\begin{bmatrix}
G_{0}\\
1\\
1\\
1\\
1\\
\end{bmatrix}=
\begin{bmatrix}
a&1&0&0&0\\
0 & 1&3&3&1\\
0 & 0&1&2&1\\
0 & 0&0&1&1\\
0 & 0&0&0&1\\
\end{bmatrix}\times
\begin{bmatrix}
G_{i - 1}\\
i^3\\
i^2\\
i\\
1
\end{bmatrix}=
\begin{bmatrix}
G_{i}\\
(i + 1)^3\\
(i + 1)^2\\
i + 1\\
1
\end{bmatrix}
\end{equation*}

 

 

$$G_i = a\times G_{i - 1} + b^i$$

\begin{equation*}
\begin{bmatrix}
a&1\\
0 & b\\
\end{bmatrix}^{i}\times
\begin{bmatrix}
G_{0}\\
b\\
\end{bmatrix}=
\begin{bmatrix}
a&1\\
0 & b\\
\end{bmatrix}\times
\begin{bmatrix}
G_{i - 1}\\
b^{i}\\
\end{bmatrix}=
\begin{bmatrix}
G_{i}\\
b^{i+1}\\
\end{bmatrix}
\end{equation*}

 

 

參考資料

深刻淺出矩陣快速冪及其簡單應用

相關文章
相關標籤/搜索