[基础知识点]PCG算法以及代码解析
程序员文章站
2022-04-16 21:29:56
...
1. 基本介绍
- 共轭梯度法是介于梯度下降法与牛顿法之间的一个方法,是一个一阶方法,它克服了梯度下降法收敛慢的缺点,又避免了存储和计算牛顿法所需要的二阶导数信息。
- 共轭梯度法的思想是,选择一个优化方向后,本次选择的步长能够将这个方向的误差更新完,在以后的优化更新过程中不再需要朝这个方向更新了。由于每次将一个方向优化到了极小,后面的优化过程将不再影响之前优化方向上的极小值,所以理论上对N维问题求极小只用对N个方向都求出极小就行了。为了不影响之前优化方向上的更新量,需要每次优化方向共轭正交。在 n 维的优化问题中,共轭梯度法最多 n 次迭代就能找到最优解(是找到,不是接近)。
- 虽然 CG 方法可以减小求解时间和内存占用,但是缺点是矩阵 A 的条件数会很大程度上影响收敛速度。为了减小条件数,需要对 A 进行预处理,即得到预处理共轭梯度法 PCG。
2. 算法流程
3. 相关代码
节选自BA代码,求解中使用PCG代码进行求解。
VecX Problem::PCGSolver(const MatXX &A, const VecX &b, int maxIter = -1) {
assert(A.rows() == A.cols() && "PCG solver ERROR: A is not a square matrix");
int rows = b.rows();
int n = maxIter < 0 ? rows : maxIter;
VecX x(VecX::Zero(rows));
MatXX M_inv = A.diagonal().asDiagonal().inverse();
VecX r0(b); // initial r = b - A*0 = b
VecX z0 = M_inv * r0;
VecX p(z0);
VecX w = A * p;
double r0z0 = r0.dot(z0);
double alpha = r0z0 / p.dot(w);
VecX r1 = r0 - alpha * w;
int i = 0;
double threshold = 1e-6 * r0.norm();
while (r1.norm() > threshold && i < n) {
i++;
VecX z1 = M_inv * r1;
double r1z1 = r1.dot(z1);
double belta = r1z1 / r0z0;
z0 = z1;
r0z0 = r1z1;
r0 = r1;
p = belta * p + z1;
w = A * p;
alpha = r1z1 / p.dot(w);
x += alpha * p;
r1 -= alpha * w;
}
return x;
}