C++,Lapack Cholesky 分解实现不准确的结果
C++, Lapack Cholesky decomposition implementation inaccurate result
我正在尝试用 C++ 实现 Cholesky 分解,它以前在 lapack dpotrf_ 中实现过。
Cholesky 分解:R' * R = A
代码:
#include <iostream>
#include <armadillo>
long my_chol(
arma::mat &R,
const arma::mat A,
long lda
)
{
arma::arma_debug_check( (A.is_square() == false), "chol(): given matrix must be square sized" );
arma::arma_debug_check( (lda<std::max(1l,(long)A.n_rows)), "chol(): LDA must be equal to or greater than max(1,N)" );
double sum;
long i, j, k;
long n = A.n_rows;
if(lda==(long)A.n_rows)
R=A;
else
{
R.zeros(lda,A.n_rows);
R.submat(0,lda-1,0,A.n_rows-1)=A;
}
for(i=0; i<n; ++i)
for(j=i+1; j<n; ++j)
R(j,i)=0;
for( i=0; i<n; ++i )
{
/* j == i */
sum = R(i,i);
for( k=(i-1); k>=0; --k )
sum -= R(k,i)*R(k,i);
if ( sum > 0.0 )
R(i,i) = sqrt( sum );
else
{
R(0) = sum; /* tunnel negative diagonal element to caller */
return (long)i+1;
}
for( j=(i+1); j<n; ++j )
{
sum = R(i,j);
for( k=(i-1); k>=0; --k )
sum -= R(k,i) * R(k,i);
R(i,j) = sum / R(i,i);
}
}
return 0;
}
我使用以下代码测试此功能:
int main()
{
arma::mat A={{10, 3, 5},{3, 60, 7},{5, 7, 9}};
arma::mat B;
my_chol(B,A,3);
std::cout<<"---------------------------\n";
std::cout<<"A:\n";
A.print();
std::cout<<"B:\n";
B.print();
std::cout<<"---------------------------\n";
return 0;
}
这是结果:
---------------------------
A:
10.0000 3.0000 5.0000
3.0000 60.0000 7.0000
5.0000 7.0000 9.0000
B:
3.1623 0.9487 1.5811
0 7.6877 0.7935
0 0 2.4229
---------------------------
但是在八度音程中测试相同的矩阵给出了不同的结果:
A=[10,3,5;3,60,7;5,7,9];
chol(A)
3.16228 0.94868 1.58114
0.00000 7.68765 0.71543
0.00000 0.00000 2.44707
尽管结果看起来很接近,但它们之间存在细微差别。
R23
和R33
略有不同。我检查了结果。 octave 的结果是正确的,而我的结果不是:
R=[3.1623,0.9487,1.5811;0,7.6877,0.7935;0,0,2.4229];
R'*R
ans =
10.0001 3.0001 4.9999
3.0001 60.0008 7.6002
4.9999 7.6002 9.0000
为什么我的代码给出了错误的结果?
最里面的循环应该是
sum -= R(k,i) * R(k,j);
而不是
sum -= R(k,i) * R(k,i);
我正在尝试用 C++ 实现 Cholesky 分解,它以前在 lapack dpotrf_ 中实现过。
Cholesky 分解:R' * R = A
代码:
#include <iostream>
#include <armadillo>
long my_chol(
arma::mat &R,
const arma::mat A,
long lda
)
{
arma::arma_debug_check( (A.is_square() == false), "chol(): given matrix must be square sized" );
arma::arma_debug_check( (lda<std::max(1l,(long)A.n_rows)), "chol(): LDA must be equal to or greater than max(1,N)" );
double sum;
long i, j, k;
long n = A.n_rows;
if(lda==(long)A.n_rows)
R=A;
else
{
R.zeros(lda,A.n_rows);
R.submat(0,lda-1,0,A.n_rows-1)=A;
}
for(i=0; i<n; ++i)
for(j=i+1; j<n; ++j)
R(j,i)=0;
for( i=0; i<n; ++i )
{
/* j == i */
sum = R(i,i);
for( k=(i-1); k>=0; --k )
sum -= R(k,i)*R(k,i);
if ( sum > 0.0 )
R(i,i) = sqrt( sum );
else
{
R(0) = sum; /* tunnel negative diagonal element to caller */
return (long)i+1;
}
for( j=(i+1); j<n; ++j )
{
sum = R(i,j);
for( k=(i-1); k>=0; --k )
sum -= R(k,i) * R(k,i);
R(i,j) = sum / R(i,i);
}
}
return 0;
}
我使用以下代码测试此功能:
int main()
{
arma::mat A={{10, 3, 5},{3, 60, 7},{5, 7, 9}};
arma::mat B;
my_chol(B,A,3);
std::cout<<"---------------------------\n";
std::cout<<"A:\n";
A.print();
std::cout<<"B:\n";
B.print();
std::cout<<"---------------------------\n";
return 0;
}
这是结果:
---------------------------
A:
10.0000 3.0000 5.0000
3.0000 60.0000 7.0000
5.0000 7.0000 9.0000
B:
3.1623 0.9487 1.5811
0 7.6877 0.7935
0 0 2.4229
---------------------------
但是在八度音程中测试相同的矩阵给出了不同的结果:
A=[10,3,5;3,60,7;5,7,9];
chol(A)
3.16228 0.94868 1.58114
0.00000 7.68765 0.71543
0.00000 0.00000 2.44707
尽管结果看起来很接近,但它们之间存在细微差别。
R23
和R33
略有不同。我检查了结果。 octave 的结果是正确的,而我的结果不是:
R=[3.1623,0.9487,1.5811;0,7.6877,0.7935;0,0,2.4229];
R'*R
ans =
10.0001 3.0001 4.9999
3.0001 60.0008 7.6002
4.9999 7.6002 9.0000
为什么我的代码给出了错误的结果?
最里面的循环应该是
sum -= R(k,i) * R(k,j);
而不是
sum -= R(k,i) * R(k,i);