欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[基础知识点]PCG算法以及代码解析

程序员文章站 2022-04-16 21:29:56
...

1. 基本介绍

  1. 共轭梯度法是介于梯度下降法与牛顿法之间的一个方法,是一个一阶方法,它克服了梯度下降法收敛慢的缺点,又避免了存储和计算牛顿法所需要的二阶导数信息。
  2. 共轭梯度法的思想是,选择一个优化方向后,本次选择的步长能够将这个方向的误差更新完,在以后的优化更新过程中不再需要朝这个方向更新了。由于每次将一个方向优化到了极小,后面的优化过程将不再影响之前优化方向上的极小值,所以理论上对N维问题求极小只用对N个方向都求出极小就行了。为了不影响之前优化方向上的更新量,需要每次优化方向共轭正交。在 n 维的优化问题中,共轭梯度法最多 n 次迭代就能找到最优解(是找到,不是接近)。
  3. 虽然 CG 方法可以减小求解时间和内存占用,但是缺点是矩阵 A 的条件数会很大程度上影响收敛速度。为了减小条件数,需要对 A 进行预处理,即得到预处理共轭梯度法 PCG。

2. 算法流程

[基础知识点]PCG算法以及代码解析

3. 相关代码

节选自BA代码,求解Aδxp=bA\delta x_p = b中使用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;
}