Matrix
程度★ 難度★★
矩陣
矩陣應該不必再介紹,各位在高中數學課都學過,大學線性代數課程也會教。
有兩種資料結構可以儲存一個矩陣,一種是使用陣列,另一種則是使用矩陣元素的位置列表。
矩陣資料結構:Array
建立一個二維陣列就可以當作矩陣。如果你喜歡的話,也可以用一維陣列當作矩陣,然後自己算索引值。
UVa 10855 11360
矩陣資料結構:Sparse Matrix
就是把矩陣元素一個一個列出來,標明行與列。如果矩陣元素為零則不列出來,就這麼簡單。
Matrix Muliplication
程度★ 難度★★
Strassen's Algorithm
http://en.wikipedia.org/wiki/Strassen_algorithm
首先把兩個矩陣相乘,改成兩個一樣大的方陣相乘。把矩陣改成稍大的方陣,長寬是2的次方,多出來的元素全部補零。
原理是Divide and Conquer,欲相乘的兩個方陣,各自切割成四塊小方陣,然後利用小方陣的乘積,兜出大方陣的乘積。
僅是單純的拆成小方陣相乘,仍然需要8次乘法,以及4次加法,把遞迴公式列出來後,運用Master Theorem,可以求出時間複雜度為O(N^3)。根據遞迴公式,要讓時間複雜度變小,主要取決於乘法的次數,至於加法的次數不是主要影響因素。
經過一些詭異的調整方法,使用7次乘法,做些加加減減,就兜出了大方陣的乘積。時間複雜度變為O(N^log27),實際取log一下,約為O(N^2.81)。
Virginia-Vassilevska-Williams Algorithm
http://www.cs.berkeley.edu/~virgi/matrixmult.pdf
當今世上最快的矩陣相乘演算法,時間複雜度為O(N^2.3727)。不過方法相當複雜,我也不懂。
矩陣相乘的速度究竟可以到達多快?
這是一個open problem,目前還沒有人知道答案的問題。
由於兩個方陣共有2*N^2個元素,光是讀取資料,這些元素不得不處理一次,所以時間複雜度的下界顯然是Ω(N^2)。據我所知,目前也尚未有人找出更高的下界。
至於上界就是剛剛提到的O(N^2.3727)。
Gaussian Elimination
程度★ 難度★★★
Gaussian Elimination
「高斯消去法」是把一個矩陣,化成對角線元素皆為一的上三角矩陣。
高斯消去法在高中數學課本、線性代數書籍都有介紹。主要用途是解聯立線性方程式、計算矩陣的determinant。
高斯消去法的過程大意是:按照字典順序窮舉一對一對的row,每窮舉出一對row,就處理這兩個row──求出首項係數的倍率,以上方row消去下方row,使下方row的首項係數變成零。
有一個特殊情況是,當上方row的首項係數是零的時候,就要考慮與下方row交換,讓上方row的首項係數盡量不是零。
這個交換row的動作稱做pivoting,不為零的那一個首項係數稱做pivot,包含pivot的那一個row稱做pivot row。
高斯消去法的過程,以row的角度來看,只有三種row運算:倍率、相減、交換。但是實作時,我們通常不會特地寫一個row的資料結構、定義這三種運算,因為程式結構太過複雜的話,程式執行速度也會變慢。實作時,通常是自己慢慢數索引值,小心的從二維陣列中取得元素,逐步完成row運算。
時間複雜度是O(N^2 * M),N×M為矩陣的大小。由於一般情況都是討論方陣較多,N與M相等,所以會把時間複雜度寫成O(N^3)。
下面提供方陣的高斯消去法程式碼;至於一般矩陣的高斯消去法,就留給大家自行練習。
解聯立線性方程式
矩陣參數化、完成高斯消去法之後,使用Iterative Method,從最後一個row開始,把目前解出的未知數反覆代入到上一個row,求得每一個未知數。
這個計算過程,由於是從最後一個未知數開始計算,而不是從第一個未知數開始計算,故命名為「逆向代入(back substitution)」。逆向代入的時間複雜度是O(N^2)。
如果要讓逆向代入的誤差變小,可以在進行高斯消去法的時候,總是把首項係數絕對值最大的row,挪到最上方,再消去餘下的row。
UVa 10109 10524 10828
計算一個方陣的determinant
一個方陣進行高斯消去法,但是保留pivot row的原有倍率。最後的上三角矩陣,其對角線元素的乘積,便是determinant。
如果矩陣裡都是整數,那麼determinant也會是整數。要避免浮點數誤差,可以使用輾轉相除法進行消去。時間複雜度是O(N^3 * logC),C是過程當中,絕對值最大的首項係數。
UVa 684
LUP Decomposition
「LUP分解」是利用高斯消去法,將一個方陣化為下三角矩陣L、上三角矩陣U、列交換矩陣P,三者的乘積。時間複雜度為O(N^3)。
有時候列交換矩陣P恰好等於單位矩陣I,而不需要把列交換矩陣P寫下來,此時「LUP分解」可簡單稱作「LU分解」。
http://ccjou.wordpress.com/2010/09/01/
LUP分解的用途是解聯立線性方程式Ax = b,當A固定,b有許多組要解,每次求解僅需時O(N^2)。若是單純的使用高斯消去法,針對每一組不同的b,每次求解皆需時O(N^3)。
一、row交換矩陣,調換參數向量的維度順序。 二、下三角矩陣,順向代入(forward substitution)。 三、上三角矩陣,逆向代入(back substitution)。
筆者線性代數學得不好,不敢多說。詳情請參考線性代數課本。
Gauss-Jordan Elimination
高斯消去法的延伸版本。矩陣參數化,把原矩陣的對角線化成一、其餘元素化為零。
用途是解聯立線性方程式、計算逆矩陣。時間複雜度與高斯消去法相同,仍是O(N^3)。
解聯立線性方程式,論效率,高斯消去法是比較好的選擇:高斯消去法暨逆向代入的總步驟數,比高斯喬登消去法還要少。論程式碼長度,高斯喬登消去法是比較好的選擇:只消修改一下高斯消去法的消去範圍,即可得到解,而不必逆向代入。
Linear Transformation
程度★ 難度★
Linear Transformation
【註:想要詳細了解線性變換,建議修習資工系線性代數課程。以下只做簡介。】
一個函數,由變數的加減法、變數的倍率所組成,稱作「線性變換」。小心,不是「線性函數」喔,因為此詞彙已被古人當作是長相為直線的函數!
f(x,y,z) = 1 x + 2 y - 5 z
f(x) = 1 x + 2 x^2 - 5 x^3 把 x^2 與 x^3 重新看作是變數 y 和 z,整個看起來也是線性變換。 至於什麼樣的變數組合,才可以重新看作是線性變換的變數呢? 讀者可以研讀線性代數的線性空間、正交基底等等概念。
Linear Transformation v.s. Matrix
只要是線性變換,都可以寫成矩陣乘法的格式!
[ x ]
f(x,y,z) = [ 1 2 -5 ] * [ y ]
[ z ]
通常會把各個變數改名成 x1 x2 x3 ...
所有輸入變數拼在一起,成為一個向量 x
輸出變數叫做 y
[ x1 ]
y = [ 1 2 -5 ] * [ x2 ]
[ x3 ]
y = f ( x )
一般提到函數,輸入可以是多重變數,但是輸出一定是單一變數。一般提到線性變換,有了矩陣相助,輸入與輸出都可以是多重變數。
[ y1 ] [ 1 2 -5 ] [ x1 ] [ y2 ] = [ 2 4 6 ] * [ x2 ] [ y3 ] [ 3 1 7 ] [ x3 ] y = f ( x )
約定俗成,我們會把x與y排列成向量,而非矩陣。如此一來,只要知道了線性變換的矩陣,就完全知道了函數式子。
輸出變數、輸入變數排列成矩陣:
[ y1 y3 ] [ 1 2 -5 ] [ x1 x4 ]
[ y2 y4 ] = [ 2 4 6 ] * [ x2 x5 ]
[ x3 x6 ]
y = f ( x )
輸出變數、輸入變數排列成向量:
[ x1 ]
[ y1 ] [ 1 2 -5 0 0 0 ] [ x2 ]
[ y2 ] = [ 2 4 6 0 0 0 ] * [ x3 ]
[ y3 ] [ 0 0 0 1 2 -5 ] [ x4 ]
[ y4 ] [ 0 0 0 2 4 6 ] [ x5 ]
[ x6 ]
y = f ( x )
Linear Transformation v.s. Computer
電腦的主要功能,就是加減乘除運算,正好可以對應線性變換的定義──也就是說,電腦可以飛快的完成線性變換!
計算機科學的應用領域,幾乎是全部,傾向將真實問題故意寫成線性變換的模樣,為的就是飛快的計算。
最佳化問題(尤其是圖論)當中,能夠表示成線性變換的問題,通常都是P問題;無法表示成線性變換的問題,絕大部分成為了NP-Complete問題。
矩陣對於計算學的重要性可見一斑!
Companion Matrix
程度★ 難度★★★
Companion Matrix
線性的函數(線性變換)可以用矩陣表示,而線性的遞迴函數也可以用矩陣表示,該矩陣稱為Companion Matrix。
數列的第N項,化作矩陣的N次方。矩陣的N次方,只要用O(logN)次矩陣乘法就能求得,原理是Divide and Conquer,請參考「Fast Exponentiation」。
1.
recurrence:
f(n) = p * f(n-1) + q * f(n-2) + r * f(n-3)
f(2) = 2
f(1) = 1
f(0) = 0
another style:
f(0) = 0
f(1) = 1
f(2) = 2
a * f(n) + b * f(n+1) + c * f(n+2) = f(n+3)
2.
[ f(0) ] [ 0 ] [ f(n) ] [ 0 1 0 ]
F0 = [ f(1) ] = [ 1 ] Fn = [ f(n+1) ] A = [ 0 0 1 ]
[ f(2) ] [ 2 ] , [ f(n+2) ] , [ a b c ]
[ 0 1 0 ] [ f(0) ]
A * F0 = [ 0 0 1 ] * [ f(1) ]
[ a b c ] [ f(2) ]
[ 0 + 1*f(1) + 0 ] [ f(1) ]
= [ 0 + 0 + 1*f(2) ] = [ f(2) ] = F1
[ a*f(0) + b*f(1) + c*f(2) ] [ f(3) ]
重點在於最後一橫列。其他只是簡單位移。
A就是Companion Matrix。
3.
F1 = A * F0 = A^1 * F0
F2 = A * F1 = A^2 * F0
⋮ ⋮ ⋮
Fn = A * Fn-1 = A^(n-1) * F0
4.
[ f(f-2) ]
Fn-3+1 = [ f(f-1) ] = A^(n-3) * F0
[ f(n) ]
^^^^ here is f(n)!
仔細來說,數列的第N項,只要用O(log(N-M))次矩陣乘法就能求得。M是線性遞迴函數的次元數,也是Companion Matrix的長與寬。
做個結論。當線性遞迴函數為M次元,想計算數列的第N項時,有兩種計算方式:
一、Dynamic Programming:算出第一項到第N項,由小到大一項一項計算,答案儲存於表格。時間複雜度為O(N + (N-M) * M)。
二、Companion Matrix:只能算出第N項,利用Divide and Conquer計算矩陣次方。時間複雜度為O(log(N-M) * M^3 + M^2);此處設定矩陣相乘是O(M^3),矩陣相乘還可以更快。
當M是常數時,時間複雜度會更美麗,一為O(N),二為O(logN)。
此手法的精髓,是將資料以不同型態呈現。轉換數域後,利用新數域的特性,讓計算方式變得更簡潔,快速求得答案。影像處理的Hough transform也是一個好例子。
當遇到一個難纏的問題,不妨換換思考角度,以不同面向來看待資料,或許能找到不錯的新方法。
UVa 10518 10655 10743 10754 10870
Toeplitz Matrix
程度★★ 難度★★
Toeplitz Matrix(Diagonal-constant Matrix)
每一條左上右下斜線,都是同一個元素的矩陣。
[ 1 5 3 2 1 ] [ 2 1 5 3 2 ] [ 7 2 1 5 3 ] [ 4 7 2 1 5 ] [ 8 4 7 2 1 ]
2N-1個數字就能紀錄一個Toeplitz Matrix。兩個Toeplitz Matrix矩陣相加需時O(N),相乘需時O(N^2)。
矩陣乘法
關於循環摺積請見本站另一篇文章「Convolution」。
循環摺積是線性變換,其矩陣是循環的Toeplitz Matrix。
對於普通的Toeplitz Matrix,只要填充元素成為循環的Toeplitz Matrix,就能化做循環摺積,就能套用傅立葉轉換計算循環摺積。
[ 1 5 3 7 2 ]
[ 1 5 3 ] [ 2 1 5 3 7 ]
[ 2 1 5 ] ---> [ 7 2 1 5 3 ]
[ 7 2 1 ] [ 3 7 2 1 5 ]
[ 5 3 7 2 1 ]
一個N×N Toeplitz Matrix乘以一個N×1向量,時間複雜度為O(NlogN);乘以一個N×M矩陣、也就是乘以M個1×N向量,時間複雜度為O(M * NlogN)。
解聯立線性方程式(Levinson-Durbin Algorithm)
時間複雜度O(N^2),比高斯消去法O(N^3)來得快。
http://fzhhzf.blogspot.com/2011/03/levinson-durbin-toeplitz.html
【待補程式碼】
Basis(Under Construction!)
程度★★ 難度★★
QR Decomposition(QR Factorization)
Singular Value Decomposition
Eigenbasis(Under Construction!)
程度★★ 難度★★
Eigenvalue / Eigenvector
數學家發現,線性變換的本質,是在某些向量上面分別伸縮。這些向量稱作eigenvector,對應的伸縮比率稱作eigenvalue。
線性變換,是以eigenvector作為新座標軸,找到輸入向量的分量,每個分量各自按照eigenvalue伸縮之後,再度合而為一,就完成了線性變換。
反覆實施變換、不斷疊代,eigenvalue絕對值小於一的方向會趨近零,eigenvalue絕對值等於一的方向保持不動,eigenvalue絕對值大於一的方向趨近無限大。絕對值越小就縮短越快,絕對值越大就伸長越快,輸入向量將漸漸偏向絕對值最大的方向。
eigenvalue為虛數是比較特別的情況,雖然在虛數域是伸縮,但是在實數域是螺旋、繞圓。
【待補文字、圖片】
UVa 720
演算法(解方程式、方程式求根)
演算法可參考:http://yhlyu.wordpress.com/2010/12/10/
寫成數學式子就是A x = λ x,A是線性變換,λ是eigenvector,x是eigenvector。
想要找到eigenvalue與eigenvector,A x = λ x移項之後,運用det = 0,就可以化作多項式求根問題。同時說明了,N*N的線性變換,恰有N個eigenvalue,可能是虛數可能是實數。
但是由於eigenvector有著縮放的特性,所以一般不採用多項式求根演算法。
演算法(Power Method)
Iterative Method。只能得到絕對值最大的eigenvalue、eigenvector近似值,時間複雜度O(N^2 * K),K是疊代次數。
eigenvalue of A: |λ1| ≥ |λ2| ≥ |λ3| ≥ ... eigenvector of A: e1 , e2 , e3 若|λ1| > |λ2|,則A∞趨近e1的方向。 xk+1 = A xk / |u| u是絕對值最大者,用於穩定x長度。
演算法(Inverse Power Method)
B = (A - αI)-1 eigenvalue of B: 1/(λ1-α) , 1/(λ2-α) , 1/(λ3-α) ... α猜得準,會讓1/(λ-α)變很大,成為B的λ1。 此時套用Power Method即可得到1/(λ-α)。 xk+1 = B xk = (A - αI)-1 xk 此處化作 (A - αI) xk+1 = xk 就可採用 LU Decomposition,疊代一次時間複雜度O(N^2)。
時間複雜度是N次Power Method的時間。
演算法(QR algorithm)
http://www.prefield.com/algorithm/math/eigensystem.html
http://blog.csdn.net/xlvector/article/details/1667243
【待補文字】