我试着用C++写一个简单的共轭梯度法函数--很简单吧?但它确实不能解任何方程组。它收敛到完全错误的值上。
我已经把函数写在这里了,并提供了主函数,其中我使用对称矩阵M生成正定矩阵A。我已经提供了所有的代码,看看是否有人可以发现任何错误。请不要因为我这样做而嘲笑我--这是一个很好理解的算法,不提供所有的代码是没有意义的,对吗?请假设ApplyLHS和innerProduct函数工作正常。
template <typename TData>
void DoConjGrad(
std::vector<TData> &in,
std::vector<TData> &out,
std::vector<TData> &A
)
{
TData tol = 10e-8;
size_t N = in.size();
std::vector<TData> r(N);
std::vector<TData> p(N);
std::vector<TData> w(N);
size_t k = 0;
TData rTr;
TData rTr_prev;
TData pTw;
TData alpha;
TData beta;
// initial guess for x0
std::fill(out.begin(), out.end(), 0);
// r0 = b - Ax0
ApplyLHS(out, r, A);
std::transform(in.cbegin(), in.cend(), r.cbegin(), r.begin(),
[](const TData &bElem, const TData &rElem){ return bElem - rElem; } );
// p0 := r0
std::copy(r.cbegin(), r.cend(), p.begin());
// calculate (rTr)0
rTr = innerProduct(r, r);
for (int i = 0; i < 100; ++i)
{
ApplyLHS(p, w, A);
pTw = innerProduct(p, w);
alpha = rTr / pTw;
std::transform(out.cbegin(), out.cend(), p.cbegin(), out.begin(),
[alpha](const TData &xElem, const TData &pElem)
{
return xElem + alpha*pElem;
});
std::transform(r.cbegin(), r.cend(), w.cbegin(), r.begin(),
[alpha](const TData &rElem, const TData &wElem)
{
return rElem - alpha*wElem;
});
if (rTr < tol)
break;
rTr_prev = rTr;
rTr = innerProduct(r, r);
beta = rTr / rTr_prev;
std::transform(r.cbegin(), r.cend(), p.cbegin(), p.begin(),
[beta](const TData &rElem, const TData &pElem)
{
return rElem + beta*pElem;
});
}
}
主要:
int main()
{
srand(100);
size_t N = 2;
std::vector<double> A(N * N);
std::vector<double> M(N * N);
std::vector<double> x_true(N);
std::vector<double> x_calc(N);
std::vector<double> b(N);
for (int i = 0; i < N; ++i)
for (int j = 0; j <= i; ++j)
{
double r = 2*((double)rand())/((double)RAND_MAX) - 1;
M[i*N + j] = r;
M[j*N + i] = r;
}
// get positive semi-definite matrix = M.T @ M
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
for (int k = 0; k < N; ++k)
A[i*N + j] = M[k*N + i] * M[k*N + j];
// generate random x
for (int i = 0; i < N; ++i)
x_true[i] = 2*((double)rand())/((double)RAND_MAX) - 1;
// calculate b
ApplyLHS(x_true, b, A);
// solve for x calc
DoConjGrad<double>(b, x_calc, A);
}
辅助函数:
template <typename TData>
void printMat(
size_t nr,
size_t nc,
std::vector<TData> &mat
)
{
for (int i = 0; i < nr; ++i)
{
for (int j = 0; j < nc; ++j)
{
std::cout.precision(5);
std::cout.width(10);
std::cout << mat[i*nc + j] << "\t";
}
std::cout << "\n";
}
}
template <typename TData>
void ApplyLHS(
std::vector<TData> &in,
std::vector<TData> &out,
std::vector<TData> &A
)
{
size_t N = in.size();
std::fill(out.begin(), out.end(), 0);
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
out[i] += in[j] * A[i*N + j];
}
template <typename TData>
TData innerProduct(
std::vector<TData> &a,
std::vector<TData> &b
)
{
TData result = 0;
for (int i = 0; i < a.size(); ++i)
{
result += a[i] * b[i];
}
return result;
}
1条答案
按热度按时间yhxst69z1#
首先是好消息:你的共轭梯度算法似乎没有什么问题!(没有任何东西会导致它失败。就我个人而言,我会让容差更小,并将循环退出条件移动到计算rTr的点之后。)
如果你用你的代码计算A的行列式...你会发现det(A)每次都=0,不管N的值是多少。(所以A是单数;存在某个非零x,使得Ax=0,因此xT.A. x=0:因此A也不是严格正定的,这是CG算法的条件)。
但一个主要的线索是M的行列式(通常)不是零……故A(A)为D(M)的平方。
所以,看看A = MTM的矩阵乘法。它不正确;(每个元素应该被初始化,然后由总和形成)。将此部分更改为以下内容,您就可以开始了。