Skip to content

第 10 章 逆矩阵

学习目标

  • 理解逆矩阵的定义与几何意义(逆变换)
  • 推导二阶逆矩阵公式,记住可逆条件
  • 掌握高斯-约当消元求逆的原理与步骤
  • 会用逆矩阵解线性方程组
  • 会用 NumPy 与 PyTorch 求逆矩阵,并能识别奇异矩阵

10.1 从数的倒数到矩阵的逆

数的世界里,每个非零数 都有倒数 ,满足

矩阵的世界里,扮演「1」这个角色的,是单位矩阵 (第 7 章验证过:)。

逆矩阵:若 阶方阵,存在 阶方阵 满足

则称 可逆, 的逆矩阵。不存在逆矩阵的方阵叫奇异矩阵

几何意义:矩阵是变换,逆矩阵就是逆变换。旋转 90° 的矩阵,逆矩阵是旋转 ;拉伸 2 倍的矩阵,逆矩阵是压缩到一半。先变换再逆变换,等于什么都没做():

逆变换

10.2 二阶逆矩阵公式:一步一步推导

python
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
    if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
        font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False

import numpy as np

A = np.array([[1.0, 0.5],
              [0.0, 1.0]])   # 剪切变换
square = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]])

fig, axes = plt.subplots(1, 3, figsize=(14, 4.5))

def draw(ax, pts, title, color):
    ax.plot(pts[:, 0], pts[:, 1], color=color, linewidth=2)
    ax.fill(pts[:, 0], pts[:, 1], color=color, alpha=0.15)
    ax.axhline(0, color="black", linewidth=1)
    ax.axvline(0, color="black", linewidth=1)
    ax.set_xlim(-1, 2.6)
    ax.set_ylim(-0.5, 2.2)
    ax.set_aspect("equal")
    ax.grid(True, linestyle=":", alpha=0.5)
    ax.set_title(title)

draw(axes[0], square, "原图", "tab:blue")
draw(axes[1], square @ A.T, "A 变换后", "tab:red")
draw(axes[2], square @ A.T @ np.linalg.inv(A).T, "A⁻¹ 还原", "tab:green")

fig.savefig("book/public/figs/fig10-inverse.png", dpi=120, bbox_inches="tight")
plt.close(fig)

假设逆矩阵存在,设 。条件 写成 4 个方程:

逐元素展开,先看第一列:

由第二个方程 ( 时),代入第一个方程:

于是 。再看第二列,同理得到:

把结果收拢:

其中 正是 。所以:

口诀:主对角线互换(),副对角线变号( 加负号),整体除以行列式。行列式为 0 时公式无意义——这就是「 时不可逆」的代数来源。

验证:,,所以

python
import numpy as np

A = np.array([[1.0, 2.0],
              [3.0, 4.0]])
det = A[0, 0] * A[1, 1] - A[0, 1] * A[1, 0]
inv = (1 / det) * np.array([[A[1, 1], -A[0, 1]],
                            [-A[1, 0], A[0, 0]]])
print("手推公式:", inv)
print("np.linalg.inv:", np.linalg.inv(A))
print("A @ inv = I?", np.allclose(A @ inv, np.eye(2)))

运行输出:

text
手推公式: [[-2.   1. ]
 [ 1.5 -0.5]]
np.linalg.inv: [[-2.   1. ]
 [ 1.5 -0.5]]
A @ inv = I? True

10.3 高斯-约当消元:对任意阶矩阵求逆

公式只适合二阶(三阶的伴随公式极其繁琐)。通用办法是高斯-约当消元:把 和单位矩阵并排写在一起,

然后用行初等变换,把左边变成单位矩阵——右边同时变成的就是 :

为什么有效?每做一次行初等变换,都相当于给整个矩阵(左右两边)左乘一个初等矩阵 。假设一共做了 次,左边变成:

那么乘积 就是 (因为 ),而它恰好作用在了右边的 上:

用代码实现,并顺手解决第 8 章留的「主元为 0 换行」问题:

python
import numpy as np

def invert_gauss_jordan(A):
    """用高斯-约当消元求方阵的逆;不可逆时抛出异常"""
    n = A.shape[0]
    M = np.array(A, dtype=float)
    I = np.eye(n)

    for col in range(n):
        pivot = col + np.argmax(np.abs(M[col:, col]))
        if M[pivot, col] == 0:
            raise np.linalg.LinAlgError("矩阵不可逆")
        M[[col, pivot]] = M[[pivot, col]]
        I[[col, pivot]] = I[[pivot, col]]

        # 把主元化为 1
        factor = M[col, col]
        M[col] /= factor
        I[col] /= factor

        # 消去其他行的这一列
        for row in range(n):
            if row != col:
                f = M[row, col]
                M[row] -= f * M[col]
                I[row] -= f * I[col]
    return I

A = np.array([[2.0, 1.0, 0.0],
              [1.0, 3.0, 1.0],
              [0.0, 1.0, 2.0]])

