Julia 教程 / Julia 线性代数完全指南
Julia 线性代数完全指南
线性代数是科学计算的基石。Julia 内置了强大的线性代数支持,底层直接调用 BLAS/LAPACK,性能媲美 C/Fortran。
1. LinearAlgebra 模块
using LinearAlgebra
# 单位矩阵
I(3) # 3×3 单位矩阵
Matrix{Float64}(I, 3, 3)
# 零矩阵与对角矩阵
zeros(3, 3)
Diagonal([1.0, 2.0, 3.0])
# 随机矩阵
A = rand(4, 4)
| 函数 | 说明 |
|---|---|
I(n) | n×n 单位矩阵 |
Diagonal(v) | 以向量 v 为对角的对角矩阵 |
Tridiagonal(dl, d, du) | 三对角矩阵 |
Symmetric(A) | 对称矩阵视图 |
Hermitian(A) | Hermite 矩阵视图 |
UpperTriangular(A) | 上三角视图 |
LowerTriangular(A) | 下三角视图 |
2. 矩阵乘法与基本运算
A = rand(3, 4)
B = rand(4, 5)
# 矩阵乘法
C = A * B # 3×5 矩阵
# 逐元素运算
A .+ 1.0 # 每个元素加 1
A .* A # 逐元素乘
A .^ 2 # 逐元素平方
# 向量内积与外积
x = rand(3)
y = rand(3)
dot(x, y) # 内积(等价于 x' * y)
x * y' # 外积,3×3 矩阵
⚠️ 注意: Julia 中 * 运算符对矩阵执行的是线性代数中的矩阵乘法,而非逐元素乘法。逐元素乘法需要使用 .*。
3. 转置与共轭转置
A = rand(ComplexF64, 3, 3)
# 转置(不取共轭)
transpose(A) # Aᵀ
# 共轭转置(Hermite 转置)
adjoint(A) # Aᴴ = conj(Aᵀ)
A' # 简写,等价于 adjoint(A)
# 实数矩阵两者等价
B = rand(3, 3)
transpose(B) == adjoint(B) # true
| 操作 | 函数 | 符号 |
|---|---|---|
| 转置 | transpose(A) | permutedims(A) |
| 共轭转置 | adjoint(A) | A' |
| 共轭 | conj(A) | — |
💡 提示: adjoint 和 transpose 返回的是惰性视图,不会立即复制数据。只有在参与运算时才真正计算,这对性能非常重要。
4. 矩阵分解
4.1 LU 分解
A = rand(5, 5)
F = lu(A)
# 获取 L 和 U
F.L # 下三角
F.U # 上三角
F.P # 置换矩阵
# 用 LU 分解解方程
b = rand(5)
x = F \ b # 等价于 A \ b
4.2 QR 分解
A = rand(5, 3)
F = qr(A)
F.Q # 正交矩阵
F.R # 上三角矩阵
# QR 分解常用于最小二乘
b = rand(5)
x = F \ b # 最小二乘解
4.3 SVD 分解(奇异值分解)
A = rand(5, 3)
F = svd(A)
F.U # 左奇异向量
F.S # 奇异值(向量)
F.V # 右奇异向量
F.Vt # 右奇异向量的转置
# 低秩近似
k = 2
A_approx = F.U[:, 1:k] * Diagonal(F.S[1:k]) * F.Vt[1:k, :]
4.4 Cholesky 分解
# 适用于正定矩阵
A = rand(3, 3)
A = A * A' + I # 确保正定
F = cholesky(A)
F.L # 下三角,A = L * Lᵀ
F.U # 上三角
4.5 特征值分解
A = rand(5, 5)
A = A + A' # 对称化
# 特征值与特征向量
F = eigen(A)
F.values # 特征值
F.vectors # 特征向量(按列排列)
# 只求部分特征值
eigs = eigen(A, 1:3) # 前 3 个
| 分解 | 函数 | 适用场景 |
|---|---|---|
| LU | lu(A) | 解线性方程组 |
| QR | qr(A) | 最小二乘、正交化 |
| SVD | svd(A) | 低秩近似、PCA |
| Cholesky | cholesky(A) | 正定矩阵、协方差矩阵 |
| Schur | schur(A) | 特征值计算、稳定性分析 |
| Eigen | eigen(A) | 特征值问题 |
| Hessenberg | hessenberg(A) | 特征值的中间步骤 |
5. 解线性方程组
# 最基本的方式:A \ b
A = rand(5, 5)
b = rand(5)
x = A \ b # 自动选择最优分解
# 验证
norm(A * x - b) # 应接近机器精度 ≈ 1e-16
# 多右端项
B = rand(5, 3)
X = A \ B # 同时解 3 个方程组
# 带特殊结构的矩阵(Julia 自动利用结构)
Symmetric(A) \ b # 对称矩阵
Hermitian(A) \ b # Hermite 矩阵
UpperTriangular(A) \ b # 上三角
⚠️ 注意: A \ b 会根据 A 的类型自动选择最优算法。对稀疏矩阵使用稀疏求解器,对带状矩阵使用带状求解器,无需手动指定。
6. 稀疏矩阵(SparseArrays)
using SparseArrays
# 创建稀疏矩阵
# 方法1:从坐标列表创建
I = [1, 2, 3, 3]
J = [2, 1, 3, 1]
V = [1.0, 2.0, 3.0, 4.0]
S = sparse(I, J, V, 3, 3)
# 方法2:从稠密矩阵转换
A = rand(1000, 1000)
A[abs.(A) .< 0.9] .= 0 # 设置大部分元素为 0
S = sparse(A)
# 方法3:使用 spzeros 构建
S = spzeros(100, 100)
S[1, 5] = 1.0
S[50, 50] = 3.14
# 稀疏矩阵操作
nnz(S) # 非零元素数量
dropzeros(S) # 去除存储的零元素
sparse(I(100)) # 稀疏单位矩阵
# 稀疏线性方程组
S = sprand(1000, 1000, 0.01) + 10I # 1% 非零率
b = rand(1000)
x = S \ b # 使用 UMFPACK 稀疏求解器
| 操作 | 稠密矩阵 | 稀疏矩阵 |
|---|---|---|
| 存储 (1000×1000, 1% 非零) | 8 MB | ≈ 0.1 MB |
| 矩阵-向量乘 | O(n²) | O(nnz) |
| 方程组求解 | O(n³) | 远低于 O(n³) |
💡 提示: 当矩阵非零元素比例低于约 30% 时,稀疏矩阵在存储和计算上都有优势。
7. BLAS/LAPACK 底层调用
using LinearAlgebra.BLAS
# Julia 默认使用 OpenBLAS 或 MKL
# 查看当前 BLAS 后端
BLAS.get_config()
# 直接调用 BLAS 例程
n = 1000
A = rand(n, n)
B = rand(n, n)
C = zeros(n, n)
# DGEMM: C = α*A*B + β*C
BLAS.gemm!('N', 'N', 1.0, A, B, 0.0, C)
# 设置 BLAS 线程数
BLAS.set_num_threads(4)
# 查看 LAPACK 版本
using LinearAlgebra.LAPACK
# 通常通过 Julia 内部自动调用
# 手动调用 LAPACK 例程(高级用法)
A = rand(5, 5)
b = rand(5)
# 等价于 A \ b,但手动控制
A_copy = copy(A)
LU, ipiv, info = LAPACK.getrf!(A_copy) # LU 分解
x = LAPACK.getrs!('N', LU, ipiv, copy(b)) # 解方程
⚠️ 注意: 一般情况下不需要直接调用 BLAS/LAPACK,Julia 的高级接口已经自动利用底层库。仅在需要极致性能调优时才考虑直接调用。
8. 矩阵函数
A = rand(4, 4)
A = A + A' # 对称化
# 矩阵指数
exp(A) # eᴬ
# 矩阵平方根
sqrt(A) # A^(1/2)
# 矩阵对数
log(A) # ln(A)
# 矩阵幂
A^3 # A * A * A
# 条件数
cond(A) # 条件数,衡量矩阵"病态"程度
# 秩
rank(A) # 矩阵的秩
# 行列式
det(A) # 行列式
# 迹
tr(A) # 主对角线元素之和
| 函数 | 说明 | 数学含义 |
|---|---|---|
exp(A) | 矩阵指数 | e^A = Σ Aⁿ/n! |
log(A) | 矩阵对数 | exp 的逆运算 |
sqrt(A) | 矩阵平方根 | B² = A |
sin(A) | 矩阵正弦 | 泰勒展开 |
det(A) | 行列式 | 体积缩放因子 |
tr(A) | 迹 | 对角线和 |
rank(A) | 秩 | 线性无关行列数 |
cond(A) | 条件数 | ‖A‖·‖A⁻¹‖ |
9. 性能注意事项
# ❌ 避免:循环中按列访问(Julia 是列主序)
A = rand(1000, 1000)
function sum_rows_bad(A)
s = 0.0
for i in 1:size(A, 1)
for j in 1:size(A, 2)
s += A[i, j] # 按行遍历,缓存不友好
end
end
return s
end
# ✅ 推荐:按列遍历
function sum_cols_good(A)
s = 0.0
for j in 1:size(A, 2)
for i in 1:size(A, 1)
s += A[i, j] # 按列遍历,缓存友好
end
end
return s
end
# ✅ 最佳:使用内置函数
sum(A)
# 预分配输出避免重复分配
function mul_add_bad!(A, B, n)
for _ in 1:n
C = A * B # 每次都分配新矩阵
# 使用 C...
end
end
function mul_add_good!(C, A, B, n)
for _ in 1:n
mul!(C, A, B) # 写入预分配的 C,零分配
# 使用 C...
end
end
💡 提示: 使用 @btime(来自 BenchmarkTools.jl)来精确测量性能:
using BenchmarkTools
@btime $A * $b
@btime mul!($C, $A, $b)
10. 实际应用
10.1 最小二乘拟合
# 用线性回归拟合 y = ax + b
using LinearAlgebra
# 生成数据
x_data = collect(1.0:10.0)
y_data = 2.5 .* x_data .+ 1.3 .+ randn(10) .* 0.5
# 构建设计矩阵 [x, 1]
X = hcat(x_data, ones(10))
# 最小二乘解:β = (XᵀX)⁻¹ Xᵀy = X \ y
β = X \ y_data
println("斜率: $(β[1]), 截距: $(β[2])")
# 预测
y_pred = X * β
residuals = y_data - y_pred
mse = sum(residuals .^ 2) / length(y_data)
10.2 主成分分析(PCA)
using LinearAlgebra
# 数据矩阵(每行一个样本)
X = rand(100, 5) # 100 个样本,5 个特征
# 中心化
X_centered = X .- mean(X, dims=1)
# 协方差矩阵
C = X_centered' * X_centered / (size(X, 1) - 1)
# 特征值分解
F = eigen(Symmetric(C))
# 按特征值从大到小排序
idx = sortperm(F.values, rev=true)
eigenvalues = F.values[idx]
eigenvectors = F.vectors[:, idx]
# 前 2 个主成分
k = 2
W = eigenvectors[:, 1:k]
X_pca = X_centered * W
# 方差解释比
explained_ratio = eigenvalues ./ sum(eigenvalues)
println("前 $k 个主成分解释了 $(sum(explained_ratio[1:k])*100)% 的方差")
10.3 协方差矩阵与多元正态
using LinearAlgebra
# 协方差矩阵必须是对称正定的
Σ = [4.0 2.0 0.5;
2.0 3.0 1.0;
0.5 1.0 2.0]
# 验证正定
isposdef(Σ) # true
# Cholesky 分解用于生成多元正态样本
L = cholesky(Σ).L
samples = L * randn(3, 1000) # 1000 个样本
11. 常见陷阱与排错
| 错误 | 原因 | 解决方案 |
|---|---|---|
SingularException | 矩阵不可逆 | 检查矩阵是否奇异,使用伪逆 pinv |
PosDefException | Cholesky 要求正定 | 确保矩阵对称正定,加小扰动 A + 1e-10I |
| 内存溢出 | 稠密矩阵太大 | 使用 sparse 稀疏存储 |
| 结果不精确 | 病态矩阵 | 检查 cond(A),考虑正则化 |
# 病态矩阵示例
A = [1.0 1.0; 1.0 1.0000001]
cond(A) # 非常大的条件数
b = [2.0, 2.0000001]
x = A \ b # 结果可能不准确
# 使用伪逆
x_pinv = pinv(A) * b
扩展阅读
- Julia LinearAlgebra 官方文档
- SparseArrays 文档
- BLAS/LAPACK 接口参考
- Gilbert Strang, Introduction to Linear Algebra
- Trefethen & Bau, Numerical Linear Algebra
- Julia Discourse - Performance Tips for Linear Algebra