NumPy 线性代数运算

NumPy提供了强大的线性代数功能,通过 np.linalg 模块可以执行矩阵乘法、求逆、分解、特征值计算等常见操作。这些函数底层调用了优化的LAPACK和BLAS库,性能优异。

1. 矩阵乘法

在NumPy中,有多种方式执行矩阵乘法(而非逐元素乘法):

  • np.dot(A, B):对于二维数组,执行矩阵乘法;对于一维数组,执行点积。
  • A @ B:Python 3.5+ 引入的矩阵乘法运算符,推荐使用。
  • np.matmul(A, B):与 @ 等价,支持广播。
import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

print("A @ B:\n", A @ B)
print("np.dot(A, B):\n", np.dot(A, B))
print("np.matmul(A, B):\n", np.matmul(A, B))

# 一维数组点积
x = np.array([1, 2])
y = np.array([3, 4])
print("x @ y =", x @ y)   # 1*3 + 2*4 = 11

输出:

A @ B:
 [[19 22]
 [43 50]]
np.dot(A, B):
 [[19 22]
 [43 50]]
np.matmul(A, B):
 [[19 22]
 [43 50]]
x @ y = 11
注意: * 是逐元素乘法,不是矩阵乘法,注意区分。

2. 逆矩阵

使用 np.linalg.inv 计算方阵的逆矩阵。如果矩阵不可逆(奇异),会抛出 LinAlgError

A = np.array([[1, 2], [3, 4]])
A_inv = np.linalg.inv(A)
print("A 的逆矩阵:\n", A_inv)

# 验证 A @ A_inv 是否为单位矩阵
print("A @ A_inv:\n", A @ A_inv)   # 近似单位矩阵

输出:

A 的逆矩阵:
 [[-2.   1. ]
 [ 1.5 -0.5]]
A @ A_inv:
 [[1.00000000e+00 0.00000000e+00]
 [8.88178420e-16 1.00000000e+00]]

3. 行列式

np.linalg.det 计算方阵的行列式。

A = np.array([[1, 2], [3, 4]])
det = np.linalg.det(A)
print("行列式:", det)   # 1*4 - 2*3 = -2

输出:

行列式: -2.0

4. 矩阵的秩

np.linalg.matrix_rank 返回矩阵的秩(线性无关的行或列的最大数目)。注意该函数在 np.linalg 中,不是默认的 rank(后者是数组的维度数)。

A = np.array([[1, 2], [2, 4]])   # 第二行是第一行的两倍,秩为1
print("矩阵的秩:", np.linalg.matrix_rank(A))

输出:

矩阵的秩: 1

5. 解线性方程组

对于方程组 Ax = b,可以使用 np.linalg.solve 求解(要求 A 为方阵且可逆)。

A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
x = np.linalg.solve(A, b)
print("解 x =", x)

# 验证
print("Ax =", A @ x)

输出:

解 x = [2. 3.]
Ax = [9. 8.]

对于超定或欠定方程组,可使用 np.linalg.lstsq(最小二乘解)。

6. 特征值和特征向量

np.linalg.eig 计算方阵的特征值和特征向量。返回一个元组 (特征值, 特征向量),其中特征向量按列排列(即 v[:, i] 是第 i 个特征值的特征向量)。

A = np.array([[1, 2], [2, 1]])
eigenvalues, eigenvectors = np.linalg.eig(A)
print("特征值:", eigenvalues)
print("特征向量:\n", eigenvectors)

# 验证 Av = λv
for i in range(2):
    print(f"A @ v{i} =", A @ eigenvectors[:, i])
    print(f"λ{i} * v{i} =", eigenvalues[i] * eigenvectors[:, i])

输出:

特征值: [ 3. -1.]
特征向量:
 [[ 0.70710678 -0.70710678]
 [ 0.70710678  0.70710678]]
A @ v0 = [2.12132034 2.12132034]
λ0 * v0 = [2.12132034 2.12132034]
A @ v1 = [ 0.70710678 -0.70710678]
λ1 * v1 = [ 0.70710678 -0.70710678]

对于对称矩阵,还可使用 np.linalg.eigh(更快、更稳定)。

7. 奇异值分解 (SVD)

SVD将矩阵分解为 U @ S @ Vh,其中 S 是奇异值的对角阵(以一维数组返回)。使用 np.linalg.svd

A = np.array([[1, 2], [3, 4], [5, 6]])   # 3x2 矩阵
U, S, Vh = np.linalg.svd(A, full_matrices=False)
print("U shape:", U.shape)
print("奇异值:", S)
print("Vh shape:", Vh.shape)

# 重构
Sigma = np.diag(S)
A_reconstructed = U @ Sigma @ Vh
print("重构误差:", np.linalg.norm(A - A_reconstructed))

输出:

U shape: (3, 2)
奇异值: [9.52551809 0.51430058]
Vh shape: (2, 2)
重构误差: 1.0658141036401503e-15

8. 矩阵范数

np.linalg.norm 可以计算向量或矩阵的各种范数。通过 ord 参数指定范数类型。

A = np.array([[1, 2], [3, 4]])
print("Frobenius 范数:", np.linalg.norm(A))               # 默认 Frobenius 范数
print("L2 范数(最大奇异值):", np.linalg.norm(A, ord=2)) # 2-范数(最大奇异值)
print("L1 范数(列和的最大值):", np.linalg.norm(A, ord=1))
print("无穷范数(行和的最大值):", np.linalg.norm(A, ord=np.inf))

v = np.array([1, 2, 3])
print("向量 L2 范数:", np.linalg.norm(v))
print("向量 L1 范数:", np.linalg.norm(v, ord=1))

输出:

Frobenius 范数: 5.477225575051661
L2 范数(最大奇异值): 5.464985704219043
L1 范数(列和的最大值): 6.0
无穷范数(行和的最大值): 7.0
向量 L2 范数: 3.7416573867739413
向量 L1 范数: 6.0

9. 其他常用线性代数函数

  • np.linalg.qr:QR分解
  • np.linalg.cholesky:Cholesky分解(要求矩阵对称正定)
  • np.linalg.eigvals:仅计算特征值(更省内存)
  • np.linalg.matrix_power:矩阵的幂
  • np.trace:矩阵的迹(对角线元素之和,虽然不是linalg模块,但常用)
# 矩阵幂
A = np.array([[1, 2], [3, 4]])
A_pow3 = np.linalg.matrix_power(A, 3)
print("A^3:\n", A_pow3)

# 迹
print("trace:", np.trace(A))

输出:

A^3:
 [[ 37  54]
 [ 81 118]]
trace: 5
提示: np.linalg 中的函数通常要求输入是浮点类型,如果输入是整数,可能会自动转换为浮点。对于大型矩阵,注意内存和性能。

总结

NumPy的线性代数模块提供了丰富且高效的函数,涵盖了矩阵运算、分解、求解等核心需求。熟练掌握这些函数,可以极大地简化科学计算和数据分析中的线性代数相关任务。下一章我们将学习NumPy的随机数生成功能。