柯列斯基分解cholesky decomposition只能用于实对称正定矩阵,实对称正定矩阵一般用于定义内积。柯列斯基分解是将矩阵分解为如下形式:
A
=
G
G
T
A=GG^T
A=GGT
其中
G
G
G是下三角矩阵。柯列斯基分解主要有两种算法:LU分解法和递推算法。
LU分解法
LU分解法主要分三步:
- 求矩阵的Doolittle分解 A = L U A=LU A=LU。
- 因为是对称矩阵,所以 U = D L T U=DL^{T} U=DLT, D D D是 U U U的对角线元素构成的对角阵。
- 算出
G
=
L
D
G=L\sqrt{D}
G=LD,虽然我们没有学过矩阵函数,但是对角阵开根号直接把对角线各个元素开根号就行了。因为是正定矩阵,所以不存在对负数开根号的问题。
对于这种算法,Python代码比较简单:
# 柯列斯基分解
def cholesky(self):
l, u = self.lu_decomposition()
# u的对角线元素开根号
n = len(self.__vectors)
d_array = [
[math.sqrt(u.vectors[i][j]) if i == j else 0 for j in range(n)]
for i in range(n)
]
d = Matrix(d_array)
return l * d, d * u
递推算法
递推算法利用的是两个分解出来的矩阵是互为转置的特点进行的。按列进行主循环迭代,每个迭代步骤主要分两小步:
- 求出对角线元素;
- 再求出该列对角线元素下方其他元素。
对角线元素使用公式就好了。利用以下迭代公式:
g
i
i
=
a
i
i
−
∑
k
=
1
i
−
1
g
i
k
2
g_{ii}=\sqrt{a_{ii}-\sum_{k=1}^{i-1}g_{ik}^2}
gii=aii−k=1∑i−1gik2
非对角线元素使用下面这个公式:
g
j
i
=
1
g
i
i
(
a
i
j
−
∑
k
=
1
i
−
1
g
i
k
g
j
k
)
g_{ji}=\frac1{g_{ii}}(a_{ij}-\sum_{k=1}^{i-1}g_{ik}g_{jk})
gji=gii1(aij−k=1∑i−1gikgjk)
算法清晰了,python代码就容易了:
# 柯列斯基分解迭代法
def cholesky_iterate(self):
n = len(self.__vectors)
array = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
# 对角线元素
s = 0
for k in range(i):
s += array[k][i] * array[k][i]
array[i][i] = math.sqrt(self.__vectors[i][i] - s)
# 非对角线元素
for j in range(i+1,n):
s1 = 0
for k in range(i):
s1 += array[k][i] * array[k][j]
array[i][j] = (self.__vectors[j][i] - s1) / array[i][i]
matrix = Matrix(array)
return matrix, Matrix(matrix.transpose())