LearnGL - 學習筆記目錄
本人才疏學淺,如有什麼錯誤,望不吝指出。
上些篇:
這一篇:瞭解 行列式(determinant)、逆矩陣(inverse)、伴隨矩陣(adjugate、adjoint)、餘子式(cofactor、minor)、代數餘子式(algebraic cofactor、minor)。
逆矩陣-inverse matrix
在計算機3D圖形學中,在實現一些特殊功能的時候,特別是一些座標還原,或重構成其他座標時,都可能會用到逆矩陣。
一個變換矩陣可以將我們傳入的向量(組)變換到此變換矩陣中的另一個座標系中,如果我們要撤銷這些變換呢,那麼就需要逆過來變換,這個逆變換的矩陣,就叫:逆矩陣。
我們都知道,標量乘以標量,如:1×n=n,就是1乘以任意數,等於那個數的本身。
同樣在矩陣中也有類型的,如:I⋅M=M,I是單位矩陣(左上角到右下角都是1,其餘都是0的方陣)乘以任意矩陣都等於那個矩陣的本身。
1×n=n,同是n1⋅n=1,而這個n1就是n的倒數。
同樣的也有:M−1⋅M=M⋅M−1=I,而這其中的M−1就是M的逆矩陣。
在求逆矩陣時,需要用到行列式的功能。
行列式-determinant
先來講一下,determinant - 矩陣行列式(這裏我覺得中文翻譯的理解不好),爲啥要一個“式”字結尾,感覺會誤導很多人,可以參考這篇文章的講解:行列式determinant到底是個啥?,其實 determinant 就是求一個矩陣的N個軸(N個列向量,N>0,範圍:1,2,…,n)圍起來的空間大小(一維)、面積(二維)、體積(三維)、無法描述的變換空間大小(大於三維),最終的值是一個標量,注意:行列式結果是一個標量,並不是什麼式子、也不是向量。
所以可以理解,它就是表述一個變換的基的包圍空間的大小。
變換:
- 一維中,假設一個2,就是可以把某個東西可以變大2倍的變換數值,它的determinant就是2,長度。
- 二維中,假設x(0.5,0)y(0,2),就是可以讓一個存在於二維下的數值對象(a,b)可以讓它在x(0.5,0)y(0,2) 的變換後的結果:[x∣y∣]⋅(ab)→[0.5002]⋅(ab)=(0.5⋅a2⋅b),相當於把a縮小一倍,b放大一倍。它的determinant就是0.5*2=1面積。
- 同理三維中的是體積。
但上面都是比較理想的變換矩陣,就是他們的每列向量之間都是垂直的(正交的)所以求他們圍起來空間,直接向量它們的向量長度就可以了。
但是如果他們不是垂直的(正交的),這個時候就需要將每個軸之間x,y,z分量上有參差的量圍成的空間給減去,才能得到最終的空間。
如上圖,a(a1,a2),b(b1,b2)向量就是不互相垂直的,黃色就是a(a1,a2)與b(b1,b2)向量x,y分量上參差的分量圍起來的面積,着黃色部分的面積就是整個長方形需要減去的面積,最終得到的面積就是a,b向量圍起來的平行四邊形的面積。
也可以從百度百科中的:矩陣行列式的公式:det(acbd)=ad−bc 來發現一些線索。
如果b,c爲零呢?det(acbd)=ad−bc→det(a00d)=ad−0=ad,那就是向量(a,0)與向量(0,d)垂直了,前者平行於x軸,後者平行於y軸,這時面積就等於ad了,如下圖
所以 行列式就是求變換矩陣的軸包圍的一個空間大小,只不過在這個二維空間單位是面積,三維就是體積了,一維就是長度。
假設有矩陣A:[acbd]
det(A),或是矩陣A的行列式 又或是記作∣A∣,那麼:det(A)=∣A∣=ad−bc
那麼矩陣A的逆矩陣,記作A−1,那麼A−1=∣A∣1⋅[d−c−ba]=ad−bc1⋅[d−c−ba]
暫且我們不管這個矩陣[d−c−ba]是怎麼來的,它看起來就像是a與d交換,b,c添加負數了。這個矩陣叫作:伴隨矩陣。後面再詳細的搬運一波內容。
這個是2X2矩陣的逆矩陣,我們可以驗證一下:
那麼繼續逆矩陣的內容
有一個矩陣A:
A=[acbd]=[84−26]
A的逆矩陣爲:
A−1=ad−bc1⋅[d−c−ba]
A−1=8⋅6−(−2)⋅41⋅[6−4−(−2)8]
A−1=48+81⋅[6−428]
A−1=561⋅[6−428]
A−1=[6⋅561−4⋅5612⋅5618⋅561]
A−1=[566−564562568]
OK,現在計算得A−1=[566−564562568],然將與A=[84−26]相乘,結果看看是否等於I:
A−1⋅A=I
[566−564562568]⋅[84−26]=[1001]
[566⋅8+562⋅4−564⋅8+568⋅4566⋅(−2)+562⋅6−564⋅(−2)+568⋅6]=[1001]
[5648+568−5632+563256−12+5612568+5648]=[1001]
[5656005656]=[1001]
[1001]=[1001]
OK,計算完畢。反過來的:A⋅A−1=I也是一樣的。
還有另一箇中計算:Matrix Inverse – 逆矩陣,這種方式可視化是以行向量來分析求解的。
而我們上面使用的是列,建議使用列的方式,更加貼合各種軟件中計算。
矩陣的逆矩陣不一定存在,由幾種方式可以判斷來,先看看二維的逆矩陣的公式:
A−1=∣A∣1⋅[d−c−ba]=ad−bc1⋅[d−c−ba]
此公式中:det(A)=∣A∣=ad−bc
如果∣A∣=0,那麼這個式子就A−1的公式中會有除以0而無意義。
因爲∣A∣=ad−bc=0,所以A−1矩陣有無意義,會是否存在,取決於A矩陣列向量的值。
如果細心觀察,可以看出:A=[acbd],矩陣A的第一列向量是:[ac],第二列向量是:[bd],而∣A∣=ad−bc,其實就是:兩列向量的叉乘:[ac]×[bd]=ad−bc。
兩個非零向量叉乘的結果爲0的時候就只有這兩個向量是:相同方向,或是相反方向的,即:兩向量共線。
所以如果一個二維變換矩陣的列向量如果向量是共線的,那麼該矩陣就沒有逆矩陣。
而按行列式,或是determinant的理解就是:如果一個二維變換矩陣的行列式爲0,就是變換矩陣的基圍起來的空間大小爲0(這裏的大小至少是面積、體積,或以上),那麼此變換矩陣沒有逆矩陣。
所以在判斷二維矩陣的逆矩陣是否存在,可以根據以下幾種方式:
- 用式子來總結:A−1=∣A∣1⋅[d−c−ba],當∣A∣=0,則A−1無意義,則A的逆矩陣A−1不存在。
- 而:∣A∣=0,也代表:ad−bc=0,也代表:ad=bc,也代表:ba=dc,則A不存在逆矩陣。
- 而:ba=dc,也可以看成是:k=ba=dc,類似直線斜率的方式,就像 Matrix Inverse – 逆矩陣 中最後是以[acbd]中的量行的數值分別當作是直線的斜率:ba=dc,則A也不存在逆矩陣。
- 而:ad=bc,除了可以推導出:k=ba=dc,也一樣可以按:ca=bc來判斷,這樣就是列向量當作直線,並比較它們兩的斜率。
上面的直線斜率的方式,可以以這種方式來看待矩陣,就像是直線方程組,把矩陣的元素當作是方程組中的係數:
[acbd]⋅[xy]=[ef]
它其實就是:
[ac]⋅x+[bd]⋅y=[ef]
寫成:
ax+by=eax+dy=f
變形以下:
by=−ax+edy=−ax+f
再變形:
y=−bax+bey=−dax+df
OK,在看看,這個與我們的直線方程的:y=kx+b,是否很像?
所以我們可以把:k:−ba=−da,看作是斜率,然後等式兩邊的符號,就成了:k:ba=da
然後是直線的上下位移量:b:be=df,但是直線方程上的b不影響行列式,因爲b只會影響直線之間是重疊共線還是平行。
所以兩個直線的斜率 k 相等了,就平行或共線了,那麼矩陣的行列式就爲0,那麼也就不存在逆矩陣了。
還可以理解爲直線方程組的各項未知數(元)的係數的話,那麼說明兩直線無x,y的解,此直線方程無根。因爲他們是共線(無窮個交點,無窮個解)或平行(一個交點都沒有,一個解都沒有)了。兩條直線方程有解的話,說明有一個x,y點是兩條直線的交點。
這類n階方陣的矩陣如果 不存在 逆矩陣(determinant 或 行列式 爲 0),則稱爲:奇異 矩陣。
矩陣如果 存在 逆矩陣(determinant 或 行列式 不爲 0),則稱爲:非奇異 矩陣。
也可以參考百度百科的:奇異矩陣,奇異矩陣也叫:singular matrix。
最後附上形象一些的圖像:
可以看到,當矩陣中的兩列向量在共線後,四邊形的面積就爲0了(紫色的區域大小爲0),也就是行列式爲0,那麼這樣的矩陣是沒有逆矩陣的。
伴隨矩陣-adjugate、adjoint matrix
伴隨矩陣英文叫:adjugate matrix,也叫 adjoint matrix。
也可參考百度百科中說明:伴隨矩陣。
早在之前的其他文章中有表述過,數學是需要很高洞察力的學科,需要去觀察它的規律。
然後聰明的人類發明了數學公式符號來表示這些規律,如:斐波那契數列、對數,指數,三角函數,等等,等等,這些都有一些簡潔的公式,這裏就不一一搬磚了,可自行搜索。
而伴隨矩陣也是被這些數學家們發現了它的特性,它的作用,也可以用於計算出逆矩陣而用的。
(這裏我猜測是數學家們先計算出了逆矩陣的方法,可能計算過程太過於繁瑣,然後提煉到最精簡的結果時,發現了這一結果是可以從現有矩陣中的數值來求出來的,但它到底是怎麼被發現的,這一歷史沒有記錄,我也搜索不到,但這些過程是非常有參考意義的,我也驚訝於竟然沒有記錄,如果有記錄的話,會對後人可能有更多的理解,或是更多特性的發現)
下面我引用百度百科中的一句:
如果二維矩陣可逆,那麼它的逆矩陣和它的伴隨矩陣之間只差一個係數,對多維矩陣也存在這個規律。
照搬一下之前的求矩陣A的逆矩陣的公式:
A−1=∣A∣1⋅[d−c−ba]=ad−bc1⋅[d−c−ba]
矩陣 [d−c−ba] 就是伴隨矩陣。
留意上面引用百度百科說的,它(伴隨矩陣)與逆矩陣就差一個係數,這個係數就是:determinant的倒數,即:矩陣行列式的倒數:det(A)1=∣A∣1,二維中(或說是二階方陣) 的這個係數就是:ad−bc1。
就像上面所說的,至於這個伴隨矩陣是怎麼得來的,之前我只是簡單的描述的一下:
“矩陣[d−c−ba]是怎麼來的,它看起來就像是a與d交換,b,c添加了負數。”
這是我個人觀察的規律,但其實它是由一段公式計算出來的,只不過二維矩陣的伴隨矩陣比較簡單的規律。
但如果要計算N維(或說N階)矩陣時,還是需要用公式來計算好些,因爲大於二維以上的矩陣的伴隨矩陣的計算複雜度會越來越大。
說是有一個公式可以算出來的,但伴隨矩陣的式子又是怎麼來的,我們也不知道,查找的資料中沒有收錄這方面的信息,它的公式是:
矩陣A的伴隨矩陣,記作:A∗,它的式子爲:
注意:A∗的每項元素做了調整從:A行列位置上的值調整爲:A列行的值(注意下標左邊還是表示 行,右邊的還是表示 列,上面說的只是他們的轉置情況)。可以理解爲 伴隨矩陣 是 處理了 轉置過 的,就是 行和列對調過 的。
A∗=⎣⎢⎢⎢⎡A11A21⋮An1A12A22⋮An2⋯⋯⋱⋯A1nA2n⋮Ann⎦⎥⎥⎥⎤
這是錯誤的,之前沒有留意到這個下標不一樣,導致我在後續驗算是發現結果不對,-_-!!!:
下面的結果是 轉置後 的,纔是對的:
A∗=⎣⎢⎢⎢⎡A11A12⋮A1nA21A22⋮A2n⋯⋯⋱⋯An1An2⋮Ann⎦⎥⎥⎥⎤
矩陣中的元素Aij是 代數餘子式,下面會講到。
假設有個3x3矩陣A:
A=⎣⎡a11a21a31a12a22a32a13a23a33⎦⎤
要求伴隨矩陣,我們又要先了解下面要講到:餘子式:Mij。後面再加以詳細的搬磚。
我們要求得矩陣A的伴隨矩陣就等於:它的矩陣A每一項元素的 代數餘子式。這裏又有一個術語:代數餘子式:Aij=(−1)i+j⋅Mij(如果要我用一個表情,我會用一個:捂臉哭笑的表情)。
餘子式:Mij 與 代數餘子式:Aij=(−1)i+j⋅Mij 在公式上看的話,後者會多了一個符號的計算:(−1)i+j。後面在加以詳細的搬磚。現在先跳過,那麼繼續伴隨矩陣的計算:
所以A∗ 中每一項 代數與字數 也等於 帶符號 的 餘子式 :
A∗=⎣⎢⎢⎢⎡A11A12⋮A1nA21A22⋮A2n⋯⋯⋱⋯An1An2⋮Ann⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡(−1)1+1⋅M11(−1)1+2⋅M12⋮(−1)1+n⋅M1n(−1)2+1⋅M21(−1)2+2⋅M22⋮(−1)2+n⋅M2n⋯⋯⋱⋯(−1)n+1⋅Mn1(−1)n+2⋅Mn2⋮(−1)n+n⋅Mnn⎦⎥⎥⎥⎤
在我們這個3x3矩陣A例子中就是(注意行列 下標 是 轉置 過的):
A∗=⎣⎡A11A12A13A21A22A23A31A32A33⎦⎤
餘子式-Cofactor、Minor
餘子式:英文叫:Cofactor,或:Minor,有輔助因子,或是輔助項,次級項,等意思。
餘子式它的符號記作:Mij,i和j分別是第i行和第j列的意思,Mij代表的就是減去第i行和第j列後的n-1方陣的行列式。這裏先來個簡述,後面會重複強調意思。
而中文的理解中在於:“餘”字。然後又是令人費解的“式”字。
- 餘字:減去對應的的行與列之後剩餘的所有項,重組的新的n-1方陣。
- 式字:教你理解這個“式”子的方法,你可以把它理解成編程語言中的:表達式。而編程中的表達式最終會有一個值的,所以式就是表達式,就是會得到一個值的式子。(前面說的 行列式 中的“式”字也是可以這麼理解的)
因爲它是從原始n方陣中,減去對應的的第i行與第j列之後剩餘的所有項,重組的新的n-1方陣的 行列式(注意它是一個餘項組成而成的n-1方陣的行列式(determinant),所以它將會是一個值):
還是用回上面的個3x3矩陣A:
A=⎣⎡a11a21a31a12a22a32a13a23a33⎦⎤
中,假設我要求第2行第1列的餘子式,先分兩步:
- 先得出 餘項,因爲是原來的一部分的項目,所以我們叫:餘子項
- 再根據 餘子項 來求這個 餘子項行列式,所以簡稱 餘子式
餘子項
第2行第1列的餘子項:
先將要刪除的第2行第1列的所有項的用紅色標出來:
A=⎣⎡a11a21a31a12a22a32a13a23a33⎦⎤
再將這些項刪除:
A=⎣⎡刪除刪除刪除a12刪除a32a13刪除a33⎦⎤
刪除,得到 餘子項:
B21=[a12a32a13a33]
餘子式
然後是這個用這個 餘子項 來來計算矩陣B 的 行列式(determinant):
那麼 餘子式 記作 Mij,我們的i是2,j是1:
M21=det(B21)=[a12a32a13a33]=a12⋅a33−a13⋅a32
然後是:其他的Mij都可以使用這種方法計算出對應3x3矩陣中的每一項元素的餘子式。
代數餘子式-Algebraic cofactor、Minor
我們前面說了伴隨矩陣就是每一項的 代數餘子式。
而 餘子式 與 代數餘子式 的區別前面也說過,這裏再重新強調一下:
餘子式:Mij 與 代數餘子式:Aij=(−1)i+j⋅Mij 在公式上看的話,後者會多了一個符號的計算:(−1)i+j。
那第i=2,j=1的Aij=A21:
A21=(−1)2+1⋅M21
A21=(−1)3⋅M21
A21=−M21
那麼我們將之前3x3的矩陣A的每一項元素的 代數餘子式 計算得出:
(上面的矩陣A跑太遠了,這裏再複製過來,方便閱讀)
A=⎣⎡a11a21a31a12a22a32a13a23a33⎦⎤
矩陣A的伴隨矩陣A∗爲每一項的 代數餘字數 (注意行列 下標 是 轉置 過的):
A∗=⎣⎡A11A12A13A21A22A23A31A32A33⎦⎤
因爲:Aij=(−1)i+j⋅Mij,所以式子可以調整爲:
A∗=⎣⎡(−1)1+1⋅M11(−1)1+2⋅M12(−1)1+3⋅M13(−1)2+1⋅M21(−1)2+2⋅M22(−1)2+3⋅M32(−1)3+1⋅M31(−1)3+2⋅M32(−1)3+3⋅M33⎦⎤
A∗=⎣⎡(−1)2⋅M11(−1)3⋅M12(−1)4⋅M13(−1)3⋅M21(−1)4⋅M22(−1)5⋅M23(−1)4⋅M31(−1)5⋅M32(−1)6⋅M33⎦⎤
爲了對齊,我把正負號的 代數餘字數 的 + 也標出來,因爲它就是帶符號的 餘子式:
A∗=⎣⎡+M11−M12+M13−M21+M22−M23+M31−M32+M33⎦⎤
伴隨矩陣-二維的例子
那麼來計算最簡單的二維矩陣,求:二維矩陣的伴隨矩陣。
就用我們前門最簡單的2x2矩陣:A=[acbd],它的 伴隨矩陣 是:A∗=[d−c−ba]
下面來看看它的A∗是怎麼計算得來的(注意行列 下標 是 轉置 過的):
A∗=[A11A12A21A22]
那看看A11,A12,A21,A22分別都是怎麼計算的:
M11=[acbd],刪除1行1列:M11=[d],A11=(−1)1+1⋅M11=(−1)2⋅[d]=d,所以A11=d
M12=[acbd],刪除1行2列:M12=[c],A12=(−1)1+2⋅M12=(−1)3⋅[c]=−c,所以A12=−c
M21=[acbd],刪除2行1列:M21=[b],A21=(−1)2+1⋅M21=(−1)3⋅[b]=−b,所以A21=−b
M22=[acbd],刪除2行2列:M22=[a],A22=(−1)2+2⋅M22=(−1)4⋅[a]=a,所以A22=a
OK,現在A11,A12,A21,A22都計算好了。
分別代回伴隨矩陣(注意行列 下標 是 轉置 過的):A∗=[A11A12A21A22]=[d−c−ba]
所以你會看到這個結果就是我之前說的結果。
二維矩陣的伴隨矩陣很簡單,所以直接總結爲之前說的簡單處理:“矩陣[d−c−ba]是怎麼來的,它看起來就像是a與d交換,b,c添加了負數。”
再根據之前描述說的,伴隨矩陣 和 逆矩陣 就差一個 係數值,這個係數值就是 矩陣的行列式的倒數
然後我們使用 矩陣的行列式的倒數 的結果,數乘 上 伴隨矩陣 得到的就是 逆矩陣
矩陣的行列式 . 伴隨矩陣 = 逆矩陣:A−1=∣A∣1⋅A∗
如果已知 逆矩陣、矩陣行列式,那麼 伴隨矩陣 : A∗=∣A∣⋅A−1
總結 & 練習
前面說明,逆矩陣A−1作用就是將某個向量A 應用了矩陣A變換成的結果B 的結果再反向變換回矩陣變換前的A:
A⋅A=BA−1⋅B=A
這種應用是非常廣泛的,矩陣Unity中大家都應該熟悉的:GameObject
下的 Transform
組件類有 LocationPosition
與 Position
:
LocationPosition
是對象的局部座標
Position
是世界座標
假設有GameObject A,B,C,他們的父級關係是:A是B的父級,B是C的父級:
A
|
+--->B
|
+--->C
如果A,B有縮放、旋轉、位移過,這時想在腳本里設置C的世界的標對齊到場景中某個對象D的世界位置對齊。
爲了方便,直接設置某個C的Position等於D的Position即可,僞代碼如下:
GameObject A, B, C, D;
void Start() {
B.Transform.SetParent(A.Transform);
C.Transform.SetParent(B.Transform);
D.Transform.SetParent(.....);
}
void C_Locate_to_D() {
C.Transform.Position = D.Transform.Position;
}
主要看:C.Transform.Position = D.Transform.Position;
這句即可,它內部處理起始用了逆矩陣來更新LocationPosition
,這樣外部用起來就相當方便了。
Transform
類的代碼中,設置 Position
的僞代碼大概如下:
class Transform {
Matrix4x4 local2worldMatrix;
Matrix4x4 world2localnMatrix;
public Vector3 Position {
get => position;
set {
position = value;
localPosition= world2localnMatrix.MultiplePoint(position);
}
}
public Vector3 LocationPosition {
get => localPosition;
set { localPosition= value; }
}
private Vector3 position;
private Vector3 localPosition;
}
這就有點像是上面說過的:
假設有一個 局部座標 到 世界座標 的變換矩陣 Matlw2(lw2==Local To World 的簡寫):
Matl2w=[acbd]
然後是 Matl2w 的逆矩陣 Matw2l(w2l==World To Local),它的作用是 世界座標 到 局部座標 的變換:
Matw2l=ad−bc1⋅[d−c−ba]
然後是 局部座標 Posl(l==Local):
Posl=[xlyl]
然後是 世界座標 Posw(w==World):
Posw=[xwyw]
那麼 將 局部座標 變換到 世界座標:
Matl2w⋅Posl=Posw
[acbd]⋅[xlyl]=[xwyw]
那麼 將 世界座標 變換到 局部座標:
Matw2l⋅Posw=Posl
ad−bc1⋅[d−c−ba]⋅[xwyw]=[xlyl]
最後的通過設置世界座標的 Posw 時,我們是已知 Posw 世界座標點的,和 Matw2l 都是已知的,求 Posl 是肯定可以的。
Matw2l⋅Posw=Posl
但最終的終極理解這個變換,你可以這麼理解(可參考 可汗學院 裏的視頻:Matrix world problem: vector combination):
Matw2l的第一列向量就是w2l 座標系下的 X 軸:[x1y1],第二列向量就是 w2l 座標系下的 Y 軸:[x2y2],這兩個軸分別縮放 [xwyw] 後,再相加(兩個軸,或是兩個向量相加),就等於 [xlyl]:
[x1y1x2y2]⋅[xwyw]=[xlyl]
調整爲:
[x1y1]⋅xw+[x2y2]⋅yw=[xlyl]
調整爲文字+公式:
W2L的軸x⋅xw+W2L的軸y⋅yw=局部坐標(W2L的軸x向量,放大,世界坐標的xw倍)+(W2L的軸y向量,放大,世界坐標的yw倍)=局部坐標
(但注意這裏的W2L的X,Y軸,可能都是縮放、旋轉過的)
然後我在 可汗學院 的小考的裏題目做測試,帶上鼠標繪製(我的畫板不見了)的過程與提交結果:
References