線性代數學習之線性系統


今天是最后一天上班,所以打醬油的時間充分,想着明天就要坐上愛心的突突車返鄉了內心真是激動萬分呀,既然要過大年了,基本上期間寫博客的心思也應該跑得九霄雲外了,所以安耐住激動的心必須年前搞一發,也提前預祝各位賞光的大佬們春節快樂~~今年的學習其實落下很多,如kotlin項目、flutter項目、ios、java並發、jvm、算法與數據結構,java全棧,android架構。。。。還想學的有基金理財、python。。。所以新的一年對於落下的這些定會一個個完整的學完,只是時間的快慢,也想看自己在何時會放棄,期待自己放棄的那一天~~其實吧,人生不就是一場馬拉松比賽嘛,到達終點的方式很多,其中也會遇到各種各樣的問題,而只要保持耐力持之以恆的往前在走,享受其中的過程,慢慢到達預期的終點就行了,重點是按着自己的節奏一步一個腳印走就行了。。。。打住,先拐回來,還是開始這次的線性代數的學習。

在上一次https://www.cnblogs.com/webor2006/p/14271706.html中學習了矩陣變換在圖形學中的簡單應用,其中對待看待矩陣有一個非常重要的視角就是可以把矩陣看作是對一個系統的描述,更准確地來說是對線性系統的描述,所以這次來學習一下當把矩陣看作是對線性系統的描述之后如何用矩陣來解決線性系統的相關問題。

什么是線性系統:

其實對於線性系統就類似於初中小學所學的線性方程組的概念,比如這么一個二元一次的方程組:

那線性系統中的“線性”又是指啥呢?其實是指未知數只能是一次方項【如此方程式中的x,y都是一次方的】。 那反之,也存在非線性方程,比如:

其中第一個方程的x是2次方;第二個未知數z是1/2次方【開根其實就是1/2次方】;第三個x不是簡單的一次方了,而是三角函數,也不是線性的。另外說它們不是線性的還有一個視角就是用繪制函數圖像能夠非常清晰的看出來,上述三個非線性的方程對應如下三個函數圖像:

是不是可以看出非線程的圖像都是曲線,而非直線,而對於咱們所要研究的“線性”系統其實都是一條直線,比如對於一次方程:

它所對應的二維坐標就是直線,如下:

通過這個直觀的視角也能加深對於“線性”它所代表的含義,但是!!!對於線性這個詞不要就認為它永遠表示的是空間中的一條直線,比如對於式子中有三個未知數:

很顯然它也是滿足線性這個條件的,如果在三維坐標系里其實還表示一個平面,如下:

再回到主題,線性“系統”,上面也說了就是多個線性的方程組織到了一起:

而研究線性系統最為關鍵的原因就是解決線性系統,換言之就是解上面的方程組。這里要提到的是解方程組看似只是一個計算問題,隨着之后的線性代數的深入學習其實對於這樣的計算是非常有意義的,不僅僅是能將生活中實際的問題給建模成線性系統的樣子【如之前所舉例的經濟系統、化學系統、電路系統..】,與此同時解線性系統還能解決線性代數這個領域內部抽象的數學問題,不過目前來說只需要看如何來解這個線性系統就成了,而要解上面這個方程組用初中的數學思想就可以使用消元法,啥叫消元法呢?

簡言之就是對於一個線性系統中有N個未知數,先想辦法消去1個未知數讓其變為N-1個未知數,之后再消去一個未知數變為N-2個未知數,依此類推,最后消去只有一個未知數時是不是就能知道該未知數的解了,然后再求出其它未知數,具體咱們來做一次【復習一下初中數學】:

首先來消去x這個數,就讓上面那個方程式左邊兩邊都乘以3,然后再拿下面的方程式減去上面的方程式,就會變為:

此時再將該式子合並同類項,那么經過這次消元之后此方程組就變為:

這樣就可以求出y了,如下:

y求出來了,再代入第一個方程式,是不是x也可以求出來了?如下:

為了再鞏固一下方程組求解的消元法,再來看一下稍復雜一點的方程組【為啥再鞏固一下,因為下面有個概念的思想來自於它】,如下:

三個未知數,先消掉x,可以讓第二個方程減去第一個方程的3倍,所以可以是如下:

 接下來再用第三個方程減去第一個方程的2倍,所以可以是如下:

此時對於這兩個方程就消去了一個x,只有2個未知數了:

此時就可以把這倆個方程的y消除了,相加既可,如下:

此時未知數z就可以求出來了:

然后再將z代入到第二個方程,所以y也可以求出了,如下:

 

最后再代入第一個方程,其中整個解就出來了:

消元法:

對於以上方程組的求解過程就是消元法,最后抽象的總結一下消元法的過程如下:

