1. 程式人生 > 其它 >線性代數入門

線性代數入門

1. 高斯消元

模板題

定義以下三種操作為 初等行變換

  1. 將某一行乘上 \(c\)
  2. 將某一行的 \(c\) 倍加到另一行
  3. 交換兩行

這裡介紹高斯-約旦消元法,利用初等行變換將矩陣轉化成對角矩陣,然後就很容易求出解。

  1. 列舉 \(i\) 表示現在要消去第 \(i\)
  2. \([i,n]\)​ 內選擇一個 \(k\)​ 滿足 \(a[k][i]\neq 0\)​,若不存在則說明無解
  3. 交換第 \(i\) 行和第 \(k\)
  4. 對於 \([i+1,n]\)​ 內所有 \(j\)​,將第 \(j\)​ 行減去 \(\dfrac{a[j][i]}{a[i][i]}\) 倍的第 \(i\) 行​

容易發現,最後第 \(i\)​ 項係數只會出現在第 \(i\)​ 行。並且任意時刻,第 \(i\)​ 行的 \(1\dots i-1\)​ 項係數都為 \(0\)​​,所以不會對其他方程的這些項造成影響。

inline bool zero(double t) {
	return fabs(t) < 1e-8;
}

signed main() {
	scanf("%d", &n);
	rep(i, 1, n) rep(j, 1, n + 1)
		scanf("%lf", &a[i][j]);
	rep(i, 1, n) {
		k = i;
		if(zero(a[i][i])) rep(j, i + 1, n) if(!zero(a[j][i])) { k = j; break; }
		if(i ^ k) rep(j, i, n + 1) swap(a[i][j], a[k][j]);
		if(zero(a[i][i])) return puts("No Solution") & 0;
		rep(j, 1, n) if(i != j) {
			t = a[j][i] / a[i][i];
			rep(k, i, n + 1) a[j][k] -= a[i][k] * t;
		}
	}
	rep(i, 1, n) printf("%.2lf\n", a[i][n + 1] / a[i][i]);
	return 0;
}

2. 行列式

對於一個方陣,其行列式是線性變換的伸縮因子。例如在三維空間中,行列式表示線性變換造成的體積放大倍率。

代數定義:\(\det A=|A|=\sum\limits_{p是n的排列}(-1)^{p的逆序對數}\prod\limits_{i=1}^na_{i,p_i}\)​​​

看右邊那串東西,其實是從方陣中選出 \(n\)​ 個數,使得沒有兩個數在同一行或同一列,然後把它們乘起來。所以如果選出的數中有一個 \(0\) 就沒有貢獻了。

排列的性質

  1. 交換兩數,逆序對數的奇偶性改變
  2. \(n\ge 2\) 時,奇偶排列(逆序對數為奇 / 偶數的排列)各佔一半