inv = invert_gauss_jordan(A)
print("高斯-约当求逆:\n", inv)
print("与库一致?", np.allclose(inv, np.linalg.inv(A)))
print("A @ A⁻¹ = I?", np.allclose(A @ inv, np.eye(3)))

运行输出:

text
高斯-约当求逆:
 [[ 0.625 -0.25   0.125]
 [-0.25   0.5   -0.25 ]
 [ 0.125 -0.25   0.625]]
与库一致? True
A @ A⁻¹ = I? True

10.4 用逆矩阵解方程组

方程 两边同时左乘 :

鸡兔同笼的矩阵形式 :

python
import numpy as np

A = np.array([[1.0, 1.0],
              [2.0, 4.0]])
b = np.array([35.0, 94.0])

x = np.linalg.inv(A) @ b
print("用逆矩阵解:", x)
print("直接 solve:", np.linalg.solve(A, b))

运行输出:

text
用逆矩阵解: [23. 12.]
直接 solve: [23. 12.]

实践上,解方程组通常直接用高斯消元(np.linalg.solve)而不是先求逆——求逆本身就要解 组方程组,更慢、误差更大。但「 的解是 」这个关系是理论基石,务必理解。

10.5 不可逆的情况

奇异矩阵(行列式为 0)求逆会报错:

python
# 预期错误:LinAlgError
import numpy as np

A = np.array([[1.0, 2.0],
              [2.0, 4.0]])   # 两行成比例,det = 0
print(np.linalg.inv(A))

报错信息是 LinAlgError: Singular matrix——矩阵是奇异的,没有逆。这和第 9 章的等价链完全一致: ⟺ 列相关 ⟺ 变换压扁空间 ⟺ 不可逆。

PyTorch 求逆:

python
import torch

A = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0]])
inv = torch.linalg.inv(A)
print("torch A⁻¹:\n", inv)
print("验证 A @ A⁻¹:\n", A @ inv)

运行输出:

text
torch A⁻¹:
 tensor([[-2.0000,  1.0000],
        [ 1.5000, -0.5000]])
验证 A @ A⁻¹:
 tensor([[ 1.0000e+00,  0.0000e+00],
        [-4.7684e-07,  1.0000e+00]])

验证结果里出现 这样的极小值,是浮点舍入误差;它约等于 0,说明 确实是单位矩阵。判断是否为单位矩阵时用 torch.allclose(A @ inv, torch.eye(2)) 更可靠。

动手实践

  1. 用公式求 的逆,验证 ,再用 np.linalg.inv 对比。
  2. 判断 是否可逆,说明理由。
  3. 用 10.3 的 invert_gauss_jordan 求一个 3×3 对角矩阵的逆,观察结果有什么规律。
  4. 用逆矩阵解 ,写出 的每一步。
  5. np.linalg.inv 一个奇异矩阵,记录并解读报错信息。

常见错误

错误写法/理解原因
把逆矩阵写成「每个元素取倒数」逆矩阵是「矩阵整体求逆」,不是逐元素求倒数; 这类对角矩阵才恰好是逐元素倒数
忘了除以 公式里 必不可少,它是整个矩阵的缩放因子
认为所有方阵都有逆只有 的方阵可逆;奇异矩阵没有逆
写成 矩阵乘法不交换,必须左乘:
混淆「求逆」与「转置」 是逆矩阵, 是转置,一般不同

章末练习

基础

  1. 用公式求下列矩阵的逆,并验证 :
  2. 判断下列矩阵是否可逆:
  3. np.linalg.inv 验证第 1 题的结果。

提高

  1. 用逆矩阵解方程组写出 与最终解的完整步骤。
  2. 证明:若 可逆,则 ;若 都可逆,则 。提示:后者验证 ,注意交换顺序。

挑战

  1. 二阶矩阵 的逆公式,用高斯-约当消元重新推导一遍,并与 10.2 的结论对照。
  2. 可逆且 ,证明解唯一:假设还有 满足方程,推出

章末自测

  1. 的逆是?
    • A.
    • B.
    • C.
    • D. 不存在
  2. 矩阵 可逆的充要条件是?
    • A.
    • B.
    • C. 是对称矩阵
    • D. 的元素都非零
    • A.
    • B.
    • C.
    • D.
  3. 奇异矩阵是?
    • A. 行列式为 0 的矩阵
    • B. 有逆的矩阵
    • C. 单位矩阵
    • D. 对角矩阵
  4. 用逆矩阵解 ,正确的是?
    • A.
    • B.
    • C.
    • D.
  5. 高斯-约当求逆中,竖线右边最初放的是?
    • A.
    • B.
    • C.
    • D. 零矩阵
  6. ,则 ?
    • A. 不可逆
    • B. 可逆
    • C. 是零矩阵
    • D. 是单位矩阵
  7. 下列哪个矩阵不可逆?
    • A.
    • B.
    • C.
    • D.
  8. (A、B 可逆)
    • A.
    • B.
    • C.
    • D.
  9. 实际解大型方程组时,推荐的做法是?
    • A. 先求逆再乘
    • B. 直接高斯消元(np.linalg.solve)
    • C. 逐元素取倒数
    • D. 转置再求逆