1、一個方程的左右兩邊同時乘以一個常數;

2、一個方程加(減)另一個方程;

3、交換兩個方程的位置 ;

高斯消元法:

在上一次https://www.cnblogs.com/webor2006/p/14265708.html我們描述了對於一個線性系統也可以很輕松地用矩形進行表達,相應的也可以基於矩陣的操作來解決線性系統相關的問題,背后的思路也很簡單,這里還是以這個三元方程來例:

此時方程左邊的系數可以用矩陣表示為:

 而對於每一個方程的結果又可以表示如下列向量:

而對於未知數表示向量之后整個方程組就可以表示為:

然而在解決線性相關的問題時,可以將未知數給忽略掉,然后將左側的系數矩陣和右側的結果向量結合到一起,形成這樣:

另外再划一條豎線,以區分系數矩陣與結果向量,如下:

而之所有將未知數x,y,z扔掉,是因為它僅僅就是代表有幾個未知數而已,你叫a,b,c也可以,u,v,w也可以,而對於未知數的個數其實通過左邊系數矩陣的列數就可以反映出來,這也是跟之前學習矩陣x向量的限制條件是相吻合的,因為要求矩陣的列數必須跟向量中的元素是一致的。另外對於系數矩陣的行數其實就代表線性系統中有幾個方程,所以基於此,就可以從這個3x3的系數矩陣中看到該系統有3個方程,3個未知數對不?對於上面的這個三行四列的矩陣有一個專用的叫法:增廣矩陣,其實也很好理解,也就是在原來的系統矩陣中增加了一列結果,矩陣增加了一些東西所以就是增廣了。

既然現在將線性方程組化成了增廣矩陣的形式了,那么原來對線性方程組的消元操作也可以直接在矩陣中來完成了,下面以增廣矩陣的角度來看一下消元的過程,首先對x消元,其實也就是將第二的方程減去第一個方程的3倍,如下:

然后再來消第三組的x,用第三個方程減去第一個方程的2倍:

此時就可以把這倆個方程的y消除了,相加既可,如下:

此時未知數z就可以求出來了,左右兩邊都除以-15,第三行就變為:

 

而接下來照理就應該進行回代的過程,也就是將解出來的z回代到上面的方程進而求解x,y,但是!!!此時先暫停回代,先來總結一下,記得上面對於線性方程組求解過程總結了三點:

1、一個方程的左右兩邊同時乘以一個常數;

2、一個方程加(減)另一個方程;

3、交換兩個方程的位置 ;

而以增廣矩陣的視角來看待的話,對應的求解過程的話述相對應的就可以變為:

1、矩陣的某一行乘以一個常數;

2、矩陣的一行加(減)另一行;

3、交換矩陣的兩行;

再來對增廣矩陣消元的過程進行一個梳理,發現其實就是首先找到第一行的位置將其化成1,然后再把該位置所在列的其它元素化為0,如下:

然后再找第二行的第二個元素,將其化為1,再將它所在列下面的其它元素全化為0,如下:

而接下來再拿第三行的第三個元素進行同樣的消元,由於第三行下面已經沒有元素且本身為1,所以整個消元結束,其中又有一個新名詞要出現了:主元(pivot),其實也就是它:

而這樣一個消元的過程又有一個名詞來表示:高斯消元法(Gauss Elimination),而為了再鞏固整個高斯消元的過程,再看幾個例子,比如說:

 

而它對應的增廣矩陣為:

接下來開始高斯消元:

  • 先找到第一行的主元,也就是在第一行的第一個位置,也就是2,首先將它化為1,所以第一行的元素全部乘以1/2,如下:

    此時的主元那就是它了:

  • 將剩下行主元的位置全化為0,也就是主元下面的元素,所以用第二行減去第一行既可,如下:
  • 接下來再找第2行的主元,目前是1/2,需要先將它化為1,所以需要乘一下2,所以此時就變為:

    此時高斯消元法就已經完成了。

接下來再來另一個例子,這里會涉及到行的交換:

 

它所對應的增廣矩陣為:

開始高斯消元,先找第一行的主元,很顯然是0,但是!!要注意了,主元是不允許為0的,因為規則是要讓主元化為1嘛,0乘任何數都為0,而且要想將主元位置行的其它元素化為0是永遠達不到了,此時怎么辦呢?其實可以對行數進行交換,這里既可以讓第一行和第二行交換,也可以讓第一行跟第三行交換,那具體選擇哪行來交換呢?由於之后這塊要進行計算機的實現,而在計算機的實現當中通常選數值最大的那一行進行交換,也就是:


而之所以選擇這最大的行其實是盡量避免誤差,那為啥選大的誤差就能盡量避免這塊其實是數值分析領域所研究的內容,這里就記住選大的行交換就好了。接下來再進行正常的高斯消元既可,這里就略過了。

