线性代数Python计算:线性方程组的通解

对齐次线性方程组
{ a 11 x 1 + a 12 x 2 + ⋯ + a 1 n x n = 0 a 21 x 1 + a 22 x 2 + ⋯ + a 2 n x n = 0 ⋯ ⋯ ⋯ a m 1 x 1 + a m 2 x 2 + ⋯ + a m n x n = 0 (1) \begin{cases}a_{11}x_1+a_{12}x_2+\cdots+a_{1n}x_n=0\\a_{21}x_1+a_{22}x_2+\cdots+a_{2n}x_n=0\\\quad\quad\quad\cdots\quad\cdots\quad\cdots\quad\\a_{m1}x_1+a_{m2}x_2+\cdots+a_{mn}x_n=0\end{cases}\tag{1} a11x1+a12x2++a1nxn=0a21x1+a22x2++a2nxn=0am1x1+am2x2++amnxn=0(1)
至少有零解。若系数矩阵 A \boldsymbol{A} A的秩(即博文《消元法与矩阵初等变换》中定义的rowLadder函数算得的rank)rank A < n \boldsymbol{A}A<n时,方程组有非零解 s 1 , s 2 , ⋯   , s n − r \boldsymbol{s}_1,\boldsymbol{s}_2,\cdots,\boldsymbol{s}_{n-r} s1,s2,,snr构成的基础解系,使得方程组的通解可表为
x = c 1 s 1 + c 2 s 2 + ⋯ + c n − r s n − r . \boldsymbol{x}=c_1\boldsymbol{s}_1+c_2\boldsymbol{s}_2+\cdots+c_{n-r}\boldsymbol{s}_{n-r}. x=c1s1+c2s2++cnrsnr.
其中 c 1 , c 2 , ⋯   , c n − r c_1,c_2,\cdots,c_{n-r} c1,c2,,cnr为任意常数。也可以更一般地表示成 x = o + c 1 s 1 + c 2 s 2 + ⋯ + c n − r s n − r \boldsymbol{x}=\boldsymbol{o}+c_1\boldsymbol{s}_1+c_2\boldsymbol{s}_2+\cdots+c_{n-r}\boldsymbol{s}_{n-r} x=o+c1s1+c2s2++cnrsnr,其中 o \boldsymbol{o} o表示零解。对非齐次线性方程组
{ a 11 x 1 + a 12 x 2 + ⋯ + a 1 n x n = b 1 a 21 x 1 + a 22 x 2 + ⋯ + a 2 n x n = b 2 ⋯ ⋯ ⋯ a m 1 x 1 + a m 2 x 2 + ⋯ + a m n x n = b n (2) \begin{cases}a_{11}x_1+a_{12}x_2+\cdots+a_{1n}x_n=b_1\\a_{21}x_1+a_{22}x_2+\cdots+a_{2n}x_n=b_2\\\quad\quad\quad\cdots\quad\cdots\quad\cdots\quad\\a_{m1}x_1+a_{m2}x_2+\cdots+a_{mn}x_n=b_n\end{cases}\tag{2} a11x1+a12x2++a1nxn=b1a21x1+a22x2++a2nxn=b2am1x1+am2x2++amnxn=bn(2)
设其增广矩阵 B = ( A , b ) \boldsymbol{B}=(\boldsymbol{A},\boldsymbol{b}) B=(A,b),若rank A \boldsymbol{A} A ( A , b ) (\boldsymbol{A},\boldsymbol{b}) (A,b)
则方程组(2)无解。若rank A = r = \boldsymbol{A}=r= A=r=rank ( A , b ) (\boldsymbol{A},\boldsymbol{b}) (A,b),则方程组(2)必有解。设有一特解为 x 0 \boldsymbol{x}_0 x0(即 A x 0 = b \boldsymbol{Ax}_0=\boldsymbol{b} Ax0=b)。方程组(2)的导出组(1)的基础解系 s 1 , s 2 , ⋯   , s n − r \boldsymbol{s}_1,\boldsymbol{s}_2,\cdots,\boldsymbol{s}_{n-r} s1,s2,,snr,则方程组的通解为
x = x 0 + c 1 s 1 + c 2 s 2 + ⋯ + c n − r s n − r (3) \boldsymbol{x}=\boldsymbol{x}_0+c_1\boldsymbol{s}_1+c_2\boldsymbol{s}_2+\cdots+c_{n-r}\boldsymbol{s}_{n-r}\tag{3} x=x0+c1s1+c2s2++cnrsnr(3)
如果将 o \boldsymbol{o} o视为齐次线性方程组(1)的特解,则方程组(1)和(2)在有解的情况下,通解具有相同的表示形式(3)。
利用博文《消元法与矩阵初等变换》中定义的消元过程函数rowLadder和回代过程函数simplestLadder,可定义如下的线性方程组的通解函数:

import numpy as np                                  #导入numpy
def mySolve(A,b):
    m,n=A.shape                                     #系数矩阵结构
    b=b.reshape(b.size, 1)                          #常量列向量
    B=np.hstack((A, b))                             #构造增广矩阵
    r, order=rowLadder(B, m, n)                     #消元
    X=np.array([])                                  #解集初始化为空
    index=np.where(abs(B[:,n])>1e-10)               #常数列非零元下标
    nonhomo=index[0].size>0                         #判断是否非齐次
    r1=r                                            #初始化增广矩阵秩
    if nonhomo:                                     #非齐次
        r1=np.max(index)+1                          #修改增广阵秩
    solvable=(r>=r1)                                #判断是否可解
    if solvable:                                    #若可解
        simplestLadder(B, r)                        #回代
        X=np.vstack((B[:r,n].reshape(r,1),          #特解
                            np.zeros((n-r,1))))
        if r<n:                                     #导出组基础解系
            x1=np.vstack((-B[:r,r:n],np.eye(n-r)))
            X=np.hstack((X,x1))
        X=X[order]
    return X

程序的第2~22行定义函数mySolve,参数A和b分别表示象形方程组(2)的系数矩阵 A \boldsymbol{A} A和常数矩阵 b \boldsymbol{b} b
第3行读取2维数组A的shape属性表示的行数m(方程个数)和列数n(未知量个数)。第4行调用b的reshape方法将其设置为 n × 1 n\times1 n×1的列矩阵。第5行调用numpy(第1行导入)的hstack函数将A和b横向连接成增广矩阵 ( A , b ) (\boldsymbol{A},\boldsymbol{b}) (A,b)为数组B。
第6行调用博文《消元法与矩阵初等变换》中定义的函数rowLadder函数将B变换成与之等价的行阶梯阵并返回系数矩阵的秩r和未知量顺序order。第7行将解集X初始化为空数组。第8行调用numpy的where函数,计算变换后常数矩阵中非零元素的下标集合index。第9行根据index是否为空设置标志nonhomo:非空,即常数矩阵b有非零元素(b.size>0),为True;否则,即b的所有元素均为0,为False。第10行将增广矩阵的秩r1初始化为系数矩阵的秩r。第11~12行对非齐次方程组更新增广矩阵的秩r1:变换后b中最后非零元素的下标,因为Python中数组下标时从0开始编排,故还需加1。
第13行根据r与r2的大小比较算得线性方程组是否有解的标志solvable。第14~21行的if语句对有解方程组求解。具体而言,第15行调用博文《消元法与矩阵初等变换》中定义的simplestLadder函数将行阶梯矩阵B准换成最简行阶梯矩阵。第16~17截取行阶梯阵B中前r行最后一列(对应常数矩阵b)B[:r,n].reshape(n-r,1)与n-r个0构成的列矩阵(由numpy的zeros((n-r,1)))通过numpy的vstack函数纵向连接为特解。第18~20行的if语句构造解集X:第19行算得齐次方程组或非齐次方程组的导出组的基础解系x1:截取矩阵B的前r行后n-r列B[:r,r:n],取反后与numpy的eye函数生成的n-r阶单位阵纵向连接(调用numpy的vstack)成一个 n × ( n − r ) n\times(n-r) n×(nr)矩阵。第20行调用hstack函数将x1连接到X的右方。21行用order调整解集X中各行顺序(使之符合未知量顺序)。
第22行将解集X返回,有3种情况:若方程组无解,返回的是空列表;方程组有唯一解,返回的是一个列矩阵;方程组有无穷多个解,齐次方程组的零解位于X首列,基础解系位于X的后n-r列;非齐次方程组的特解位于X的首列,后n-r列表示导出组的基础解系。
例1 利用上述程序定义的mySolve函数解齐次线性方程组 { 3 x 1 + 4 x 2 − 5 x 3 + 7 x 4 = 0 2 x 1 − 3 x 2 + 3 x 3 − 2 x 4 = 0 4 x 1 + 11 x 2 − 13 x 3 + 16 x 4 = 0 7 x 1 − 2 x 2 + x 3 + 3 x 4 = 0 . \begin{cases}3x_1+4x_2-5x_3+7x_4=0\\2x_1-3x_2+3x_3-2x_4=0\\4x_1+11x_2-13x_3+16x_4=0\\7x_1-2x_2+x_3+3x_4=0\end{cases}. 3x1+4x25x3+7x4=02x13x2+3x32x4=04x1+11x213x3+16x4=07x12x2+x3+3x4=0.

import numpy as np                                  #导入numpy
from fractions import Fraction as F                 #导入Fraction
np.set_printoptions(formatter=                      #设置输出数据格式
                    {'all':lambda x:str(F(x).limit_denominator())})
A=np.array([[3,4,-5,7],                             #系数矩阵
            [2,-3,3,-2],
            [4,11,-13,16],
            [7,-2,1,3]],dtype='float')
b=np.array([0,0,0,0])                               #常数矩阵
X=mySolve(A,b)                                      #解方程
print(X)

程序的第5~9行按题设设置系数矩阵和常数矩阵。第10行调用mySolve函数解方程组,返回值赋予X。运行程序,输出

[[0  3/17 -13/17]
 [0 19/17 -20/17]
 [0   1      0  ]
 [0   0      1  ]]

三列数据分别对应零解 ( 0 0 0 0 ) \begin{pmatrix}0\\0\\0\\0\end{pmatrix} 0000和基础解系 ( 3 17 19 17 1 0 ) , ( − 13 17 − 20 17 0 1 ) \begin{pmatrix}\frac{3}{17}\\\frac{19}{17}\\1\\0\end{pmatrix}\text{,}\begin{pmatrix}-\frac{13}{17}\\-\frac{20}{17}\\0\\1\end{pmatrix} 1731719101713172001
例2 用Python解线性方程组
{ x 1 − x 2 − x 3 = 2 2 x 1 − x 2 − 3 x 3 = 1 3 x 1 + 2 x 2 − 5 x 3 = 0 . \begin{cases}x_1-x_2-x_3=2\\ 2x_1-x_2-3x_3=1\\ 3x_1+2x_2-5x_3=0 \end{cases}. x1x2x3=22x1x23x3=13x1+2x25x3=0.

import numpy as np                                  #导入numpy
from fractions import Fraction as F                 #导入Fraction
np.set_printoptions(formatter=                      #设置输出数据格式
                    {'all':lambda x:str(F(x).limit_denominator())})
A=np.array([[1,-1,-1],                              #系数矩阵
            [2,-1,-3],
            [3,2,-5]],dtype='float')
b=np.array([2,1,0])                                 #常数矩阵
X=mySolve(A,b)                                      #解方程
print(X)

运行程序,输出

[[5]
 [0]
 [3]]

表示唯一解 ( x 1 x 2 x 3 ) = ( 5 0 3 ) \begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}5\\0\\3\end{pmatrix} x1x2x3=503
例3 用Python解线性方程组 { 4 x 1 + 2 x 2 − x 3 = 2 3 x 1 − x 2 + 2 x 3 = 10 11 x 1 + 3 x 2 = 8 \begin{cases}4x_1+2x_2-x_3=2\\3x_1-x_2+2x_3=10\\11x_1+3x_2\quad\quad=8\end{cases} 4x1+2x2x3=23x1x2+2x3=1011x1+3x2=8

import numpy as np                                  #导入numpy
A=np.array([[4,2,-1],                               #系数矩阵
            [3,-1,2],
            [11,3,0]],dtype='float')
b=np.array([2,10,8])                                #常数矩阵
X=mySolve(A,b)                                      #解方程
print(X)

运行程序,输出

[]

表示无解。
例4 用Python解线性方程组 { 2 x 1 + x 2 − x 3 + x 4 = 1 4 x 1 + 2 x 2 − 2 x 3 + x 4 = 2 2 x 1 + x 2 − x 3 − x 4 = 1 \begin{cases} 2x_1+x_2-x_3+x_4=1\\ 4x_1+2x_2-2x_3+x_4=2\\ 2x_1+x_2-x_3-x_4=1 \end{cases} 2x1+x2x3+x4=14x1+2x22x3+x4=22x1+x2x3x4=1

import numpy as np                                  #导入numpy
from utility import mySolve                         #导入mySolve
from fractions import Fraction as F                 #导入Fraction
np.set_printoptions(formatter=                      #设置输出数据格式
                    {'all':lambda x:str(F(x).limit_denominator())})
A=np.array([[2,1,-1,1],                             #系数矩阵
            [4,2,-2,1],
            [2,1,-1,-1]],dtype='float')
b=np.array([1,2,1])                                 #常数矩阵
X=mySolve(A,b)                                      #解方程组
print(X)

运行程序,输出

[[1/2 1/2 -1/2]
 [ 0   0    1]
 [ 0   1    0]
 [ 0   0    0]]

对应特解 x 0 = ( x 1 x 2 x 3 x 4 ) = ( 1 2 0 0 0 ) \boldsymbol{x}_0=\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}\frac{1}{2}\\0\\0\\0\end{pmatrix} x0=x1x2x3x4=21000和导出组的基础解系 s 1 = ( x 1 x 2 x 3 x 4 ) = ( 1 2 0 1 0 ) \boldsymbol{s}_1=\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}\frac{1}{2}\\0\\1\\0\end{pmatrix} s1=x1x2x3x4=21010 s 2 = ( x 1 x 2 x 3 x 4 ) = ( − 1 2 1 0 0 ) \boldsymbol{s}_2=\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}-\frac{1}{2}\\1\\0\\0\end{pmatrix} s2=x1x2x3x4=21100
由例1、例2、例3和例4可见,此处定义的mySolve函数适用于解实数域ℝ上的各种情形的线性方程组。而numpy提供的solve函数只适用于可逆系数矩阵的特殊情形(见博文《解可逆系数矩阵线性方程组》)。
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

你可能感兴趣的:(线性代数,线性代数,python,开发语言)