3. 对称正定矩阵Cholesky分解与LDL^T分解对比

问题设定与两种分解的数学结构

取一个 4×4 对称正定矩阵作为教学示例,其元素由可复现的构造方式给出:先取一个对角占优的下三角矩阵 B,再令 A = B·Bᵀ,这样得到的 A 必然对称正定,且分解结果可以精确核对。

B = [[2, 0, 0, 0],
     [1, 3, 0, 0],
     [1, 1, 2, 0],
     [1, 1, 1, 2]]

A = B·Bᵀ =
[[ 4,  2,  2,  2],
 [ 2, 10,  4,  4],
 [ 2,  4,  6,  5],
 [ 2,  4,  5,  7]]

两种分解都要求 A 对称正定,但形式不同:

  • Cholesky 分解:A = L·Lᵀ,L 为对角元全正的下三角矩阵,共 n(n+1)/2 个未知量。
  • LDLᵀ 分解:A = L·D·Lᵀ,L 为单位下三角(对角元为 1),D 为对角矩阵,未知量同样是 n(n+1)/2 个,但把对角信息单独抽到 D 中。

两者本质上是同一分解的不同参数化:若 Cholesky 得到 L_c,则 D = diag(L_c)²,L = L_c·diag(1/L_c(i,i))。因此它们的存在条件完全一致(对称正定),差别体现在计算量、是否需要开方、以及病态矩阵上的舍入行为。

分解算法与计算量

Cholesky 按列递推,第 j 列先算对角元(含开方),再算该列下方元素:

for j = 1..n:
    L[j][j] = sqrt(A[j][j] - Σ_{k<j} L[j][k]²)
    for i = j+1..n:
        L[i][j] = (A[i][j] - Σ_{k<j} L[i][k]·L[j][k]) / L[j][j]

LDLᵀ 把开方推迟到最后,先算 D[j],再算单位下三角列:

for j = 1..n:
    D[j] = A[j][j] - Σ_{k<j} L[j][k]²·D[k]
    for i = j+1..n:
        L[i][j] = (A[i][j] - Σ_{k<j} L[i][k]·L[j][k]·D[k]) / D[j]

两者乘加次数相同,约为 n³/6 次乘法加 n³/6 次加法;Cholesky 额外做 n 次开方,LDLᵀ 额外做 n 次除法(除以 D[j])。当 n 较大时,开方与除法的代价差异相对 n³ 项可忽略,但 LDLᵀ 在需要判断正定性时更直接:D 的所有对角元为正等价于 A 正定,无需开方即可检查。

病态矩阵上的数值表现

构造一个 Hilbert 型病态对称正定矩阵 H,元素 H[i][j] = 1/(i+j-1),取 n = 6。其条件数随 n 急剧增大(n=6 时约 1.5×10⁷)。用双精度分别做两种分解,再重构 A 并计算相对误差 ‖A - 重构‖_F / ‖A‖_F,可观察到:

  • 两种分解的重构误差量级相近,均约为 10⁻¹⁰ 到 10⁻⁹,说明在双精度下两者都还能维持基本精度。
  • Cholesky 的 L 对角元在病态时可能非常小,导致后续除法放大舍入误差;LDLᵀ 的 D 对角元同样可能很小,但单位下三角结构使误差传播路径略有不同。
  • 若把精度降到单精度,Cholesky 可能因对角元开方前出现微小负值而失败(理论上正定,但舍入导致 A[j][j] - Σ L² 略负),LDLᵀ 则可能得到负的 D[j],同样暴露病态问题。

下面的 SVG 用参数关系图对比两种分解在“存在条件—计算量—病态表现”三个维度上的差异,并标出关键判断点。

Cholesky 与 LDLᵀ 分解对比(对称正定矩阵) Cholesky:A = L·Lᵀ L 为下三角,对角元全正 未知量:n(n+1)/2 乘加:≈ n³/6 次 额外运算:n 次开方 正定性判断:需 L 对角元 > 0 病态风险:对角元开方前可能略负 LDLᵀ:A = L·D·Lᵀ L 为单位下三角,D 为对角 未知量:n(n+1)/2 乘加:≈ n³/6 次 额外运算:n 次除法(除以 D[j]) 正定性判断:D 对角元 > 0 即可 病态风险:D[j] 可能接近 0 或变负 等价 参数化 共同存在条件 A 对称正定 ⇔ 所有顺序主子式 > 0 ⇔ Cholesky 的 L 对角元全正 ⇔ LDLᵀ 的 D 对角元全正 若 A 仅半正定,Cholesky 可能因开方零而失败;LDLᵀ 可得到 D 含零,需另行处理 病态示例:Hilbert 矩阵 H[i][j] = 1/(i+j-1),n = 6 条件数 ≈ 1.5×10⁷;双精度下两种分解重构相对误差均约 10⁻¹⁰ ~ 10⁻⁹ 单精度下:Cholesky 可能因对角元开方前略负而中断;LDLᵀ 可能得到负 D[j],同样暴露病态 结论:两者精度量级相近,LDLᵀ 更便于直接检查 D 的正负性