但是!!!高斯消元法有一個問題,我們只能通過矩陣讀出最后一個未知數的結果,之后還得回代再來得出所有其它未知數的結果,那。。有木有一種方法再繼續對矩陣進行變形從而直觀的通過矩陣就能知道每一個未知數的解是多少呢?答案是肯定的,往下看。

高斯-約旦消元法:

這里還是先來對咱們的高斯消元法進行一個流程梳理,對於這樣一個方程:

高斯消元的過程如下:

而在上面也說了高斯消元的一個問題就是只能看出z這個未知數的解,對於x,y還看不出,因為此時的方程式其實化成了它:

那還得繼續做矩陣變換才行,此時其實將它化為0,對於y的解就出來了:

而將其化為0也很簡單,將第三行乘10再跟第二行相加,就變化:

是不是此時就能清晰的通過矩陣的樣子看出y的解就為-2了,同樣的繼續,再將第三個主元列的第一行的4也化成0,其實就是反過來從第三行往第一行再進行一次高斯消元的過程,所以只需要讓第一行減去第三行的4倍既可,如下:

此時y的值就為-2了,其對應的線性方程就化簡成了:

此時倒數第一行的主元之上元素都已經化為0了,接下來則回到倒數第二行,將它上面的元素化為0了,所以同樣的套路,用第一行減去第二行的2倍,變化如下:

經過這樣反着的高斯消元之后,通過矩陣就可以直觀看出來所有未知數的解了,如下:

此時對於上面的整個過程又有一個專業名詞出現了:高斯-約旦消元法 Gauss-Jordan Elimination,總結一下它的過程分為兩個:前向過程(從上到下)和后向過程(從下到上),而具體的步驟如下:

前向過程(從上到下):

1、選擇最上的主元,將其化為1;

2、主元下面的所有行減去主元所在行的某個倍數,使得主元下面所有元素都為0;

后向過程(從下到上):

1、選擇最下的主元;

2、主元上面的所有行減去主元所在行的某個倍數,使得主元上面所有元素都為0;

不過目前咱們的這種高斯約旦消元的系數矩陣都是針對方陣而言的,那對於非方陣的系數矩陣這里暫時不考慮,待之后再來學習。

實現高斯-約旦消元法:

回到python的世界,來實現一下我們的高斯-約旦消元法。

新建類:

構造函數:

對於構造函數很顯然需要一個系統矩陣和一個結果向量,所以定義如下:

首先需要判斷一下矩陣的行數需要等於向量的列數,所以先斷言一下:

而由於后續的計算中需要不斷的使用矩陣的行數與列數,所以先賦值給變量,方便使用:

由於目標咱們只考慮方陣的矩陣,對於非方陣的形式還木有學習到,所以這里也斷言一下,待之后擴展之后再來將此斷言去掉:

接下來則將系數矩陣A和結果向量b變幻成一個增廣矩陣,具體如下:

接下來獲取了系統矩陣的i行向量之后,應該還需要往它后面追加一個結果向量的第i個元素在后面對吧?而由於Vector類設計的初衷是一個不可變的,那如何向它后面追加元素呢,其實可以在它里面增加一個append方法,不過這里簡單一點,再封裝一個獲取底層列表的方法,如下:

 

但是!!!此時是有問題的,因為它返回的引用,在外部的修改都會影響到Vector底層的_values這個集合,所以這里需要返回它的一個拷貝,其實思想跟java類似,那在python中是如何返回數據的拷貝對象呢?如下:

此時就可以調用向量的這個函數然后向它進行元素的追加了,如下:

接下來則遍歷i既可,如下:

 

gauss_jordan_elimination():

接下來關鍵就是來實現高斯約旦消元法了,總的來說它就是兩個過程,先來定義一下:

_forward():

對於前項的過程就是循環遍歷每行,然后每行遍歷時分別對每行的主元化為1,然后再將它所在列的其它行化為0,所以具體實現如下:

這里在找主元時需要考慮一個特殊的情況,就是有可能當前的A[i][i]剛好就是等於0,而根據上面理論的描述,此時需要找它下面最大的元素所在的行進行一下交換,而對於程序的實現就不判斷了,直接每次找主元時都遍歷找一下,這樣結果肯定是木問題的,所以此時先來找i行和n行之間最大的元素,如下:

接下來則就往下開始遍歷了,邏輯比較好理解,直接貼出來了:

接下來則可以進行兩行數據的交換了,而在python中對於矩陣兩行數據的交換相當簡單,如下:

找到了主元之后,接下來則需要將它歸為一,所以如下:

接下來則需要將主元下面的行的元素都化為0,如下:

其實也就是實現了高斯消元了,但是還不是高斯約旦消元,因為還差一個后向的過程。

_backward():

有了前向的實現,對於后向的就簡單多了,實現如下:

有了它,則是完整的一個高斯約旦消元的過程了。

fancy_print():

接下來打印一下增廣矩陣,以便看一下經過高斯約旦之后的增廣矩陣的樣子,也就是想打印成這種形式:

如下:

然后對於每一行的矩陣元素后面再加一個豎線和對應的結果元素,所以每行結尾不能以回車符,所以這里需要加句話:

然后再來打印結果元素:

測試:

接下來編寫測試代碼來驗證一下:

運行,發現報錯了。。

這是因為增廣矩陣的初始化有問題,如下:

 

再運行,還是報錯。。

那。。這是啥原因呢?debug一下:

為啥返回一個None呢?超級坑,再來往里debug,答案就出來了:

熟悉python的肯定就看到問題了,而對於小白的我,完全沒找出來,將它改為它就好了:

所以對於習慣java的人來說,在寫python這塊一定要注意了,這塊確實是比較容易出錯,再運行就o了:

這個跟之前理論描述的結果相吻合:

為了更加精准的測試咱們寫的程序是無bug的,所以這里再多弄幾組測試用例,可以筆算一下,這里就直接貼出來了:

from playLA.LinearSystem import LinearSystem
from playLA.Vector import Vector
from playLA.Matrix import Matrix

if __name__ == "__main__":
    A = Matrix([[1, 2, 4], [3, 7, 2], [2, 3, 3]])
    b = Vector([7, -11, 1])
    ls = LinearSystem(A, b)
    ls.gauss_jordan_elimination()
    ls.fancy_print()
    print()

    A2 = Matrix([[1, -3, 5], [2, -1, -3], [3, 1, 4]])
    b2 = Vector([-9, 19, -13])
    ls2 = LinearSystem(A2, b2)
    ls2.gauss_jordan_elimination()
    ls2.fancy_print()
    print()

    A3 = Matrix([[1, 2, -2], [2, -3, 1], [3, -1, 3]])
    b3 = Vector([6, -10, -16])
    ls3 = LinearSystem(A3, b3)
    ls3.gauss_jordan_elimination()
    ls3.fancy_print()
    print()

    A4 = Matrix([[3, 1, -2], [5, -3, 10], [7, 4, 16]])
    b4 = Vector([4, 32, 13])
    ls4 = LinearSystem(A4, b4)
    ls4.gauss_jordan_elimination()
    ls4.fancy_print()
    print()

    A5 = Matrix([[6, -3, 2], [5, 1, 12], [8, 5, 1]])
    b5 = Vector([31, 36, 11])
    ls5 = LinearSystem(A5, b5)
    ls5.gauss_jordan_elimination()
    ls5.fancy_print()
    print()

    A6 = Matrix([[1, 1, 1], [1, -1, -1], [2, 1, 5]])
    b6 = Vector([3, -1, 8])
    ls6 = LinearSystem(A6, b6)
    ls6.gauss_jordan_elimination()
    ls6.fancy_print()
    print()

運行結果:

/Users/xiongwei/opt/anaconda3/bin/python3.8 /Users/xiongwei/Documents/workspace/python/Play-with-Linear-Algebra/LinearAlgebra/main_linear_system.py
1.0 0.0 0.0 | -1.0
-0.0 1.0 0.0 | -2.0
-0.0 -0.0 1.0 | 3.0

1.0 0.0 0.0 | 1.9999999999999996
-0.0 1.0 0.0 | -3.0000000000000018
0.0 0.0 1.0 | -3.9999999999999996

1.0 0.0 0.0 | -1.9999999999999998
0.0 1.0 0.0 | 1.0
-0.0 -0.0 1.0 | -3.0

1.0 0.0 0.0 | 2.9999999999999996
-0.0 1.0 0.0 | -3.9999999999999996
0.0 0.0 1.0 | 0.4999999999999999

1.0 0.0 0.0 | 3.0
-0.0 1.0 0.0 | -3.0
-0.0 -0.0 1.0 | 2.0

1.0 0.0 0.0 | 1.0
0.0 1.0 0.0 | 1.0
-0.0 -0.0 1.0 | 1.0


Process finished with exit code 0

其中由於浮點運算會有精度問題,所以可能結果看到的跟預期有些偏差,但是其實是近似預期的。不過目前咱們的程序還有很多情況木有考慮,比如非方陣,有些方式組只有一個解,也有些是無解的等等,下面就來解決這樣的情況。

線性方程組解的結構:

在上面也拋出了對於咱們目前的實現的問題,接下來則來具體分析一下會出現其它情況的方程組。

方程組無解:

它對應的增廣矩陣為:

