之前的文章介绍过Gauss消元法、Doolittle和Crout分解等解线性方程组的直接方法。但是直接法不是在任何时候都可用的,它有以下缺点:
- 在有舍入误差的情况下,直接法只能得到方程的近似解。
- 如果待求解问题规模很大,直接法求解的计算量是很大的,更糟的是随着计算量的增大舍入误差积累也会越多,从而得到的解误差更大。所以直接法适合小规模稠密型方程组的求解。
- 另外,直接法的程序设计也比较复杂。
迭代法解线性方程组
对于方程组
A
x
=
b
Ax=b
Ax=b 一般可以将其写成如下形式
x
=
M
x
+
g
x=Mx+g
x=Mx+g建立上述方程组的迭代公式:
x
(
k
+
1
)
=
M
x
(
k
)
+
g
,
k
=
0
,
1
,
2
,
…
x^{(k+1)}=Mx^{(k)}+g,k=0,1,2,\dots
x(k+1)=Mx(k)+g,k=0,1,2,…这个迭代公式有什么用呢?
当向量序列
x
(
k
)
x^{(k)}
x(k) 收敛于
x
∗
x^*
x∗ 时,有
x
∗
=
M
x
∗
+
g
x^*=Mx^*+g
x∗=Mx∗+g可以发现
x
∗
x^*
x∗ 即为方程组
x
=
M
x
+
g
x=Mx+g
x=Mx+g 的解,也就是说,当
x
(
k
)
x^{(k)}
x(k) 收敛时,
x
(
k
)
x^{(k)}
x(k) 即为方程组的解。
以上就是迭代法解方程组的基本原理,只要 x ( k ) x^{(k)} x(k)收敛,就可以通过k步迭代得到方程组的近似解。
下面介绍两种经典的迭代法:Jacobi迭代法和Gauss-Seidel迭代法。
Jacobi迭代法
假设有下列线性方程组
{
a
11
x
1
+
a
12
x
2
+
a
13
x
3
=
b
1
a
21
x
1
+
a
22
x
2
+
a
23
x
3
=
b
2
a
31
x
1
+
a
32
x
2
+
a
33
x
3
=
b
3
\left\{\begin{array}{l}a_{11}x_1+a_{12}x_2+a_{13}x_3=b_1\\a_{21}x_1+a_{22}x_2+a_{23}x_3=b_2\\a_{31}x_1+a_{32}x_2+a_{33}x_3=b_3\end{array}\right.
⎩⎨⎧a11x1+a12x2+a13x3=b1a21x1+a22x2+a23x3=b2a31x1+a32x2+a33x3=b3
将其写成下面形式
{
x
1
=
1
a
11
(
b
1
−
a
12
x
2
−
a
13
x
3
)
x
2
=
1
a
22
(
b
2
−
a
21
x
1
−
a
23
x
3
)
x
3
=
1
a
33
(
b
3
−
a
31
x
1
−
a
32
x
2
)
\left\{\begin{array}{l}x_1=\frac1{a_{11}}(b_1-a_{12}x_2-a_{13}x_3)\\x_2=\frac1{a_{22}}(b_2-a_{21}x_1-a_{23}x_3)\\x_3=\frac1{a_{33}}(b_3-a_{31}x_1-a_{32}x_2)\end{array}\right.
⎩⎨⎧x1=a111(b1−a12x2−a13x3)x2=a221(b2−a21x1−a23x3)x3=a331(b3−a31x1−a32x2)
建立迭代公式
{
x
1
(
k
+
1
)
=
1
a
11
(
b
1
−
a
12
x
2
(
k
)
−
a
13
x
3
(
k
)
)
x
2
(
k
+
1
)
=
1
a
22
(
b
2
−
a
21
x
1
(
k
)
−
a
23
x
3
(
k
)
)
x
3
(
k
+
1
)
=
1
a
33
(
b
3
−
a
31
x
1
(
k
)
−
a
32
x
2
(
k
)
)
\left\{\begin{array}{l}x_1^{(k+1)}=\frac1{a_{11}}(b_1-a_{12}x_2^{(k)}-a_{13}x_3^{(k)})\\x_2^{(k+1)}=\frac1{a_{22}}(b_2-a_{21}x_1^{(k)}-a_{23}x_3^{(k)})\\x_3^{(k+1)}=\frac1{a_{33}}(b_3-a_{31}x_1^{(k)}-a_{32}x_2^{(k)})\end{array}\right.
⎩⎪⎨⎪⎧x1(k+1)=a111(b1−a12x2(k)−a13x3(k))x2(k+1)=a221(b2−a21x1(k)−a23x3(k))x3(k+1)=a331(b3−a31x1(k)−a32x2(k))
对于每一个
x
i
(
k
+
1
)
x_i^{(k+1)}
xi(k+1) ,有如下公式
x
i
(
k
+
1
)
=
1
a
i
i
(
b
i
−
∑
j
=
1
i
−
1
a
i
j
x
j
(
k
)
−
∑
j
=
i
+
1
n
a
i
j
x
j
(
k
)
)
i
=
1
,
2
,
⋯
,
n
,
k
=
0
,
1
,
⋯
x_i^{(k+1)}=\frac1{a_{ii}}(b_i-\sum_{j=1}^{i-1}a_{ij}x_j^{(k)}-\sum_{j=i+1}^na_{ij}x_j^{(k)})\\i=1,2,\cdots,n,k=0,1,\cdots
xi(k+1)=aii1(bi−j=1∑i−1aijxj(k)−j=i+1∑naijxj(k))i=1,2,⋯,n,k=0,1,⋯然后我们就可根据这个公式解线性方程组了。
但是为了便于理论分析,我们把它写成矩阵的形式。
假设方程组为
A
x
=
b
Ax=b
Ax=b,将
A
A
A 分裂为
A
=
D
−
L
−
U
A = D - L - U
A=D−L−U其中
D
D
D 为对角矩阵
d
i
a
g
(
a
11
,
a
22
,
…
,
a
n
n
)
diag(a_{11},a_{22},\dots,a_{nn})
diag(a11,a22,…,ann),
−
L
-L
−L 和
−
U
-U
−U 分别如下
−
L
=
(
0
a
21
0
a
31
a
32
0
⋮
⋮
⋱
⋱
a
n
1
a
n
2
…
a
n
n
−
1
0
)
−
U
=
(
0
a
12
a
13
…
a
1
n
0
a
23
…
a
2
n
⋱
⋱
⋮
⋱
a
n
−
1
n
0
)
-L=\begin{pmatrix}0&&&&\\a_{21}&0&&&\\a_{31}&a_{32}&0&&\\\vdots&\vdots&\ddots&\ddots&\\a_{n1}&a_{n2}&\dots&a_{nn-1}&0\end{pmatrix}\\ \;\\ -U=\begin{pmatrix}0&a_{12}&a_{13}&\dots&a_{1n}\\&0&a_{23}&\dots&a_{2n}\\&&\ddots&\ddots&\vdots\\&&&\ddots&a_{n-1n}\\&&&&0\end{pmatrix}
−L=⎝⎜⎜⎜⎜⎜⎛0a21a31⋮an10a32⋮an20⋱…⋱ann−10⎠⎟⎟⎟⎟⎟⎞−U=⎝⎜⎜⎜⎜⎜⎛0a120a13a23⋱……⋱⋱a1na2n⋮an−1n0⎠⎟⎟⎟⎟⎟⎞则方程组和迭代公式可写为
(
D
−
L
−
U
)
x
=
b
x
=
D
−
1
(
L
+
U
)
x
+
D
−
1
b
x
(
k
+
1
)
=
D
−
1
(
L
+
U
)
x
(
k
)
+
D
−
1
b
(
迭
代
公
式
)
k
=
0
,
1
,
⋯
(D - L - U)x=b\\ \;\\ x = D^{-1}(L+U)x + D^{-1}b\\ \;\\ x^{(k+1)} = D^{-1}(L+U)x^{(k)} + D^{-1}b(迭代公式)\\ \;\\ k=0,1,\cdots
(D−L−U)x=bx=D−1(L+U)x+D−1bx(k+1)=D−1(L+U)x(k)+D−1b(迭代公式)k=0,1,⋯
可以验证矩阵形式的迭代公式和上面大括号方程组表示的迭代公式一致的。
下面根据公式 x ( k + 1 ) = D − 1 ( L + U ) x ( k ) + D − 1 b x^{(k+1)} = D^{-1}(L+U)x^{(k)} + D^{-1}b x(k+1)=D−1(L+U)x(k)+D−1b 编写代码
算法代码
def Jfunction(A,b,k):
n = A.shape[1]
D = np.eye(n)
D[np.arange(n),np.arange(n)] = A[np.arange(n),np.arange(n)]
LU = D - A
X = np.zeros(n)
for i in range(k):
D_inv = np.linalg.inv(D)
X = np.dot(np.dot(D_inv,LU),X) + np.dot(D_inv,b)
return X
实验
解下列方程组
方程组的精确解为
x
=
(
1
,
1
,
1
)
x = (1,1,1)
x=(1,1,1)
A = np.array([
[10,3,1],
[2,-10,3],
[1,3,10]
])
b = np.array([14,-5,14])
k = 6
X = Jfunction(A,b,k);
实验结果
Gauss-Seidel迭代法
在J迭代法中在计算
x
i
(
k
+
1
)
x_i^{(k+1)}
xi(k+1)时,前
i
−
1
i-1
i−1 个分量已经计算完成了,如果可以利用这些信息,可能加速迭代法的收敛。因此我们对迭代公式做如下修改
{
x
1
(
k
+
1
)
=
1
a
11
(
b
1
−
a
12
x
2
(
k
)
−
a
13
x
3
(
k
)
)
x
2
(
k
+
1
)
=
1
a
22
(
b
2
−
a
21
x
1
(
k
+
1
)
−
a
23
x
3
(
k
)
)
x
3
(
k
+
1
)
=
1
a
33
(
b
3
−
a
31
x
1
(
k
+
1
)
−
a
32
x
2
(
k
+
1
)
)
\left\{\begin{array}{l}x_1^{(k+1)}=\frac1{a_{11}}(b_1-a_{12}x_2^{(k)}-a_{13}x_3^{(k)})\\x_2^{(k+1)}=\frac1{a_{22}}(b_2-a_{21}x_1^{(k+1)}-a_{23}x_3^{(k)})\\x_3^{(k+1)}=\frac1{a_{33}}(b_3-a_{31}x_1^{(k+1)}-a_{32}x_2^{(k+1)})\end{array}\right.
⎩⎪⎨⎪⎧x1(k+1)=a111(b1−a12x2(k)−a13x3(k))x2(k+1)=a221(b2−a21x1(k+1)−a23x3(k))x3(k+1)=a331(b3−a31x1(k+1)−a32x2(k+1))这就是Gauss-Seidel迭代法的迭代公式
对于每一个
x
i
(
k
+
1
)
x_i^{(k+1)}
xi(k+1) ,有如下公式
x
i
(
k
+
1
)
=
1
a
i
i
(
b
i
−
∑
j
=
1
i
−
1
a
i
j
x
j
(
k
+
1
)
−
∑
j
=
i
+
1
n
a
i
j
x
j
(
k
)
)
i
=
1
,
2
,
⋯
,
n
,
k
=
0
,
1
,
⋯
x_i^{(k+1)}=\frac1{a_{ii}}(b_i-\sum_{j=1}^{i-1}a_{ij}x_j^{(k+1)}-\sum_{j=i+1}^na_{ij}x_j^{(k)})\\i=1,2,\cdots,n,k=0,1,\cdots
xi(k+1)=aii1(bi−j=1∑i−1aijxj(k+1)−j=i+1∑naijxj(k))i=1,2,⋯,n,k=0,1,⋯上述公式也可以写成矩阵形式,我们可以从J迭代法的矩阵形式推出G-S迭代法的矩阵形式
按照J迭代法的迭代公式有
x
(
k
+
1
)
=
D
−
1
(
L
+
U
)
x
(
k
)
+
D
−
1
b
D
x
(
k
+
1
)
=
L
x
(
k
)
+
U
x
(
k
)
+
b
x^{(k+1)} = D^{-1}(L+U)x^{(k)} + D^{-1}b\\ \;\\ Dx^{(k+1)} =Lx^{(k)}+Ux^{(k)}+b
x(k+1)=D−1(L+U)x(k)+D−1bDx(k+1)=Lx(k)+Ux(k)+b不难发现
L
x
(
k
)
Lx^{(k)}
Lx(k) 对应了G-S迭代公式中的
∑
j
=
1
i
−
1
a
i
j
x
j
(
k
+
1
)
\sum_{j=1}^{i-1}a_{ij}x_j^{(k+1)}
∑j=1i−1aijxj(k+1) 部分,因此按照G-S迭代法的迭代公式,上面迭代公式应该写成
D
x
(
k
+
1
)
=
L
x
(
k
+
1
)
+
U
x
(
k
)
+
b
Dx^{(k+1)} =Lx^{(k+1)}+Ux^{(k)}+b
Dx(k+1)=Lx(k+1)+Ux(k)+b整理,得
x
(
k
+
1
)
=
(
D
−
L
)
−
1
U
x
(
k
)
+
(
D
−
L
)
−
1
b
x^{(k+1)} =(D-L)^{-1}Ux^{(k)}+(D-L)^{-1}b
x(k+1)=(D−L)−1Ux(k)+(D−L)−1b
下面根据公式 x ( k + 1 ) = ( D − L ) − 1 U x ( k ) + ( D − L ) − 1 b x^{(k+1)} =(D-L)^{-1}Ux^{(k)}+(D-L)^{-1}b x(k+1)=(D−L)−1Ux(k)+(D−L)−1b 编写代码
算法代码
def G_Sfunction(A,b,k):
n = A.shape[1]
D = np.eye(n)
D[np.arange(n),np.arange(n)] = A[np.arange(n),np.arange(n)]
LU = D - A
L = np.tril(LU)
U = np.triu(LU)
D_L = D - L
X = np.zeros(n)
for i in range(k):
D_L_inv = np.linalg.inv(D_L)
X = np.dot(np.dot(D_L_inv,U),X) + np.dot(D_L_inv,b)
return X
实验
解下列方程组(一样的例题)
方程组的精确解为
x
=
(
1
,
1
,
1
)
x = (1,1,1)
x=(1,1,1)
A = np.array([
[10,3,1],
[2,-10,3],
[1,3,10]
])
b = np.array([14,-5,14])
k = 6
X = G_Sfunction(A,b,k);
实验结果
可以发现G-S迭代法在第4次迭代完成的时候就已经达到了J迭代法第六次迭代完成的效果了。
迭代法的特点
- 只能求的近似解
- 可以在迭代的过程中对误差进行矫正
- 如果系数矩阵是稀疏的,即使面对大规模的问题,也可能通过较小的计算量得到方程组的解
- 原理简单方便实现