行列式的性質

  1. \(|M|=|M^T|\)
  2. 交換兩行,行列式反號
  3. 用一個數乘行列式的某行,等於用這個數乘此行列式
  4. 如果行列式中有兩行成比例,則行列式等於 \(0\)(先把比例係數提出來,發現兩行相同了,交換兩行行列式不變。但根據性質 2,行列式反號了,所以只能為 \(0\)
  5. 若行列式中某一行是兩組數之和,則這個行列式等於兩個行列式之和,這兩個行列式分別以這兩組數為該行,而其餘各行與原行列式對應各行相同。即:\(\left|\begin{matrix}\dots\\a_{i,1}+b_{i,1},a_{i,2}+b_{i,2}\dots a_{i,n}+b_{i,n}\\\dots\end{matrix}\right|=\left|\begin{matrix}\dots\\a_{i,1},a_{i,2}\dots a_{i,n}\\\dots\end{matrix}\right|+\left|\begin{matrix}\dots\\b_{i,1},b_{i,2}\dots b_{i,n}\\\dots\end{matrix}\right|\)
  6. 將某一行的倍數加到另一行,行列式的值不變
  7. \(\left|\begin{matrix}A~0\\C~B\end{matrix}\right|=|A||B|\)​,其中 \(A,B\)​​ 是方陣 (從選數的角度考慮)
  8. 一個矩陣的行列式可以按行展開,即:\(\left|\begin{matrix}\dots\\a_{i,1},a_{i,2}\dots a_{i,n}\\\dots\end{matrix}\right|=\left|\begin{matrix}\dots\\a_{i,1},0\dots 0\\\dots\end{matrix}\right|+\left|\begin{matrix}\dots\\0,a_{i,2},0\dots 0\\\dots\end{matrix}\right|+\dots+\left|\begin{matrix}\dots\\0,\dots 0,a_{i,n}\\\dots\end{matrix}\right|\)​​​​​(還是從選數的角度理解,第 \(i\)​​​​​ 行只能選一個數,選 \(a_{i,1},a_{i,2},\dots,a_{i,n}\)​​​​​ 的方案是獨立的)。進一步地,定義代數餘子式:\(A_{i,j}=(-1)^{i+j}|M_{i,j}|\)​​​​​,其中 \(M_{i,j}\)​​​​​ 是原矩陣刪去第 \(i\)​​​​​ 行和第 \(j\)​​​​​ 列得到的矩陣。一個矩陣的行列式,等於它某一行中,每個元素乘上它的代數餘子式的和。即:\(|B|=\sum\limits_{j=1}^nb_{i,j}A_{i,j}=(-1)^{i+j}b_{i,j}|M_{i,j}|\)​​​​​​​(展開後,每個矩陣的第 \(i\)​​​​​ 行只有一個數,用 \(i+j-2\)​​​​​ 次交換鄰行鄰列把它交換到 \((1,1)\)​​​​​,然後用性質 7)
  9. 上(下)三角矩陣的行列式為其主對角線元素之積。

行列式求值

模板題

現在考慮如何計算一個矩陣的行列式。觀察到第 9 條性質,我們可以把矩陣消成上三角矩陣,同時注意符號的變化,然後直接得出答案。

但當矩陣內都是整數時,仍會涉及實數運算,導致精度較低。考慮一種輾轉相除的方法:每次選定兩行 \(i,j\)​​,將第 \(j\)​​ 行儘可能多地減去第 \(i\)​​ 行,然後交換這兩行,直到 \(a_{i,i}=0\)​。這樣時間複雜度是 \(O(n^3+n^2\log V)\)​ 的。

rep(i, 1, n) rep(j, i + 1, n) {
	while(a[i][i]) {
		t = a[j][i] / a[i][i];
		rep(k, i, n) a[j][k] = (a[j][k] - a[i][k] * t % p + p) % p;
		swap(a[i], a[j]);
		ans = -ans;
	}
	swap(a[i], a[j]);
	ans = -ans;
}
if(ans < 0) ans += p;
rep(i, 1, n) (ans *= a[i][i]) %= p;

3. 矩陣求逆

模板題

對於矩陣 \(A\),若存在矩陣 \(B\) 使得 \(AB=I\)\(I\) 是單位矩陣),則 \(B\)\(A\) 的逆,也可寫作 \(A^{-1}\)

對矩陣進行高斯消元時,我們發現可以僅用初等行變換將矩陣變成單位矩陣,而每一種初等行變換都可以表示成一次矩陣乘法。即 \(AP_1P_2\dots P_k=I\)。那麼 \(P_1P_2\dots P_k\) 不就是答案嗎?

我們可以在高斯消元時記錄下所有操作,然後搞一個單位矩陣,再把這些操作做一遍。或者更方便地,我們在原矩陣右邊新增一個單位矩陣,把左邊消成單位矩陣,右邊就求出了逆。

若原矩陣不能消成單位矩陣,則原矩陣不可逆。

n = read();
rep(i, 1, n) rep(j, 1, n) a[i][j] = read();
rep(i, 1, n) a[i][i + n] = 1;
rep(i, 1, n) {
	if(!a[i][i]) rep(j, i + 1, n) if(a[j][i]) { swap(a[i], a[j]); break; }
	if(!a[i][i]) return puts("No Solution") & 0;
	tmp = poww(a[i][i]);
	rep(j, 1, n) if(i != j) {
		t = a[j][i] * tmp % P;
		rep(k, 1, n * 2) a[j][k] = (a[j][k] - a[i][k] * t % P + P) % P;
	}
	rep(j, i, n * 2) (a[i][j] *= tmp) %= P;
}
rep(i, 1, n) {
	rep(j, n + 1, n * 2)
		printf("%lld ", a[i][j]);
	putchar(10);
}