然后對第一行的主元進行消元就變為:

然后再對第二行的主元進行消元,由於此時主元是4,照理應該要進行歸一操作,不過這里簡化一下,直接讓第二行+第二行將第二行主元下面的歸0,如下:

此時,發現矛盾點了:

無論如何這個式子也不可能成立對吧?這也就意味着無法找到一個x,y,z滿足這個式子了,也就是無解!!!

方程組有無數解:

接下來再來看一個方程組:

化為增廣矩陣:

然后處理第一行的主元,將其下面的化為0,變為:

接下來處理第二行的處元,先將它歸一:

接下來則以第二個主元將其下面的元素歸為0,直接第二行+第三行既可:

目前高斯消元過程已經結束了,發現此時的最后一行出現了全0行,很顯然也是一個有效的等式,因為0*x+0*y+0*z = 0永遠恆等,接下來則可以繼續往下進行消元,由於最后一行沒有主元了,所以回到倒數第二行,將這個主元上面的元素變為0,也就是用第一行減去2倍的第二行,變化如下:

而此時方程式就變為了:

此時再變換一下:

是不是z可以取任意值,都能得到一組x,y,z滿足方程組,方程組有無數組解!

三種方程式總結:

接下來總結一下對於方程組的三種解的情況:

對於上面三種情況其實用肉眼已經能分清楚了,但是!!!用一個更嚴謹的方式來看增廣矩陣來得到方程組解的結構是有唯一解,無解、還是有無數解之前,需要在往下學習一個概念。

行最簡形式:

其實對於線性方程組的增廣矩陣進行高斯消元的過程其實是將增廣矩陣變幻成了階梯型矩陣,如果嚴格的來說啥是階梯型矩陣要敘述的條件還是挺麻煩的,但是呢,可以用一種輔助方式來助於理解,比如對於這個有唯一解的增廣矩陣,可以這么做個輔助線:

從這個輔助線來看就成了一個階梯型對不?再仔細觀察,其實這個階梯型的線將矩陣分為兩部分,階梯的下面所有的元素都為0:

所以對於其它兩種解的增廣矩陣也可以做輔助線如下:

而對於這些矩陣都滿足:非零行的第一個元素(主元)為1、主元所在列在其它元素均為0,不過這里又有一個新名詞要出現了,也就是經過了高斯約旦消元之后所化成的矩陣叫做行最簡形式【reduced row echelon form (RREF)】。為了進一步對這個行最簡形式有更深刻的認識,下面再來看其它的一些增廣矩陣,因為目前所示的增廣矩陣都太標准的【都是三個未知數三個方程】,比如看這么一個:

 

然后畫階梯輔助線:

其中對於第一行的主元沒有在第一個位置也沒關系,依然是滿足行最簡形式。下面再來看一下:

畫階梯輔助線:

也是滿足行最簡形式,因為首元值都是1,然后它所在列的的其它元素全為0,下面再來看幾個不滿足行最簡形式的增廣矩陣:

其中主元列中有不為0的:

接下來再來看一個:

其中標紅的主元不滿足行最簡形式,因為隨着行號的上升第三行的主元位置比第二行的主元位置還靠左了,最后再來看一個:

這個為啥不滿足行最簡形式呢?因為第一行是全0行,對於階梯型矩陣而言全0行必須是在最后一行。

知道了行最簡形式這個概念之后,對於方程有解無解就可以進一步嚴謹化了,也就是當通過高斯約旦消元之后將增廣矩陣化為行最簡單形式之后,其實三種形式可以進一步抽象為:

進一步再形象一點可以總結為:

這里要特別注意:一定是要看非零行的個數和未知數的個數,而不能看非零行的個數和整個增廣矩陣行的個數,為什么?下面舉兩個反面例子就知道了,比如說:

它的非零行有3行,而正好跟增廣矩陣的3行是一樣的,那是不是就應該有唯一解呢?其實它是無解的,因為非零行<未知數個數【目前有四列,也就是有四個未知數】,滿足它:

同樣的,再來看一個例子:

化為最簡形式之后,發現非零行的個數為3,而整個增廣矩陣的行數是4,也就是非零行<增廣矩陣的行數,是不是無解呢?其實是有無數個解的,這是因為正確的視角得這樣看:非零行=未知數的個數【系數矩陣有3列,也就存在3個未知數】,滿足它:

而上面這倆反面例子其實已經打破了nxn的系數矩陣了,而是nxm的系數矩陣,也就是方程個數和未知數的個數是不一樣的,但是!!依然可以將它化成最簡形式來看整個方程式解的結構【有唯一解、無解、無數解】。

直觀理解線性方程組解的結構:

目前我們已經知道對於一個方程組的解的類型是通過將方程組化為最簡形式之后,判斷系統矩陣的非零行和未知數的個數進行一個比較,如果相等則有唯一解,而如果小於則是無數解,否則無解。其實比較的就是方程的數量和未知數的數量的關系,那為什么它們之間的數量不匹配會使得方程組的解的結構產生變化呢?下面以一個直觀的視角來進一步理解線性方程組解的結構。

在初高中學習方程組的時候,一定都聽過這樣的一個概念:“如果一個方程組有n個未知數的話,一定得要有n個方程才有可能有唯一解”,這其實也很好理解,因為每一個方程就代表一個約束條件,如果有n個未知數的話需要有n個約束條件才能把這n個未知數給固定住,

二個未知數:

比如有這么一個方程:

在二維坐標系中表示的話其實就是一條直線,如下:

而滿足這個式子的點n多,不足以求出一個唯一解,但是!!!如果再加一個方程使之成為一個方程組:

 

此時在二維坐標系中就可以看到有一個相交點了:

此時這個方程就有一個唯一解了,但是!!!在上面的描述中有個詞高亮了,就是“可能”,也就是說並非有n個方程n個未知數就一定有唯一解,看下面這個方程組其實就無唯一解了,如下:

它的坐標上的直線是這樣的:

三個未知數:

接下來看一下有三個未知數的方程,比如:

對於三維空間中滿足上式子的所有點集合起來其實就是一個平面,如下:

 

那如果有兩個三元方程組成一個方程組,如果兩個平面相加的話可能是這樣:

可以看到相交的部分組成了一條直線,而直線上有N多個點,也就是有無數解,所以當方程組個數小於未知數個數時,一定是沒有唯一解,所以這就跟上面說的這種情況吻合了:

 

但是!!!也有可能無解,因為有可能兩個平面不相交呀,比如像這樣:

另外還有一個情況就是有三個三元方程組成的方程組,此時有可能是有唯一解,像這樣:

 

其中三個平面相交的點就是整個三元方程的唯一解。也有可能有無數解,像這樣:

其中相交的是一條直線,有無數個點當然也就是無數個解嘍。也有可能是沒有解,像這樣:

 

三個平面完全不相交,還有可能像這樣:

 

相加的位置是兩條平行線,無法找到一個點同時在三個平面上。還有可能像這樣:

四個未知數:

如果再升級,有四個未知數呢?就得要到四維空間上來想象了,這里看一下動圖形象感受一下:

其中相交的是一個三角形,很顯然是無解的,但是如果再將匯集成一個相交點其實也有一個唯一解的,這里也就是說明“對於n個未知數n個方程,才可能有唯一解”,其中唯一解是非常湊巧的情況,大概率也是沒有解的。

三個二元方程:

這里再回到比較直觀的二維視角來,比如有三個方程,但是只有二個未知數,在坐標點是兩條連線,可能是這樣:

 

此時對於第三個二元方程也是對應一個新的直線,可能是這樣的:

 

其中這種情況肯定是無解的對吧,但是如果這樣呢?

此時又有唯一解了,那有沒有無數解的情況出現了,有:

所以此時針對這種情況又有個結論:“當方程個數多於未知數個數時,可能有唯一解,也有可能無解,也有可能有無數解”。

三種情況總結:

上面舉例了這么多種情況,其實簡單總結就是如下這張表:

有9種情況,但是此表以再進一步簡化的,怎么簡化呢?這里得回到行最簡形式角度來看了,先看個結論:“對於行最簡形式,系數矩陣的非零行不可能大於未知數個數”,比如:

所以,上面9點情況可以改變為:

 

而對於無解的情況就是看看當化為最簡形式之后有木有一行產生矛盾,所以先把無解的情況也去掉,只看木有矛盾的情況,進一步簡化表格為:

 

 而當行最簡形式非零行=未知數時:

 

很明顯只有可能是唯一解,因為只有方陣才可能滿足非零行=未知數的個數,也就是說:“對於行最簡形式,系數矩陣的非零行等於未知數個數時,一定有唯一解”,所以表格又可化簡為:

 

而由於此時將有無解的情況去掉了,這里再將無解的也考慮進來,其所有的情況就為:

所以,經過這么一分析,對於一個方程組的解的結構的計算就變得比較簡單了,首先判斷是否無解,如果不是,則再看有解的情況是無數解還是唯一解。

更一般化的高斯-約旦消元法:

在上面學習線性系統求解時舉的都是當通過高斯約旦消元法化為行最簡形式后都是n個未知數n個方程增廣矩陣進行求解的,那如果是方程的個數和未知數的個數不匹配時在具體使用高斯約束消元法時會出現什么樣的問題,又該如何處理呢?

這里先來回憶一下高斯消元的過程,分為兩步驟:

而此次咱們探討的是方程的個數跟未知數的個數不匹配的情況,依然要嚴格遵守上面這個過程才行,下面看個具體例子:

 

它對應的增廣矩陣為:

首先找到第一個主元:

已經是1了就不需要將其歸一了,然后接下來則就是將它下面的元素歸0,很簡單,只需要讓第二行+第一行、第三行-第一行、第四行+第一行*2既可,變為:

此時發現居然它下面的第二行也都為0了,所以找第二行主元時發現第二列元素為0之后則往后列再找非0的既可,此時找到的是2將其當作主元:

由於主元不是1,先將其歸1,直接當第二行/2變化為:

然后再將它下面的元素歸為0,這里具體就不算了,直接給出結果:

 

此時發現第四列下面也都為0了,同樣的第三的主元則又找非0元素,此時就為3了:

此時主元不為1,先歸一,然后再將下面的歸0,整體為:

以上就是高斯消元的過程,接下來則需要后向過程了,將主元之上的元素也都歸0,整體為:

此時對於方程組就已經化為行最簡形式了,如下:

 很明顯該方程是有解的,因為行最簡形式的系數矩陣的非零行是3行,小於未知數,當然有無數解嘍。

而行最簡形式對應的方程組的樣子就為:

它又可以變化為:

其中x,z,u其實就是表示主元,對於有主元的列可以稱之為:

而對於沒有主元的列可以稱之為:

有了這個定義之后,是不是發現:

x,z,u表示主元,它是等號右邊是由結果元素和自由列進行操作運算,上面這式子再變換一下可能更加的清晰,如下:

 

而其中y、w可以有無數個值,所以整個線性系統是有無數個解的。而舉這個例子跟之前不同的主元並非是在n行n列了,而且通過這個視角對於一個線性系統也很容易寫出它的解的式子了。

下面再來看一個特殊的線性方程系統,方程個數>方程未知個數:

它對應的增廣矩陣為:

然后先進行前向的高斯消元過程,過程直接略過,因為已經對整個消元的過程非常熟悉了,結果如下:

其中出現了矛盾的第三行,一看就是無解了,也就沒必要進行后向過程了。

實現更一般化的高斯-約旦消元法:

接下來回到python的世界,來實現高斯約旦消元的過程:

去掉nxn方程的斷言:

由於之前咱們只考慮了nxn的系統矩陣:

而這里肯定是沒有這個限制了,因為對於nxm的矩陣依然是可以進行求解的,所以先去掉:

增加主元列的存儲變量:

由於對於更一般的線性系統而言,主元列並非是n行n列,而對於這種系統的求解對於主元列在哪是非常重要的,所以定義一下變量專門存儲主元列的信息,如下:

其中將其也定義pulic的,因為之后可能在外面會直接用到它。

_forward()邏輯修改:

對於原來前向過程的實現回憶一下:

由於此時已經不是nxn的方陣了,所以對於前向的過程邏輯也得進行重新修改了:

此時需要判斷Ab[i][k]是否是主元,因為如今主元的位置並非是n行n列了:

此時就需要修改_max_row()尋找主元位置的邏輯了:
 

而它里面的實現邏輯基本不用變,就替換下新的參數既可,如下:

回到主流程繼續往下改:

其中對於判斷浮點式是否等於0在這塊封裝了兩個方法供以后的調用方便:

這樣對於原來Vector中判斷浮點式是否為0就可以使用該方法了,如下:

最后記得要記錄一下主元列到變量當中,如下:

_backward()邏輯修改:

有了前向過程的修改思路,對於后項過程的修改就比較簡單了,先來回憶一下目前的實現:

 

也是由於不是nxn的系數矩陣了,所以修改如下:

 

測試:

先來看一下原來咱們的測試用例好不好使:

from playLA.LinearSystem import LinearSystem
from playLA.Vector import Vector
from playLA.Matrix import Matrix

if __name__ == "__main__":
    A = Matrix([[1, 2, 4], [3, 7, 2], [2, 3, 3]])
    b = Vector([7, -11, 1])
    ls = LinearSystem(A, b)
    ls.gauss_jordan_elimination()
    ls.fancy_print()
    print()

    A2 = Matrix([[1, -3, 5], [2, -1, -3], [3, 1, 4]])
    b2 = Vector([-9, 19, -13])
    ls2 = LinearSystem(A2, b2)
    ls2.gauss_jordan_elimination()
    ls2.fancy_print()
    print()

    A3 = Matrix([[1, 2, -2], [2, -3, 1], [3, -1, 3]])
    b3 = Vector([6, -10, -16])
    ls3 = LinearSystem(A3, b3)
    ls3.gauss_jordan_elimination()
    ls3.fancy_print()
    print()

    A4 = Matrix([[3, 1, -2], [5, -3, 10], [7, 4, 16]])
    b4 = Vector([4, 32, 13])
    ls4 = LinearSystem(A4, b4)
    ls4.gauss_jordan_elimination()
    ls4.fancy_print()
    print()

    A5 = Matrix([[6, -3, 2], [5, 1, 12], [8, 5, 1]])
    b5 = Vector([31, 36, 11])
    ls5 = LinearSystem(A5, b5)
    ls5.gauss_jordan_elimination()
    ls5.fancy_print()
    print()

    A6 = Matrix([[1, 1, 1], [1, -1, -1], [2, 1, 5]])
    b6 = Vector([3, -1, 8])
    ls6 = LinearSystem(A6, b6)
    ls6.gauss_jordan_elimination()
    ls6.fancy_print()
    print()

