对齐次线性方程组
{ 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=0⋯⋯⋯am1x1+am2x2+⋯+amnxn=0(1)
至少有零解。若系数矩阵 A \boldsymbol{A} A的秩(即博文《消元法与矩阵初等变换》中定义的rowLadder函数算得的rank)rank A < n \boldsymbol{A}
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+⋯+cn−rsn−r.
其中 c 1 , c 2 , ⋯ , c n − r c_1,c_2,\cdots,c_{n-r} c1,c2,⋯,cn−r为任意常数。也可以更一般地表示成 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+⋯+cn−rsn−r,其中 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=b2⋯⋯⋯am1x1+am2x2+⋯+amnxn=bn(2)
设其增广矩阵 B = ( A , b ) \boldsymbol{B}=(\boldsymbol{A},\boldsymbol{b}) B=(A,b),若rank A \boldsymbol{A} A
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+⋯+cn−rsn−r(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×(n−r)矩阵。第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+4x2−5x3+7x4=02x1−3x2+3x3−2x4=04x1+11x2−13x3+16x4=07x1−2x2+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} ⎝⎜⎜⎛173171910⎠⎟⎟⎞,⎝⎜⎜⎛−1713−172001⎠⎟⎟⎞。
例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}. ⎩⎪⎨⎪⎧x1−x2−x3=22x1−x2−3x3=13x1+2x2−5x3=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+2x2−x3=23x1−x2+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+x2−x3+x4=14x1+2x2−2x3+x4=22x1+x2−x3−x4=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函数只适用于可逆系数矩阵的特殊情形(见博文《解可逆系数矩阵线性方程组》)。
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!