上一节我们提到了矩阵和线性方程组的关系,那么这一节主要探讨如何用矩阵去解线性方程组。
首先介绍阶梯型矩阵:
一个矩阵称为阶梯型矩阵,需要以下三个条件:
1.每个非零行都在每一零行上。
2.某一行的先导元素所在列下放元素都是零。
3.某一先导元素所在列下放元素都是零。
(和看天书差不多)
我们用图形来表示它。一个阶梯型矩阵形如下面的形式:
[ # ∗ ∗ ⋯ ∗ 0 # ∗ ⋯ ∗ 0 0 # ⋯ ∗ ⋮ ⋮ ⋮ ⋱ ⋮ 0 0 0 ⋯ # ] \begin{bmatrix} \# & * & * & \cdots & * \\ 0 & \# & * & \cdots & * \\ 0 & 0 & \# & \cdots & * \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \cdots & \# \\ \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎡#00⋮0∗#0⋮0∗∗#⋮0⋯⋯⋯⋱⋯∗∗∗⋮#⎦⎥⎥⎥⎥⎥⎤
其中,#代表先导元素,* 为任意元素。先导元素可以往后推,因而下面这几个矩阵均为阶梯型矩阵:
[ 1 5 3 2 0 0 1 5 0 0 0 0 ] , [ 1 0 3 2 0 2 0 5 0 0 0 4 0 0 0 0 ] , [ 1 0 3 2 2 0 7 0 0 2 5 3 0 2 0 0 0 0 0 5 0 0 0 0 0 0 0 1 ] \begin{bmatrix} 1 & 5 & 3 & 2\\ 0 & 0 & 1 & 5\\ 0 & 0 & 0 & 0 \end{bmatrix}, \begin{bmatrix} 1 & 0 & 3 & 2\\ 0 & 2 & 0 & 5\\ 0 & 0 & 0 & 4\\ 0 & 0 & 0 & 0 \end{bmatrix}, \begin{bmatrix} 1 & 0 & 3 & 2 & 2 & 0 &7\\ 0 & 0 & 2 & 5 & 3 & 0 &2\\ 0 & 0 & 0 & 0 & 0 & 5 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 &1 \end{bmatrix} ⎣⎡100500310250⎦⎤,⎣⎢⎢⎡1000020030002540⎦⎥⎥⎤,⎣⎢⎢⎡1000000032002500230000507201⎦⎥⎥⎤
观察上面几个矩阵,其实大概就能得到人话版规律:
1.下半部分全部是0
2.第一个是非零元素的位置每行依次右移,至少移动一个
3.其他元素随意,可以出现零行
接下来是主元的概念:矩阵中主元位置是矩阵中对应于它阶梯型中先导元素为1的位置,而放置的非零元即主元,相当于是每行的第一个非零元素。
介绍完上面这些概念,我们探讨如何将一个矩阵化成对应的阶梯型矩阵。因为只有化成阶梯型矩阵之后才能方便的求解。
为什么阶梯型矩阵如此重要?将它对应于线性方程组,最后一行其实就已经是单元素,对于可求解的线性方程组,那么可以进行求解;求解完成后依次回带,由于它的优良性质,每次只会多一个未知元,将已经求解的元带进去就又可以得到一个元,依次类推,最后均可计算出来。这对应于回带过程,详细过程将在下面展示。
我们上节提到了初等行变换,进行那三种变换不会影响矩阵,因而我们要利用这三种方式来化简一个矩阵。
我们待求的形式是什么?其实一句话:每一个主元下面都是0。因而,整个化简过程就两句话:找到主元,然后把它下面的非零元素全部消掉。
那么如何找到主元呢?接下来的讲解将使用以下的矩阵:
[ 0 − 3 − 6 4 9 − 1 − 2 − 1 3 1 − 2 − 3 0 3 − 1 1 4 5 − 9 − 7 ] \begin{bmatrix} 0 & -3 & -6 & 4 & 9\\ -1 & -2 & -1 & 3 & 1\\ -2 & -3 & 0 & 3 & -1\\ 1 & 4 & 5 & -9 & -7 \end{bmatrix} ⎣⎢⎢⎡0−1−21−3−2−34−6−105433−991−1−7⎦⎥⎥⎤
注意上面这句话:找到主元,然后把它下面的非零元素全部消掉。显然每行有个主元,那么我们同样的操作要执行 m m m遍, m m m为行数。
那么如何操作呢?
先从第一行开始。找到第一列的绝对值最小的非零元素,然后换到第一排。 对于上面的矩阵,我们可以把第四排换到第一行。因为交换两行不影响矩阵,因而可以随意交换。
于是矩阵变成下面这样:
[ 1 4 5 − 9 − 7 − 1 − 2 − 1 3 1 − 2 − 3 0 3 − 1 0 − 3 − 6 4 9 ] \begin{bmatrix} 1 & 4 & 5 & -9 & -7\\ -1 & -2 & -1 & 3 & 1\\ -2 & -3 & 0 & 3 & -1\\ 0 & -3 & -6 & 4 & 9 \end{bmatrix} ⎣⎢⎢⎡1−1−204−2−3−35−10−6−9334−71−19⎦⎥⎥⎤
然后,把它下面的行中第一列非零元素消掉。具体操作就是:每一行乘以 a 1 , 1 a i , 1 \frac{a_{1,1}}{a_{i,1}} ai,1a1,1然后减去第一行,就可以消去该行的第一个元素。对于上面的矩阵,相当于是第二行直接加上第一行,第三行加上第二行的两倍,第四行由于该列直接是0就不用处理。(注意:这里为了表述方便就用乘代替了除,其实严格来说应该是除)
处理之后我们发现第一行下面全是0了:
[ 1 4 5 − 9 − 7 0 2 4 − 6 − 6 0 5 10 − 15 − 15 0 − 3 − 6 4 9 ] \begin{bmatrix} 1 & 4 & 5 & -9 & -7\\ 0 & 2 & 4 & -6 & -6\\ 0 & 5 & 10 & -15 & -15\\ 0 & -3 & -6 & 4 & 9 \end{bmatrix} ⎣⎢⎢⎡1000425−35410−6−9−6−154−7−6−159⎦⎥⎥⎤
(你自己处理的时候可以把第二行除以2,第三行除以5,但是对于计算机处理的时候就不进行处理了)
人工处理后变成这样:
[ 1 4 5 − 9 − 7 0 1 2 − 3 − 3 0 1 2 − 3 − 3 0 − 3 − 6 4 9 ] \begin{bmatrix} 1 & 4 & 5 & -9 & -7\\ 0 & 1 & 2 & -3 & -3\\ 0 & 1 & 2 & -3 & -3\\ 0 & -3 & -6 & 4 & 9 \end{bmatrix} ⎣⎢⎢⎡1000411−3522−6−9−3−34−7−3−39⎦⎥⎥⎤
然后对第二行,同样处理:找到第二排及下面中,第二列的绝对值最小的非零元素,然后换到第二排。然后把第二行下面中第二列非零元素消掉。
具体操作就是:由于第二排第二个已经是绝对值最小的非零元素了,不进行处理;然后,第三排直接减第二排,第四排加上第二排的三倍,得到下面的:
[ 1 4 5 − 9 − 7 0 1 2 − 3 − 3 0 0 0 0 0 0 0 0 − 5 0 ] \begin{bmatrix} 1 & 4 & 5 & -9 & -7\\ 0 & 1 & 2 & -3 & -3\\ 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & -5 & 0 \end{bmatrix} ⎣⎢⎢⎡100041005200−9−30−5−7−300⎦⎥⎥⎤
对于第三行,一样处理,结果发现:没有非零元素了!如果你是解方程那么客户以直接判任意解或者无解了。但是我们这里是化简,因而继续。没有的话直接跳过。
那么到了第四行:第四列中非零元素在第四排,第三排没有(由于上一行没有处理因而还是以第三行作为起始处理行),因而把第四排换到第三排,就化简完成了:
[ 1 4 5 − 9 − 7 0 1 2 − 3 − 3 0 0 0 − 5 0 0 0 0 0 0 ] \begin{bmatrix} 1 & 4 & 5 & -9 & -7\\ 0 & 1 & 2 & -3 & -3\\ 0 & 0 & 0 & -5 & 0\\ 0 & 0 & 0 & 0 & 0 \end{bmatrix} ⎣⎢⎢⎡100041005200−9−3−50−7−300⎦⎥⎥⎤
这就是高斯消元的过程——化简到阶梯型矩阵。总时间复杂度为 O ( n 3 ) O(n^3) O(n3)
注意:该算法适用的不仅是矩阵化简。由于行列式也需要化成上三角行列式因而也需要这么处理。计算矩阵的秩仍然是该算法。因而请牢记。
下面附上这段处理的代码——人工处理必然没有计算机处理的精确。
for (int i = 1; i <= n;i++)
{
double base = a[i][i];//初始基准:a[i][i]
if(base==0)//基准为0,类比第一行的处理
{
for (int j = i + 1; j <= n;j++)//找到它下面行的非零元素
if(a[j][i]!=0)//这里是只要非零就换,但是正经的写法是找到非零的绝对值最小元素。
{
for (int k = i; k <= n + 1;k++)//交换整行
swap(a[i][k], a[j][k]);
base = a[i][i];//新的基准
break;
}
}
if(base==0)//整行全为0,直接进行下一行
continue;
for (int j = i + 1; j <= n;j++)//消去下面的所有非零元素
{
double multiple = a[j][i] / base;//a[j][i]/a[i][i]的倍数
for (int k = i; k <= n + 1;k++)
a[j][k] -= multiple * a[i][k];//该行每个元素都要消去,因为是对整行进行操作。
}
}
这样处理之后,就剩解了。这里有两种方法:一是若尔当消元(人工处理方法),或者是回带法(计算机处理方法)
若尔当消元,就是把一个阶梯型矩阵化简为简化阶梯型矩阵的过程。
而何为简化阶梯型矩阵呢?
1.每个非零行先导元素都是1。这个可以通过该行同时除以先导元素得到。
2.每个先导元素1是该元素所在列的唯一非零元素。达到这个的过程就是若尔当消元。
注意到每一行的先导元素下面都是0,那么为了达到第二条,我们需要向上消元。而阶梯型矩阵有个很好的性质:先导元素前面都是0。那么我们只需要用这一行依次对上面进行处理就行。
用上文中已经化简了的阶梯型矩阵举例:
[ 1 4 5 − 9 − 7 0 1 2 − 3 − 3 0 0 0 − 5 0 0 0 0 0 0 ] \begin{bmatrix} 1 & 4 & 5 & -9 & -7\\ 0 & 1 & 2 & -3 & -3\\ 0 & 0 & 0 & -5 & 0\\ 0 & 0 & 0 & 0 & 0 \end{bmatrix} ⎣⎢⎢⎡100041005200−9−3−50−7−300⎦⎥⎥⎤
若尔当消元过程是从下往上的,因而也叫向前步骤。而高斯消元是从上往下的,也叫向后步骤。
对于第四行,没有主元,下一个;
第三行,主元在第四列。先把第三行除以-5:
[ 1 4 5 − 9 − 7 0 1 2 − 3 − 3 0 0 0 1 0 0 0 0 0 0 ] \begin{bmatrix} 1 & 4 & 5 & -9 & -7\\ 0 & 1 & 2 & -3 & -3\\ 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & 0 \end{bmatrix} ⎣⎢⎢⎡100041005200−9−310−7−300⎦⎥⎥⎤
再对第二行加第三行的3倍,第一行加第三行的9倍:
[ 1 4 5 0 0 0 1 2 0 0 0 0 0 1 0 0 0 0 0 0 ] \begin{bmatrix} 1 & 4 & 5 & 0 & 0\\ 0 & 1 & 2 & 0 & 0\\ 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & 0 \end{bmatrix} ⎣⎢⎢⎡10004100520000100000⎦⎥⎥⎤
对第二行,主元在第二列。第一行减去第二行的四倍即可:
[ 1 0 − 3 0 0 0 1 2 0 0 0 0 0 1 0 0 0 0 0 0 ] \begin{bmatrix} 1 & 0 & -3 & 0 & 0\\ 0 & 1 & 2 & 0 & 0\\ 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & 0 \end{bmatrix} ⎣⎢⎢⎡10000100−320000100000⎦⎥⎥⎤
第一行已经满足条件。整个过程结束。这样操作完就可以出结果了:
{ x 1 − 3 x 3 = 0 x 2 + 2 x 3 = 0 x 3 为自由变量 x 4 = 0 \begin{cases} x_1-3x_3=0\\ x_2+2x_3=0\\ x_3\text{为自由变量}\\ x_4=0 \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧x1−3x3=0x2+2x3=0x3为自由变量x4=0
对于手工操作来说,这样就可以计算的更加准确,但是对于计算机通常不使用这种方法。
这种方法其实更好想:因为最后一个已经计算出来了,依次回带即可。我们拿一个可以解出来的矩阵举例子:
[ 1 3 − 5 4 0 1 − 3 3 0 0 1 2 ] \begin{bmatrix} 1 & 3 & -5 & 4\\ 0 & 1 & -3 & 3\\ 0 & 0 & 1 & 2 \end{bmatrix} ⎣⎡100310−5−31432⎦⎤
我们回想矩阵和线性方程组的关系,容易发现最后一行其实代表的是 x 3 = 2 x_3=2 x3=2。那么直接就解出来了,把这个信息回带到第二行,化简得到 x 2 = 9 x_2=9 x2=9,又解出来一个,再会带到第一行,计算出 x 1 = − 13 x_1=-13 x1=−13。因而解为
{ x 1 = − 13 x 2 = 9 x 3 = 2 \begin{cases} x_1=-13\\ x_2=9\\ x_3=2\\ \end{cases} ⎩⎪⎨⎪⎧x1=−13x2=9x3=2
直接上代码:
for (int i = n; i >= 1; i--)//从后往前计算
{
for (int j = i + 1; j <= n; j++)//将已经算出来存在ans里的元的值带进去
a[i][n + 1] -= ans[j] * a[i][j];
if (a[i][i] == 0)//有无主元
{
flag = a[i][n + 1] == 0 ? 0 : -1;
break;
}
ans[i] = a[i][n + 1] / a[i][i];//求解该行的主元
}