4. 特徵值與特徵向量

有時,對一個向量進行一次線性變換(左乘一個矩陣),這個向量方向不變,只是長度發生了變化,即 \(Av=\lambda v\)​​​。也可以表示成 \((A-\lambda I)v=0\)​​

對於一個矩陣 \(A\)​​​​,若存在數 \(\lambda\)​​​​ 和向量 \(v\)​​​​ 使得 \(Av=\lambda v\)​​​​,稱 \(\lambda\)​​​​ 為矩陣 \(A\)​​​​ 的 特徵值\(v\)​​​​ 為 特徵向量。同時定義 \(f(x)=|xI-A|\)​​​​ 為 特徵多項式\(|xI-A|=0\)​​​​ 為 特徵方程

定理 \(n\) 階矩陣 \(A\)\(n\) 個特徵值,為其特徵方程的 \(n\) 個根。特徵值 \(\lambda\)​ 對應的特徵向量為 \((\lambda I-A)x=0\)​ 的非零解。

注意一個特徵值對應無窮多個特徵向量,因為可以對向量進行數乘,仍滿足定義。

矩陣對角化

相似矩陣:對於兩個矩陣 \(A,B\)​,若存在可逆矩陣 \(P\)​ 使得 \(P^{-1}AP=B\)​,則 \(A,B\)​ 相似。

考慮矩陣 \(A\) 和對角矩陣 \(\Lambda=\left[\begin{matrix}\lambda_1\\&\lambda_2\\&&\ddots\\&&&\lambda_n\end{matrix}\right]\) 相似的條件。設矩陣 \(P\)\(n\) 個列向量構成即 \(P=[p_1,p_2,\dots,p_n]\),且 \(P\)​ 可逆

\(A[p_1,p_2,\dots,p_n]=[p_1,p_2,\dots,p_n]\Lambda\)\([Ap_1,Ap_2,\dots,Ap_n]=[\lambda_1p_1,\lambda_2p_2,\dots,\lambda_np_n]\)

那麼 \(Ap_i=\lambda_ip_i\)。這不就是特徵值和特徵向量的定義嗎?

於是有 \(A=P\Lambda P^{-1}\)​​​,其中 \(\Lambda\)​ 是由 \(A\)​ 的所有特徵值構成的對角矩陣,\(P\)​​ 的第 \(i\)​ 列是特徵值 \(\lambda_i\)​​​ 對應的特徵向量。

於是我們可以快速計算一些矩陣的冪,\(A^n=(P\Lambda P^{-1})^n=P\Lambda^nP^{-1}\)​。

遞推數列通項公式

這裡只討論二階齊次線性遞推數列。

首先我們可以直接用上文的結論。​比如斐波納契轉移矩陣 \(\left[\begin{matrix}1~1\\1~0\end{matrix}\right]\)​​​​,特徵方程 \(\left|\begin{matrix}\lambda-1~-1\\-1~\lambda\end{matrix}\right|=0\)​​​​ 即 \(\lambda^2-\lambda-1=0\)​​​​,解得 \(\lambda=\dfrac{1\pm\sqrt 5}{2}\)​​​​,並分別求出特徵向量 \([1,\dfrac{\sqrt 5-1}{2}]^T,[1,-\dfrac{\sqrt 5+1}{2}]^T\)​​​​​​。​然後一頓爆算求出 \(P\)​​​​​​ 的逆 \(\left[\begin{matrix}\dfrac{5+\sqrt5}{10}~\dfrac{\sqrt5}{5}\\\dfrac{5-\sqrt5}{10}~-\dfrac{\sqrt5}{5}\end{matrix}\right]\)​​​​​​。把 \(P,\Lambda^n,P^{-1}\)​​​​​​ 依次乘起來,最後乘上 \([1,0]^T\)​​​​​​,就可得出通項公式 \(f_n=\dfrac{\sqrt 5}{5}\left[(\dfrac{1+\sqrt5}{2})^n-(\dfrac{1-\sqrt5}{2})^n\right]\)​​。​

