本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
LAFF Week 6 Notes
本章的主要内容:
在中学的时候我们都学习过如何解三元一次方程组。来看看下面三元一次方程组是怎么解的: $$ \left\{\begin{matrix} 2x_0& &+&4x_1 &-&2x_2 &=& -10&\\ 4x_0& &-&2x_1 &+&6x_2 &=& 20& \\ 6x_0& &-&4x_1 &+&2x_2 &=&18& \end{matrix}\right. $$ 解的方法很简单:消元;也就是用方程组内的几个线性关系通过加减、与标量相乘 和等式的顺序变换,可以达到减少未知数的效果。上面的例子,我们可以观察到:
-2
乘以第一行的结果,可以消去第二行中的 $x_0$-3
乘以第一行的结果,可以消去第三行中的 $x_0$
完成上面两步以后,我们的方程组变成了这个样子:
$$
\left\{\begin{matrix}
2x_0& &+&4x_1 &-&2x_2 &= &-10\\
& &-&10x_1 &+&10x_2 &= &40 \\
& &-&16x_1 &+&8x_2 &= &48
\end{matrix}\right.
$$
我们发现第二行和第三行的方程只存在 $x_1$、$x_2$ 两个未知数了。按照之前的方法,我们在将第三行加上第二行乘以 -1.6
的结果,发现可以消除第三行中的 $x_1$。得到的结果如下:
$$
\left\{\begin{matrix}
2x_0& &+&4x_1 &-&2x_2 &= &-10\\
& &-&10x_1 &+&10x_2 &= &40 \\
& && &-&8x_2 &= &-16
\end{matrix}\right.
$$
我们可以将上面的方程组写成如下不带未知变量的形式:
$$
\left(
\begin{array}{r r r | r}
2 & 4 & -2 & -10 \\
4 & -2 & 6 & 20 \\
6 & -4 & 2 & -18
\end{array}
\right)
$$
这样的形式被称为 Appended Matrices,这种形式可以更方便的将方程组与矩阵联系起来。让我们把上面这个流程写成该矩阵的形式:
$$
\left( \begin{array}{r r r | r}
2 & 4 & -2 & -10 \\
& -2 & 6 & 40 \\
& -4 & 2 & -48
\end{array} \right)\tag{step.1}
$$
$$
\left( \begin{array}{r r r | r}
2 & 4 & -2 & -10 \\
& -2 & 6 & 40 \\
& & 2 & -16
\end{array} \right)\tag{step.2}
$$
如果我们把每一步消去的未知变量的位置都用 0
填充的话,我们会发现整个消元的过程实际上是目标矩阵变换为一个三角矩阵的过程(此处是上三角矩阵):
$$
\left( \begin{array}{r r r | r}
2 & 4 & -2 & -10 \\
0 & -2 & 6 & 40 \\
0& 0 & 2 & -16
\end{array} \right)
$$
我们再来看看当初将例子中的原矩阵变换为三角矩阵的(以 $L$ 表示方程组的行):
现在我们将上面例子中的 Appended Matrices 的左边部分表示为一个单位矩阵与其的乘积:
$$
\left( \begin{array}{r r r}
1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{array} \right)
\left(
\begin{array}{r r r | r}
2 & 4 & -2 & -10 \\
4 & -2 & 6 & 20 \\
6 & -4 & 2 & -18
\end{array}
\right)
$$
如果我们注意观察的话,我们第一步和第二步是需要消除第二行和第三行中的 $x_0$。先来看看第二行,这里发生的点积如下图:
<html>
<img src=“/_media/math/linear_algebra/laff/gs_tr.svg” width=“600”>
</html>
消除第二行中 $x_0$,也就是将目标矩阵中第二行中 $x_0$ 的系数置零。在计算该系数的点积中,我们只需要将单位矩阵中参与点积运算的第二行的第一个元素修改,使其与 2
的乘积与第二行 $x_0$ 系数
-4
之和为 0
即可,也就是通过修改单位矩阵第二行第一个元素,保证单位矩阵第二行与目标矩阵第一列点积为 0
即可。
因此我们能得到上图粉色区域的关系,从而轻松的求到一个 multiplier
(粉色区域的 ?
),也就是我们之前在高斯消元中用第二行和第一行消除 $x_0$ 需要用到的系数。而这个系数恰好能放到单位矩阵对应的位置中。
依次类推,第三行与第一行消除 $x_0$、第三行与第二行消除 $x_1$ 的系数,我们都可以放置到这个单位矩阵中。因此我们可以用如下的矩阵乘法来表示之前高斯消元的整个过程:
$$
\left( \begin{array}{r r r}
1 & 0 & 0 \\
\frac{-4}{2} & 1 &0 \\
\frac{-6}{2} & -\frac{-16}{-10} & 1 \end{array} \right)
\left(
\begin{array}{r r r | r}
2 & 4 & -2 & -10 \\
4 & -2 & 6 & 20 \\
6 & -4 & 2 & -18
\end{array}
\right)
$$
我们再观察作为结果的最右边的部分。在之前的高斯消元法中,我们每进行消除一个未知数的步骤,都需要同时更新最右边方程组的结果部分。
而在高斯变换中,我们得知了如何用一个矩阵来表示整个目标矩阵消元的过程。因此对于代表方程组结果的向量,我们并不需要每次更新,只需要在得到消元过程的矩阵之后,将其与目标矩阵中最右边代表结果的向量做矩阵乘法,就可以得到消元以后代表方程组结果的新向量。以上面的例子可以表示为:
$$
\begin{pmatrix}
1 & 0 & 0 \\
\frac{-4}{2} & 1 &0 \\
\frac{-6}{2} & -\frac{-16}{-10} & 1
\end{pmatrix}
\begin{pmatrix}
-10 \\
20 \\
-18
\end{pmatrix}
=
\begin{pmatrix}
-10 \\
40 \\
-16
\end{pmatrix}
$$
我们将这种把高斯变换直接应用到目标矩阵的最右边部分的运算称为 Forward substitution.
在计算机中,算法的设计与数学上的推理有一些差异。这种差异主要体现在计算机算法的设计要尽量的节省对内存的读取。下面来看看高斯变换的设计过程。
首先我们再次确认下目标:我们需要通过高斯变换将目标矩阵转化为一个上三角矩阵。根据之前高斯变换部分的推导可以发现,每次迭代中我们都需要消除一个变量;也就是说,每次对单位矩阵的更新是一个列向量。因此,我们采用四象分割法来对矩阵进行处理。
其次,我们来看一下单位矩阵中需要更新的 multiplier
与目标矩阵中向量之间的关系。根据之前推导的结果很容易发现, multiplier
与目标矩阵中向量的关系(以之前的例子为例):
<html>
<img src=“/_media/math/linear_algebra/laff/gs_al_step_1_r.svg” width=“350”>
</html>
当我们得到了消除 $x_0$ 的向量,我们需要使用该向量消除目标矩阵中需要被消除 $x_0$ 系数的行,本例中应该是目标矩阵中第一列第二、三行的第一个元素,也就是上图中的 A21
:
<html>
<img src=“/_media/math/linear_algebra/laff/gs_al_step_2.svg” width=“450”>
</html>
而此时为了节省空间,我们发现目标矩阵中已经被我们消除了 $x_0$ 系数向量($A_{21}$)的位置全是 0
,因此我们可以利用该位置来存储单位矩阵中高斯消元的记录(上图第三个矩阵)。
这里需要注意的是,单位矩阵中存放的高斯消元的步骤向量,是需要求负的,这样算出结果才正确。但如果将该步骤向量存放到目标矩阵中,则不需要加负号了(课程中也没有详细说明,个人猜测应该是默认操作为减法)
应用了高斯变换之后,我们需要更新一下我们的目标矩阵,而更新的方法很简单,直接使用更新单位矩阵对应列与目标矩阵的对应行点积就可得到目标矩阵中更新后的元素即可。下图的演示过程是目标矩阵中最正中心的元素 -2
,更新之后值变为 -10
:
<html>
<img src=“/_media/math/linear_algebra/laff/gs_al_step_3.svg” width=“450”>
</html>
利用更新后的单位矩阵将目标矩阵更新(也就是更新的单位矩阵乘上目标矩阵)后,再存储上高斯变换的步骤,我们就可以得到新的目标矩阵:
$$
\begin{pmatrix}
2 & 4& -2\\
2 & -10 & 10\\
3 & -16 & 8
\end{pmatrix}
$$
到此我们完成了第一轮循环,也就是消除 $x_0$ 序数的循环。接下来我们接着分割矩阵,进入第二轮消除 $x_1$ 系数的循环。在本例中,本轮是目标矩阵的第二行与第三行进行消除操作。而在经过第二轮的分割,参与运算的目标矩阵如下图:
<html>
<img src=“/_media/math/linear_algebra/laff/gs_al_step_4.svg” width=“350”>
</html>
我们发现第二轮的更新中依然是用对角线元素下方的向量来除以对角线上的元素来获取 multiplier
,也就是:
$$L_{21} = - A_{21} / a_{11}$$
因此,整个算法中对于目标矩阵左边部分的操作,其迭代部分已经非常明显了:
multiplier
当然,我们还记得高斯变换算法中需要对目标矩阵的右边部分进行 Forward substitution。在算法中,我们可以通过每一步迭代中得到的更新的单位矩阵与当前的目标矩阵的右边部分相乘,从而得到更新后的目标矩阵的右部。本步骤在算法中作为独立的一部分算法,在后面会接着讨论。
看算法的一个诀窍:把所有下标为 $1$ 的内容想象成当前循环中的内容。
之前的内容中我们已经讨论过高斯消元算法中每次迭代的内容。为了算法设计的方便,我们再来看看这部分迭代内容有什么样的特点。
参考一下之前用单位矩阵表达消元步骤的方法,我们发现在每一步的消元过程中,目标矩阵的对应区域都会归零(也就是通过抵消掉指定变量的系数来消除变量)。
而从输入输出的角度来看,有两点需要注意:
multiplier
。
根据上面的信息,如果使用四象分割,那么一个高斯变换的迭代部分可以表示为以下的矩阵乘法:
$$
\left( \begin{array}{r | r r}
\color{blue}{I} & 0 & 0 \\
\hline
0 & 1 & 0 \\
0 & \color{red}{-l_{21}} & \color{blue}{I}
\end{array} \right)
\left(
\begin{array}{r | r r}
U_{00} & u_{01} & U_{22} \\
\hline
0 & a_{11} & a_{12}^T \\
0 & a_{21} & A_{22}
\end{array}
\right)
=
\left(
\begin{array}{r | r r}
U_{00} & u_{01} & U{22} \\
\hline
0 & v_{11} & u_{12}^T \\
0 & 0 & A_{22}^{new}
\end{array}
\right)
$$
那么这个算法中每次需要更新的有哪些呢?很显然,是 multiplier
和 子矩阵 A22
。因此我们不妨利用这个关系,来看看如何得到 multiplier
和 A22
至于矩阵中其他向量 / 元素的表达式。先将等号左边计算一下:
$$
\left( \begin{array}{r | r r}
\color{blue}{I} & 0 & 0 \\
\hline
0 & 1 & 0 \\
0 & \color{red}{-l_{21}} & \color{blue}{I}
\end{array} \right)
\left(
\begin{array}{r | r r}
U_{00} & u_{01} & U_{22} \\
\hline
0 & a_{11} & a_{12}^T \\
0 & a_{21} & A_{22}
\end{array}
\right)
=
\left(
\begin{array}{r | r r}
U_{00} & u_{01} & U_{22} \\
\hline
0 & \left( \begin{array}{r | r}
1 & 0 \\
\hline
\color{#F0F}{-l_{21}} & \color{#F0F}{I}
\end{array} \right)
& \left( \begin{array}{r | r}
\color{green}{a_{11}} & a_{12}^T \\
\hline
a_{21} & A_{22}
\end{array} \right) \\
\end{array}
\right)
\\
=
\left(
\begin{array}{r | r r}
U_{00} & u_{01} & U_{22} \\
\hline
0 &
& \left( \begin{array}{r | r}
a_{11} & a_{12}^T \\
\hline
\color{red}{-l_{21}a_{11}+a_{21}} & \color{#F0F}{-l_{21}a_{12}^T + A_{22} }
\end{array} \right) \\
\end{array}
\right)
$$
将这个结果与矩阵乘法的右边对应,我们可以得到两个等式:
整理一下:
这里有一点非常重要:我们之前提到过,为了节省空间,我们可以将 multiplier
存放到目标矩阵中。在迭代中,我们可以看到的是 $a_{21}$ 的位置实际上存放了单位矩阵中 $l_{21}$ 的内容。那么这个过程可以描述为两个步骤:
因此整个迭代部分的算法就可以写成:
a21 = a21 / a11 //it means l21=a21/a11, then a21 = l21;
A22 = A22 - a21 * a12t // means A22=A22-l21*a12t , rank 1 update
下面是完整的算法:
Forward Substitution 就是将表示消元步骤的单位矩阵与目标矩阵的右边表示结果的向量相乘,得到与变换后的三角矩阵匹配的结果向量。利用之前的表示方法,我们也可以将该迭代过程表示成如下的矩阵乘法:
$$
\left( \begin{array}{r | r r}
\color{blue}{I} & 0 & 0 \\
\hline
0 & 1 & 0 \\
0 & \color{red}{-l_{21}} & \color{blue}{I}
\end{array} \right)
\left( \begin{array}
y_0 \\
\hline
\color{blue}{\beta_1} \\
\hline
b_2
\end{array} \right)
=
\left( \begin{array}
y_0 \\
\hline
\color{blue}{\chi_1} \\
\hline
b_2^{new}
\end{array} \right)
$$
化简左边以后可以得到:
$$
\left( \begin{array}
y_0 \\
\hline
\color{blue}{\beta_1} \\
\hline
\color{#0F0}-l_{21}\beta{11}+b_2
\end{array} \right)
=
\left( \begin{array}
y_0 \\
\hline
\color{blue}{\chi_1} \\
\hline
b_2^{new}
\end{array} \right)
$$
可以看到的是,每次更新中,只有 $b_2$ 的值发生了更新,也就是每次更新只对结果变量的余下部分造成影响。因此每次迭代的内容实际上就是更新 $b_2$。又因为 $l_{21}$ 实际上就是更新后的 $a_{21}$,因此整个迭代过程可以记做:
b_2 = b_2 - a21b11
整个算法演示如下:
先来了解一下 LU 分解的概念:
n*n
的矩阵n*n
的下三角单位矩阵n*n
的上三角矩阵
对于以上条件,如果存在 $A = LU$,那么我们就称 $LU$ 是 $A$ 的 $LU$ 分解。
本章这里我们暂且认为 $LU$ 分解就是高斯消元 的一种,在之后的章节里会对这两者的关系作出详细解释。同时,我们也会在后面看到两者几乎拥有一模一样的算法分析步骤及内容。
还是按之前处理高斯变换的四象分割的办法,我们将 $A、L、U$ 分别分割,然后用矩阵乘法表示我们之前提到过的等式 $A=LU$:
$$
\left( \begin{array}{c | c c} A_{00} & a_{01} & A_{02} \\ \hline a_{10}^T & \alpha_{11} & a_{12}^T \\
A_{20} & a_{21} & A_{02}
\end{array} \right) =
\left( \begin{array}{c | c c} L_{00} & 0 & 0\\ \hline l_{10}^T & 1 & 0 \\
L_{20} & l_{21} & L_{02}
\end{array} \right)
\left( \begin{array}{c | c c} U_{00} & u_{01} & U_{02} \\ \hline 0 & \upsilon_{11} & u_{12}^T \\
0& 0 & A_{02}
\end{array} \right)
$$
这里是不是很熟悉,跟之前的高斯变换简直一模一样!而我们注意到了高斯变换中,真正对算法起更新作用的,只有在当前循环所处的分割块的元素(比如什么 $U_{00}$ 的就完全没啥用)。那么对于 $LU$ 分解,假设当前是第一次循环,因此做为上次循环中的元素 $A_{00}$ 为空;也就是说第一次的分割中 $$
\displaystyle \left( \begin{array}{c c} \alpha_{11} & a_{12}^T \\
a_{21} & A_{02}
\end{array} \right)
$$
代表了整个目标矩阵(不清楚可参考 Week.3 1.1)。那么很显然,我们有:
$$
\left( \begin{array}{r | r} \alpha_{11} & a_{12}^T \\
\hline
a_{21} & A_{02}
\end{array} \right) =
\left( \begin{array}{r | r} 1 & 0 \\
\hline
l_{21} & L_{02}
\end{array} \right)
\left( \begin{array}{r | r}\upsilon_{11} & u_{12}^T \\
\hline
0 & A_{02}
\end{array} \right)
\\
=
\left( \begin{array}{r | r} \upsilon_{11} & a_{12}^T \\
\hline
\upsilon_{11} l_{21} & l_{21} u_{12}^T + L_{22} U_{22}
\end{array} \right)
$$
将元素一一对应起来,则能得到以下关系:
从上面关系中,我们发现每次迭代更新的内容依然是 multiplier
和 目标矩阵。不过对于目标矩阵的更新,我们发现这里有一点与之前的高斯变换不同;这里面多了一个 $L_{22}U_{22}$。
如果足够细心,就能明白发现这个矩阵乘积的意义:$L_{22}$ 是一个单元下三角矩阵, $U_{22}$ 是上三角矩阵,他们又各自是 $L$ 与 $U$ 的子矩阵。$A_{22}$ 是我们已知的,$l_{21}u_{12}^T$,也就是 $a_{21}u_{12}^T$,是一个外积,一个相对于 $A_{22}$ 的 rank1 更新,并且也是已知的。那么这一行的意思就很明显了:
那么整个算法的迭代部分就基本可以确定了:
a21 = a21/a11
A22 = A22 - a21a12t
整个算法如下:
这一个分解是解出 $z$ 是多少,并提供给后面的 $Ux = z$。
回想一下之前高斯变换的 forward substitution,我们用一个代表消元的矩阵乘以
按照老办法,我们将 $L$ 也按四象分割。再自上往下分割 向量 $z$ 与 目标向量 $b$。根据 $Lz = b$ 我们可以得到以下等式:
$$
\left( \begin{array}{c | c}
1 & 0 \\
\hline
l_{21} & L_{22} \\
\end{array}
\right)
\left( \begin{array} {r}
\zeta_1 \\
\hline
z_2
\end{array}
\right)
=
\left(\begin{array}{r} \beta_1 \\ \hline b_2 \end{array}\right)
$$
化简后:
$$
\left( \begin{array} {c}
\zeta_1 \\
\hline
l_{21}\zeta_1 + L_{22}z{2}
\end{array}
\right)
=
\left(\begin{array}{c} \beta_1 \\ \hline b_2 \end{array}\right)
$$
因此可以得到关系:
注意 $L_{22}z_{2}$,是一个下三角单元矩阵与向量的乘积。这里与 $LU$ 分解类似,该乘积代表了更新后 $b$ 的值。而对其更新的是 $-l_{21}\zeta_1$。因此每次迭代中的更新的内容如下:
b2 = b2 -beta1 * l21
迭代开头的 b2
就是更新过的 $b$,也就是 $L_{22}z_{2}$。下面是整个算法:
之前解决了 $Lz = b$ 这个方程组,我们得到了现在我们需要求解 $Ux = z$ 。这一步是在求解 $x$ 是多少。按照之前的手法,将参与运算的单位都进行对应的分解,然后根据 $Ux = z$ 列出等式,有:
$$
\left( \begin{array}{c | c}
\nu_{11} & u_{12}^T \\
\hline
0 & U_{22} \\
\end{array}
\right)
\left( \begin{array} {r}
\chi_1 \\
\hline
x_2
\end{array}
\right)
=
\left(\begin{array}{r} \zeta_1 \\ \hline z_2 \end{array}\right)
$$
化简以后有:
$$
\left( \begin{array} {c}
\nu_{11}\chi_1 + u_{12}^Tx_2\\
\hline
U_{22}x_{2}
\end{array}
\right)
=
\left(\begin{array}{c} \zeta_1 \\ \hline z_2 \end{array}\right)
$$
这里跟之前有一些不同。想象一下之前高斯消元中的 back substitution,求解的过程是自下而上的。也就是说,我们会先解出 $x_2$,再代入 $x_2$ 解出 $x_1$,以此类推。那么我们再来看看上面的式子,其中第二行的关系是:
$$
U_{22}x_2 = z_2
$$
假设当前的循环开始于这么一个状态:
那么作为结果的 $z$ 向量中,对应 $x_2$ 部分的分量 $z_2$,存放的就是上一轮计算出来的 $x_2$。因为此时并没有通过 $U_{22}x_2$ 来更新 $z_2$ 中的内容。因为 $z_2$ 是已知的,那么可以确定的是:
$$z_2 = x_2$$
再将这个结果带入到
$$\nu_{11}\chi_1 + u_{12}^Tx_2 = \zeta_1$$
可以得到:
$$ \chi_1 = (\zeta_1 - u_{12}^Tz_2) / \nu_{11} $$
那么可以看出来,整个循环的过程是这样的:
接下来就是算法部分了。我们注意到,在逐步求出 $x$ 的过程中,每一次迭代中我们只会使用对应大小的 $z$ 的分量来求 $x$。这就意味着之前使用的 $z$ 中对应分量不会再使用到了,一个例子就是我们之前所提到的 $x_2 = z_2$。因此我们可以利用 $z$ 的空间来存储 $x$,也就是将每次求出的 $x$ 直接存储到 $z$ 中。那么循环部分的算法实际上就可以写成:
zeta1 = zeta1 - u12t * zeta2 // x1 = zeta1 -u12t *zeta2, zeta1 = x1;
zeta1 = zeta1 / u11 // update chi1
完整的算法如下。注意该算法是自左下往右上的,是一个逐步用求得未知数代入上面的方程,求得新的未知数的过程。