1. 引言

几乎所有的物理系统或科学问题都涉及一个线性代数方程组。例如,拟合数据点曲线、优化成本函数、分析电路网络等问题,都需要求解线性方程。

计算线性代数中一些基本任务包括:

  • 求解线性方程组 $ A\mathbf{x} = \mathbf{b} $
  • 求一个方阵 $ A $ 的逆
  • 计算矩阵的行列式

接下来将介绍一些用于执行这些基本任务的实用算法。

我们将学习如何将一个矩阵分解成更简单的“积木块”。对这些较小的“积木块”进行操作通常比直接处理原始矩阵更容易。

2. 部分选主元的高斯消去法

考虑一个包含三个未知数的线性方程组:

$$ \begin{align*} x + 2y + z &= 2\ 2x + 6y + z &= 7\ x + y + 4z &= 3 \end{align*} $$

我们可以将其写成矩阵形式 $ A\mathbf{x} = \mathbf{b} $:

$$ \begin{bmatrix} 1 & 2 & 1 \ 2 & 6 & 1 \ 1 & 1 & 4 \end{bmatrix} \begin{bmatrix} x \ y \ z \end{bmatrix} = \begin{bmatrix} 2 \ 7 \ 3 \end{bmatrix} $$

高中阶段学过的解法是通过初等行变换对方程进行处理。由于行变换会影响方程右边的值,因此我们通常使用增广矩阵 $ M=[A|\mathbf{b}] $ 来跟踪所有变化。

我们将 $ A $ 中第 $ (1,1) $ 位置的元素称为第一个主元(pivot),第 $ (2,2) $ 位置的元素称为第二个主元,依此类推。在高斯消去法中,核心思想是:在第 $ j $ 列中,将主元 $ a_{jj} $ 下方的所有元素变为 0。

因此,一个关键要求是:主元必须非零。 有时我们需要交换第 $ j $ 行和第 $ k $ 行(其中 $ k > j $),以确保主元非零。最常见的做法是选择 $ k $,使得 $ |a_{kj}| $ 最大化。这被称为部分选主元(Partial Pivoting)。

高斯消去法的伪代码如下:

algorithm GaussianElim(A, b):
    // INPUT
    //    A = 非奇异矩阵,大小 n x n
    //    b = 右侧向量
    // OUTPUT
    //    U = 上三角形式的 A
    //    c = 对应 U 的变换后的 b 向量

    if A[j][j] == 0:
        big = 0.0
        kRow = j
        for k from j+1 to n-1:
            if abs(A[k][j]) > big:
                big = abs(A[k][j])
                kRow = k
        // 交换行
        for l from j to n-1:
            swap A[j][l] and A[kRow][l]
        swap b[j] and b[kRow]

    pivot = A[j][j]
    if pivot == 0:
        throw exception (矩阵奇异)

    for i from j+1 to n-1:
        mult = A[i][j] / pivot
        for l from j to n-1:
            A[i][l] -= mult * A[j][l]
        b[i] -= mult * b[j]

    return A, b

消去完成后,我们得到一个上三角系统 $ U\mathbf{x} = \mathbf{c} $,可以使用回代法(Back Substitution)求解:

algorithm BackSubstitution(U, c):
    // INPUT
    //    U = 上三角矩阵
    //    c = 右侧向量
    // OUTPUT
    //    解向量 x

    for i from n-1 downto 0:
        sum = 0
        for j from i+1 to n-1:
            sum += x[j] * U[i][j]
        x[i] = (c[i] - sum) / U[i][i]
    return x

例如,求解上述电路问题得到的解向量为 $ \mathbf{x} = [-3, 2, 1] $。

3. LU 分解

假设我们可以将矩阵 $ A $ 分解为两个矩阵的乘积:

$$ A = L \cdot U $$

其中 $ L $ 是下三角矩阵,$ U $ 是上三角矩阵。我们可以用这个分解来解线性方程组:

$$ A\mathbf{x} = (L \cdot U)\mathbf{x} = L \cdot (U\mathbf{x}) = \mathbf{b} $$

我们首先求解 $ L\mathbf{y} = \mathbf{b} $,然后求解 $ U\mathbf{x} = \mathbf{y} $。这两个三角系统的求解分别使用前向代入(Forward Substitution)和回代法(Back Substitution)即可。

前向代入算法如下:

algorithm FwdSubstitution(L, b):
    for i from 0 to n-1:
        sum = 0
        for j from 0 to i-1:
            sum += y[j] * L[i][j]
        y[i] = (b[i] - sum) / L[i][i]
    return y

3.1. LU 分解算法

设 $ A $ 是一个 $ n \times n $ 的非奇异方阵,我们可以将其写成块形式:

$$ \begin{bmatrix} a_{11} & \mathbf{a_{12}} \ \mathbf{a_{21}} & A_{22} \end{bmatrix} = \begin{bmatrix} 1 & \mathbf{0} \ \mathbf{l_{21}} & L_{22} \end{bmatrix} \begin{bmatrix} u_{11} & \mathbf{u_{12}} \ \mathbf{0} & U_{22} \end{bmatrix} $$