運行結果:

/Users/xiongwei/opt/anaconda3/bin/python3.8 /Users/xiongwei/Documents/workspace/python/Play-with-Linear-Algebra/LinearAlgebra/main_linear_system.py
1.0 0.0 0.0 | -1.0
-0.0 1.0 0.0 | -2.0
-0.0 -0.0 1.0 | 3.0

1.0 0.0 0.0 | 1.9999999999999996
-0.0 1.0 0.0 | -3.0000000000000018
0.0 0.0 1.0 | -3.9999999999999996

1.0 0.0 0.0 | -1.9999999999999998
0.0 1.0 0.0 | 1.0
-0.0 -0.0 1.0 | -3.0

1.0 0.0 0.0 | 2.9999999999999996
-0.0 1.0 0.0 | -3.9999999999999996
0.0 0.0 1.0 | 0.4999999999999999

1.0 0.0 0.0 | 3.0
-0.0 1.0 0.0 | -3.0
-0.0 -0.0 1.0 | 2.0

1.0 0.0 0.0 | 1.0
0.0 1.0 0.0 | 1.0
-0.0 -0.0 1.0 | 1.0


Process finished with exit code 0

接下來再來新添加一些特殊的測試用例:

    A7 = Matrix([[1, -1, 2, 0, 3],
                 [-1, 1, 0, 2, -5],
                 [1, -1, 4, 2, 4],
                 [-2, 2, -5, -1, -3]])
    b7 = Vector([1, 5, 13, -1])
    ls7 = LinearSystem(A7, b7)
    ls7.gauss_jordan_elimination()
    ls7.fancy_print()
    print()

運行:

其實也就是上面理論描述的這個結果:

下面再來增加一個測試用例,如之前所述的:

如下:

    A8 = Matrix([[2, 2],
                 [2, 1],
                 [1, 2]])
    b8 = Vector([3, 2.5, 7])
    ls8 = LinearSystem(A8, b8)
    ls8.gauss_jordan_elimination()
    ls8.fancy_print()
    print()

運行結果:

不過為了看出有解無解,這里對於高斯約旦消元方法增加一個返回值,如下:

下面修改下測試用例:

    b8 = Vector([3, 2.5, 7])
    ls8 = LinearSystem(A8, b8)
    if not ls8.gauss_jordan_elimination():
        print("No Solution!")
    ls8.fancy_print()
    print()

    A9 = Matrix([[2, 0, 1],
                 [-1, -1, -2],
                 [-3, 0, 1]])
    b9 = Vector([1, 0, 0])
    ls9 = LinearSystem(A9, b9)
    if not ls9.gauss_jordan_elimination():
        print("No Solution!")
    ls9.fancy_print()
    print()

運行:

其中最后一種情況知道為啥木有解了不,因為很顯然非0行其實是2行,而未知數是3個,另外最后一行的結果是一個非0。

齊次線性方程組:

在最后再來學習一個概念,如標題所示:齊次線性方程組,那它是個什么東東呢?其實下面這種方程組就是:

也就是方程組的每一個結果都為0,而求解過程跟普通方程的求解是一樣的,如下:

但是!!它有一個特點,對於這樣的方式是肯定有解的,因為至少有一個解是讓所有未知數取0對不?而也有可能它是一個解還是無數解,依然還是根據之前的判斷規則來,如上方程很明顯有無數解,因為非0行是2,而未知數是3。

既然它最后一列肯定永遠為零,那么咱們就不需要使用增廣矩陣了,在求解的過程中直接對系數矩陣進行操作既可,如下:

其中它也是滿足行最簡形式的對不?同樣也可以用之前所學的方式來判斷方程組的解的形式。所以總的而言先了解齊次與非齊次的區別:

那了解這個概念有啥用呢?因為在線性代數中對於齊次線性方程組它特殊的性質會在未來的學習中起到一定的作用,比如之后的空間的學習。


免責聲明!

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



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