3. 线性代数基础与稠密矩阵运算

在 SciPy 的科学计算体系中,scipy.linalg 是专门面向稠密矩阵的线性代数模块。它建立在 LAPACK 与 BLAS 之上,提供比 numpy.linalg 更完整、更可控的接口:既包含矩阵分解、线性方程组求解、行列式与特征值计算等基础能力,也提供分解结果的显式返回、覆盖写选项、带宽/三角结构识别等工程化参数。理解它的组织方式,比记住单个函数名更重要。

本节围绕四个核心任务展开:矩阵分解(LU、QR、Cholesky)、求解线性方程组、计算行列式、计算特征值。它们并非彼此孤立——分解是求解与特征值计算的基础,而矩阵结构(对称正定、三角、带状)决定了应选择哪种分解与求解路径。

模块定位与常用函数地图

scipy.linalg 的函数大致可分为几类:分解类(lu、lu_factor、qr、cholesky、svd)、求解类(solve、solve_triangular、lstsq)、谱分析类(eig、eigh、eigvalsh)、以及行列式与范数等辅助函数(det、norm、cond)。与 numpy.linalg 相比,scipy.linalg 默认总是返回一个新数组,且提供 overwrite_a、check_finite 等参数,便于在性能敏感场景中控制内存与校验开销。

scipy.linalg 稠密矩阵运算关系图 输入矩阵 A 稠密 n×n / m×n 判断矩阵结构 对称正定?三角?带状? 一般方阵?超定? 矩阵分解(核心) LU 分解 A = P·L·U 一般方阵 QR 分解 A = Q·R 任意 m×n Cholesky A = L·Lᵀ 对称正定 SVD A = U·Σ·Vᵀ 任意矩阵 求解线性方程组 A·x = b solve / lu_solve solve_triangular lstsq(超定) 复用分解可多次求解 行列式与条件数 det(A) det / slogdet cond(条件数) norm(范数) 判断奇异与病态 特征值问题 A·v = λ·v eig / eigvals eigh / eigvalsh (对称/厄米特专用) 广义特征值 eig(A,B) 分解是求解、行列式与特征值计算的共同基础;矩阵结构决定分解方式的选择

矩阵分解:LU、QR 与 Cholesky

LU 分解把一般方阵写成 A = P·L·U,其中 P 是置换矩阵(记录行交换),L 是单位下三角矩阵,U 是上三角矩阵。SciPy 提供两种接口:lu(A) 直接返回 P、L、U 三个矩阵,适合教学与需要显式矩阵的场合;lu_factor(A) 返回紧凑的 LU 数组与 pivot 索引,配合 lu_solve 使用,内存更省、速度更快。示例:

import numpy as np
from scipy import linalg

A = np.array([[4.0, 3.0, 2.0],
              [2.0, 5.0, 1.0],
              [1.0, 2.0, 6.0]])
P, L, U = linalg.lu(A)
print(np.allclose(P @ L @ U, A))   # True,验证分解正确性

QR 分解把矩阵写成 A = Q·R,Q 的列正交(QᵀQ = I),R 为上三角。它对任意 m×n 矩阵都适用,是求解最小二乘问题的标准工具。linalg.qr(A, mode='economic') 只返回经济型 Q(m×n)与 R(n×n),在 m 远大于 n 时能显著节省内存。

Cholesky 分解针对对称正定矩阵,写成 A = L·Lᵀ(L 为下三角)。它的计算量约为 LU 的一半,且无需选主元,数值稳定性好。若矩阵并非正定,linalg.cholesky 会抛出 LinAlgError,这本身就是一个有用的结构检验手段。实际使用中可先尝试 Cholesky,失败再退回 LU 或 QR。

求解线性方程组

求解 A·x = b 的首选是 linalg.solve(A, b),它内部根据矩阵结构自动选择分解方式。当需要反复用同一个 A 求解不同 b 时,应先做一次分解再复用:

lu, piv = linalg.lu_factor(A)
x1 = linalg.lu_solve((lu, piv), b1)
x2 = linalg.lu_solve((lu, piv), b2)   # 复用分解,避免重复 O(n³) 开销

若已知矩阵是三角阵,应使用 solve_triangular 并指定 lower=True/False,复杂度降为 O(n²)。对于超定方程组(方程数多于未知数),solve 不适用,应改用 lstsq 求最小二乘解,它基于 SVD 或 QR,能同时返回残差、秩与奇异值。判断结果是否可信,可结合 cond(A):条件数极大时,即使残差很小,解也可能严重偏离真值。

行列式与特征值

linalg.det(A) 直接返回行列式,但数值上容易上溢或下溢。更稳健的做法是 slogdet(A),它返回符号与对数行列式,即 det(A) = sign · exp(logabsdet)。行列式接近零提示矩阵可能奇异,但更可靠的奇异/病态判断应看条件数或秩。

特征值计算分两条路径:一般矩阵用 eig(A),返回特征值与(右)特征向量,结果为复数类型;对称或厄米特矩阵务必用 eigh(A),它利用结构保证特征值为实数、特征向量正交,且数值更稳定、速度更快。只关心特征值不关心向量时,用 eigvals 或 eigvalsh 可省去向量计算。广义特征值问题 A·v = λ·B·v 用 eig(A, B) 求解。

选择分解与求解路径的实用原则:先判断矩阵是否对称正定(可用 np.allclose(A, A.T) 与 Cholesky 试探),再判断是否为三角或带状结构,最后才退回通用 LU/QR。结构信息利用得越充分,计算越快、结果越稳。
常见误区:对对称矩阵使用 eig 而非 eigh,会得到带微小虚部的复数结果并损失一半性能;用 det 判断奇异性在数值上不可靠,应改用 cond 或 matrix_rank;对超定系统直接调用 solve 会报错,应使用 lstsq。

把上述能力串起来,一个典型的工程流程是:读入系数矩阵 → 检查对称性与条件数 → 选择分解(Cholesky/LU/QR)→ 复用分解求解多个右端项 → 用残差 norm(A @ x - b) 与条件数共同判断结果质量。掌握这条主线,scipy.linalg 的众多函数就不再是零散 API,而是一套围绕矩阵结构组织起来的计算体系。