通过比较两边,可以得到:

$$ \begin{align*} u_{11} &= a_{11} \ \mathbf{u_{12}} &= \mathbf{a_{12}} \ \mathbf{l_{21}} &= \frac{1}{u_{11}} \mathbf{a_{21}} \ L_{22}U_{22} &= A_{22} - \mathbf{l_{21}} \cdot \mathbf{u_{12}} \end{align*} $$

我们可以递归地解决这个子问题,从而得到完整的 LU 分解。

伪代码如下:

algorithm LUdcmp(A):
    // INPUT
    //    A = 非奇异方阵
    // OUTPUT
    //    L = 单位下三角矩阵
    //    U = 上三角矩阵

    N = A 的维度

    if N == 1:
        return L = [1], U = A

    a11 = A[0][0]
    a12 = A[0][1..N-1]
    a21 = A[1..N-1][0]
    A22 = A[1..N-1][1..N-1]

    u11 = a11
    u12 = a12
    l21 = a21 / u11

    S22 = A22 - l21 * u12

    L22, U22 = LUdcmp(S22)

    构造 L 和 U 矩阵并返回

4. QR 分解

除了 LU 分解,还有一种常用的矩阵分解方法是 QR 分解。它在某些场景下特别有用。

4.1. Gram-Schmidt 正交化过程

假设我们有一组基向量 $ \mathcal{B} = { \mathbf{w}_1, \ldots, \mathbf{w}_n } $,我们希望从中构造一个正交基 $ { \mathbf{v}_1, \ldots, \mathbf{v}_n } $。

Gram-Schmidt 过程如下:

$$ \mathbf{v}_k = \mathbf{w}k - \sum{j=1}^{k-1} \frac{\mathbf{w}_k \cdot \mathbf{v}_j}{|\mathbf{v}_j|^2} \mathbf{v}_j $$

通过这个过程,我们可以逐步构造出正交基。

4.2. Gram-Schmidt 的改进

我们可以通过正交化后归一化的方式直接构造标准正交基 $ { \mathbf{u}_1, \ldots, \mathbf{u}_n } $,并表示原基向量为:

$$ \mathbf{w}j = \sum{i=1}^j r_{ij} \mathbf{u}_i $$

其中:

$$ r_{ij} = \mathbf{w}j \cdot \mathbf{u}i, \quad i < j $$ $$ r{jj} = | \mathbf{w}j | - \sum{i=1}^{j-1} r{ij}^2 $$

4.3. 正交矩阵

如果一个矩阵的列向量构成了 $ \mathbf{R}^n $ 的标准正交基,则称该矩阵为正交矩阵。正交矩阵 $ Q $ 满足:

$$ Q^T Q = Q Q^T = I $$

因此,其逆矩阵为:

$$ Q^{-1} = Q^T $$

4.4. QR 分解算法

QR 分解可以将矩阵 $ A $ 分解为一个正交矩阵 $ Q $ 和一个上三角矩阵 $ R $:

$$ A = Q R $$

实现 QR 分解的伪代码如下:

algorithm QRFact(A):
    // INPUT
    //    A = 非奇异方阵
    // OUTPUT
    //    Q = 正交矩阵
    //    R = 上三角矩阵

    N = A 的维度
    u = 零矩阵
    R = 零矩阵

    for j from 0 to N-1:
        w_j = A[:, j]

        // 计算 w_j 的模平方
        norm2 = sum(w_j[k]^2 for k in 0..N-1)

        // 计算 R[i][j]
        sum_r = 0
        for i from 0 to j-1:
            R[i][j] = dot(w_j, u[i])
            sum_r += R[i][j]^2
        R[j][j] = sqrt(norm2 - sum_r)

        // 计算 u[j]
        sum_vec = 0
        for i from 0 to j-1:
            sum_vec += R[i][j] * u[i]
        u[j] = (w_j - sum_vec) / R[j][j]

    Q = transpose(u)
    return Q, R

4.5. 使用 QR 分解求解线性方程组

一旦我们有了 QR 分解,就可以高效地求解线性方程组:

$$ A\mathbf{x} = \mathbf{b} \Rightarrow QR\mathbf{x} = \mathbf{b} \Rightarrow R\mathbf{x} = Q^T\mathbf{b} $$

由于 $ R $ 是上三角矩阵,所以可以使用回代法快速求解。

5. 总结

本文回顾了高斯消去法这一中学阶段就接触的解线性方程组的方法,并介绍了 LU 和 QR 分解等更高效的矩阵分解技术。这些分解方法是更复杂算法的基础。

如今,像 MATLAB、Eigen、LAPACK、Linpack、NAG 等数值计算库都已广泛支持 $ A = LU $、$ A = QR $ 等分解方法,用于求解高达 $ 10^6 \times 10^6 $ 的大型线性系统。


原始标题:Fast Algorithms for Solving a System of Linear Equations