這麼麻煩,為什麼不用生成函式呢

考慮一個數列 \(f_i=pf_{i-1}+qf_{i-2}\)​,其中 \(f_0,f_1\) 已知

嘗試構造等比數列:設存在 \(a,b\) 使得 \(f_{i+1}-af_i=b(f_i-af_{i-1})\)​,即 \(f_{i+1}=(a+b)f_i-abf_{i-1}\)

發現 \(a,b\)​ 是方程 \(x^2-px-q=0\)​ 的根。而這個方程就是轉移矩陣的特徵方程

所以 \(\{f_{i+1}-af_i\}\)​ 是公比為 \(b\)​ 的等比數列,有 \(f_{i+1}-af_i=b^i(f_1-af_0)\)​。

又發現 \(a,b\) 也滿足 \(f_{i+1}-bf_i=a(f_i-bf_{i-1})\)

所以 \(\{f_{i+1}-bf_i\}\) 是公比為 \(a\) 的等差數列,有 \(f_{i+1}-bf_i=a^i(f_1-bf_0)\)​。​

兩式相減,得到 \(f_i=\dfrac{f_1-af_0}{b-a}b^i+\dfrac{f_1-bf_0}{a-b}a^i\)

其實並不用每次推。可以設 \(f_i=\alpha a^i+\beta b^i\)​,解一個方程就完了

比如斐波納契數列,\(a=\dfrac{1+\sqrt 5}{2},b=\dfrac{1-\sqrt 5}{2}\),設 \(f_i=\alpha a^i+\beta b^i\),則 \(\begin{cases}f_0=\alpha +\beta=0\\f_1=\frac{1+\sqrt 5}{2}\alpha+\frac{1-\sqrt 5}{2}\beta\end{cases}\)

解得 \(\alpha=\dfrac{\sqrt 5}{5},\beta=-\dfrac{\sqrt 5}{5}\),那麼 \(f_n=\dfrac{\sqrt 5}{5}\left[(\dfrac{1+\sqrt5}{2})^n-(\dfrac{1-\sqrt5}{2})^n\right]\)​。​

對於更高階的情況,記個結論:設 \(f_i=\alpha a^i+\beta b^i+\gamma c^i+\dots\)​,列方程組​

例題 - 塊速遞推

常係數齊次線性遞推

模板題

首先可以寫出一個 \(k\times k\) 的轉移矩陣:\(A=\left[\begin{matrix}f_1~f_2~f_3~\dots f_{k-1}~f_k\\1~~~0~~~0~~\dots~~~0~~~0\\0~~~1~~~0~~\dots~~~0~~~0\\0~~~0~~~1~~\dots~~~0~~~0\\\vdots~~~\vdots~~~\vdots~~~\ddots~~~\vdots~~~\vdots\\0~~~0~~~0~~\dots~~~1~~~0\end{matrix}\right]\)

初始矩陣 \(B=[a_{k-1},a_{k-2},\dots,a_0]^T\)​​,答案是 \((A^nB)_k\)​​。

凱萊-哈密頓定理:對於矩陣 \(A\)​​​,\(f(x)=|xI-A|\)​​​ 為其特徵多項式,有 \(f(A)=O\)​​​,其中 \(O\)​​​ 是零矩陣。

本題中,可以用行列式性質 7 和 8 計算出 \(f(x)=x^k-f_1x^{k-1}-f_2^{k-2}-\dots-f_k\)

那麼只需計算 \(g(x)=x^n\bmod f(x)=\sum\limits_{i=0}^{k-1}g_ix^i\)​,答案為 \((g(A)B)_k=(\sum\limits_{i=0}^{k-1}g_iA^iB)_k=\sum\limits_{i=0}^{k-1}g_i(A^iB)_k=\sum\limits_{i=0}^{k-1}g_ia_i\)​。

計算 \(g(x)\) 可以用倍增 + 多項式取模。時間複雜度 \(O(k\log k\log n)\)​。