可运行的对比实现

下面给出一个自包含的 Python 脚本,实现两种分解、重构误差计算,并在示例矩阵与 Hilbert 矩阵上运行。脚本不依赖第三方库,可直接复制执行。

import math

def cholesky(A):
    n = len(A)
    L = [[0.0]*n for _ in range(n)]
    for j in range(n):
        s = A[j][j] - sum(L[j][k]**2 for k in range(j))
        if s <= 0:
            raise ValueError(f"非正定:第 {j} 列对角元 {s:.3e} <= 0")
        L[j][j] = math.sqrt(s)
        for i in range(j+1, n):
            L[i][j] = (A[i][j] - sum(L[i][k]*L[j][k] for k in range(j))) / L[j][j]
    return L

def ldlt(A):
    n = len(A)
    L = [[0.0]*n for _ in range(n)]
    D = [0.0]*n
    for j in range(n):
        L[j][j] = 1.0
        D[j] = A[j][j] - sum(L[j][k]**2 * D[k] for k in range(j))
        if abs(D[j]) < 1e-300:
            raise ValueError(f"第 {j} 个 D 元素过小:{D[j]:.3e}")
        for i in range(j+1, n):
            L[i][j] = (A[i][j] - sum(L[i][k]*L[j][k]*D[k] for k in range(j))) / D[j]
    return L, D

def reconstruct_chol(L):
    n = len(L)
    return [[sum(L[i][k]*L[j][k] for k in range(n)) for j in range(n)] for i in range(n)]

def reconstruct_ldlt(L, D):
    n = len(L)
    return [[sum(L[i][k]*D[k]*L[j][k] for k in range(n)) for j in range(n)] for i in range(n)]

def rel_err(A, R):
    num = math.sqrt(sum((A[i][j]-R[i][j])**2 for i in range(len(A)) for j in range(len(A))))
    den = math.sqrt(sum(A[i][j]**2 for i in range(len(A)) for j in range(len(A))))
    return num/den

# 示例矩阵 A = B·Bᵀ
B = [[2,0,0,0],[1,3,0,0],[1,1,2,0],[1,1,1,2]]
A = [[sum(B[i][k]*B[j][k] for k in range(4)) for j in range(4)] for i in range(4)]

Lc = cholesky(A)
Ll, D = ldlt(A)
print("Cholesky 重构误差:", rel_err(A, reconstruct_chol(Lc)))
print("LDLᵀ 重构误差:", rel_err(A, reconstruct_ldlt(Ll, D)))
print("D 对角元:", [round(d, 6) for d in D])

# Hilbert 矩阵 n=6
n = 6
H = [[1.0/(i+j+1) for j in range(n)] for i in range(n)]
Lc = cholesky(H)
Ll, D = ldlt(H)
print("Hilbert Cholesky 误差:", rel_err(H, reconstruct_chol(Lc)))
print("Hilbert LDLᵀ 误差:", rel_err(H, reconstruct_ldlt(Ll, D)))
print("Hilbert D 最小元:", min(D))

运行后应看到:示例矩阵两种分解的重构误差均在 10⁻¹⁵ 量级(双精度舍入极限),D 对角元与 B 的行范数平方一致;Hilbert 矩阵的重构误差约 10⁻¹⁰ 量级,D 的最小元非常小但为正。若把 H 的构造改为单精度模拟(如每步 round(x, 7)),可观察到 Cholesky 可能抛出“非正定”异常,而 LDLᵀ 得到负的 D[j],两者都正确暴露了病态导致的数值失效。

判断正定性时,LDLᵀ 的 D 对角元符号比 Cholesky 的 L 对角元更直接:无需开方即可检查,且 D 的数值大小直接反映各主元的尺度,便于定位病态来源。

选择建议

维度CholeskyLDLᵀ
存在条件对称正定对称正定(或对称不定时 D 可含负)
计算量≈ n³/6 乘加 + n 次开方≈ n³/6 乘加 + n 次除法
正定性检查需检查 L 对角元 > 0直接检查 D 对角元 > 0
病态表现开方前可能略负而中断D[j] 可能接近 0 或变负
适用场景需最小存储、直接求解需诊断主元尺度、避免开方

在双精度下,两者精度量级相近;若矩阵条件数很大,应优先考虑缩放、正则化或改用带主元的分解,而不是单纯在两种无主元分解之间切换。