実数マトリクスQR分解アルゴリズムのC++実現
15313 ワード
ヘッダファイル:
実装ファイル:
テストコード:
実行結果:
/*
* Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected]
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 or any later version.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. A copy of the GNU General Public License is available at:
* http://www.fsf.org/licensing/licenses
*/
/*****************************************************************************
* qrd.h
*
* Class template of QR decomposition for real matrix.
*
* For an m-by-n matrix A, the QR decomposition is an m-by-m orthogonal
* matrix Q and an m-by-n upper triangular matrix R so that A = Q*R.
*
* For economy size, denotes p = min(m,n), then Q is m-by-p, and R is n-by-p,
* this file provides the economic decomposition format.
*
* The QR decompostion always exists, even if the matrix does not have full
* rank, so the constructor will never fail. The Q and R factors can be
* retrived via the getQ() and getR() methods. Furthermore, a solve() method
* is provided to find the least squares solution of Ax=b or AX=B using the
* QR factors.
*
* Zhang Ming, 2010-01 (revised 2010-12), Xi'an Jiaotong University.
*****************************************************************************/
#ifndef QRD_H
#define QRD_H
#include <matrix.h>
namespace splab
{
template <typename Real>
class QRD
{
public:
QRD();
~QRD();
void dec( const Matrix<Real> &A );
bool isFullRank() const;
Matrix<Real> getQ();
Matrix<Real> getR();
Matrix<Real> getH();
Vector<Real> solve( const Vector<Real> &b );
Matrix<Real> solve( const Matrix<Real> &B );
private:
// internal storage of QR
Matrix<Real> QR;
// diagonal of R.
Vector<Real> RDiag;
};
// class QRD
#include <qrd-impl.h>
}
// namespace splab
#endif
// QRD_H
実装ファイル:
/*
* Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected]
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 or any later version.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. A copy of the GNU General Public License is available at:
* http://www.fsf.org/licensing/licenses
*/
/*****************************************************************************
* qrd-impl.h
*
* Implementation for QRD class.
*
* Zhang Ming, 2010-01 (revised 2010-12), Xi'an Jiaotong University.
*****************************************************************************/
/**
* constructor and destructor
*/
template<typename Real>
QRD<Real>::QRD()
{
}
template<typename Real>
QRD<Real>::~QRD()
{
}
/**
* Create a QR factorization object for A.
*/
template <typename Real>
void QRD<Real>::dec( const Matrix<Real> &A )
{
int m = A.rows(),
n = A.cols(),
p = min(m,n);
QR = A;
RDiag = Vector<Real>(p);
// main loop.
for( int k=0; k<p; ++k )
{
// Compute 2-norm of k-th column without under/overflow.
Real nrm = 0;
for( int i=k; i<m; ++i )
nrm = hypot( nrm, QR[i][k] );
// for( int i=k; i<m; ++i )
// nrm += QR[i][k]*QR[i][k];
// nrm = sqrt(nrm);
if( nrm != 0 )
{
// Form k-th Householder vector.
if( QR[k][k] < 0 )
nrm = -nrm;
for( int i=k; i<m; ++i )
QR[i][k] /= nrm;
QR[k][k] += 1;
// Apply transformation to remaining columns.
for( int j=k+1; j<n; ++j )
{
Real s = 0;
for( int i=k; i<m; ++i )
s += QR[i][k]*QR[i][j];
s = -s/QR[k][k];
for( int i=k; i<m; ++i )
QR[i][j] += s*QR[i][k];
}
}
RDiag[k] = -nrm;
}
}
/**
* Flag to denote the matrix is of full rank.
*/
template <typename Real>
inline bool QRD<Real>::isFullRank() const
{
for( int j=0; j<RDiag.dim(); ++j )
if( RDiag[j] == 0 )
return false;
return true;
}
/**
* Return the upper triangular factorof the QR factorization.
*/
template <typename Real>
Matrix<Real> QRD<Real>::getQ()
{
int m = QR.rows(),
p = RDiag.dim();
Matrix<Real> Q( m, p );
for( int k=p-1; k>=0; --k )
{
for( int i=0; i<m; ++i )
Q[i][k] = 0;
Q[k][k] = 1;
for( int j=k; j<p; ++j )
if( QR[k][k] != 0 )
{
Real s = 0;
for( int i=k; i<m; ++i )
s += QR[i][k] * Q[i][j];
s = -s / QR[k][k];
for( int i=k; i<m; ++i )
Q[i][j] += s*QR[i][k];
}
}
return Q;
}
/**
* Return the orthogonal factorof the QR factorization.
*/
template <typename Real>
Matrix<Real> QRD<Real>::getR()
{
int n = QR.cols(),
p = RDiag.dim();
Matrix<Real> R( p, n );
for( int i=0; i<p; ++i )
for( int j=0; j<n; ++j )
if( i < j )
R[i][j] = QR[i][j];
else if( i == j )
R[i][j] = RDiag[i];
return R;
}
/**
* Retreive the Householder vectors from QR factorization
*/
template <typename Real>
Matrix<Real> QRD<Real>::getH()
{
int m = QR.rows(),
p = RDiag.dim();
Matrix<Real> H( m, p );
for( int i=0; i<m; ++i )
for( int j=0; j<=i&&j<p; ++j )
H[i][j] = QR[i][j];
return H;
}
/**
* Least squares solution of A*x = b
* Return x: a vector that minimizes the two norm of Q*R*X-B.
* If B is non-conformant, or if QR.isFullRank() is false,
* the routinereturns a null (0-length) vector.
*/
template <typename Real>
Vector<Real> QRD<Real>::solve( const Vector<Real> &b )
{
int m = QR.rows(),
n = QR.cols();
assert( b.dim() == m );
// matrix is rank deficient
if( !isFullRank() )
return Vector<Real>();
Vector<Real> x = b;
// compute y = transpose(Q)*b
for( int k=0; k<n; ++k )
{
Real s = 0;
for( int i=k; i<m; ++i )
s += QR[i][k]*x[i];
s = -s/QR[k][k];
for( int i=k; i<m; ++i )
x[i] += s*QR[i][k];
}
// solve R*x = y;
for( int k=n-1; k>=0; --k )
{
x[k] /= RDiag[k];
for( int i=0; i<k; ++i )
x[i] -= x[k]*QR[i][k];
}
// return n portion of x
Vector<Real> x_(n);
for( int i=0; i<n; ++i )
x_[i] = x[i];
return x_;
}
/**
* Least squares solution of A*X = B
* return X: a matrix that minimizes the two norm of Q*R*X-B.
* If B is non-conformant, or if QR.isFullRank() is false, the
* routinereturns a null (0) matrix.
*/
template <typename Real>
Matrix<Real> QRD<Real>::solve( const Matrix<Real> &B )
{
int m = QR.rows();
int n = QR.cols();
assert( B.rows() == m );
// matrix is rank deficient
if( !isFullRank() )
return Matrix<Real>(0,0);
int nx = B.cols();
Matrix<Real> X = B;
// compute Y = transpose(Q)*B
for( int k=0; k<n; ++k )
for( int j=0; j<nx; ++j )
{
Real s = 0;
for( int i=k; i<m; ++i )
s += QR[i][k]*X[i][j];
s = -s/QR[k][k];
for( int i=k; i<m; ++i )
X[i][j] += s*QR[i][k];
}
// solve R*X = Y;
for( int k=n-1; k>=0; --k )
{
for( int j=0; j<nx; ++j )
X[k][j] /= RDiag[k];
for( int i=0; i<k; ++i )
for( int j=0; j<nx; ++j )
X[i][j] -= X[k][j]*QR[i][k];
}
// return n x nx portion of X
Matrix<Real> X_( n, nx );
for( int i=0; i<n; ++i )
for( int j=0; j<nx; ++j )
X_[i][j] = X[i][j];
return X_;
}
テストコード:
/*****************************************************************************
* qrd_test.cpp
*
* QRD class testing.
*
* Zhang Ming, 2010-01 (revised 2010-12), Xi'an Jiaotong University.
*****************************************************************************/
#define BOUNDS_CHECK
#include <iostream>
#include <iomanip>
#include <qrd.h>
using namespace std;
using namespace splab;
typedef double Type;
const int M = 4;
const int N = 3;
int main()
{
Matrix<Type> A(M,N), Q, R;
A[0][0] = 1; A[0][1] = 0; A[0][2] = 0;
A[1][0] = 1; A[1][1] = 2; A[1][2] = 4;
A[2][0] = 1; A[2][1] = 3; A[2][2] = 9;
A[3][0] = 1; A[3][1] = 3; A[3][2] = 9;
Matrix<Type> B = trT(A);
QRD<Type> qr;
qr.dec(B);
Q = qr.getQ();
R = qr.getR();
cout << setiosflags(ios::fixed) << setprecision(4);
cout << "The original matrix B : " << B << endl;
cout << "The orthogonal matrix Q : " << Q << endl;
cout << "The upper triangular matrix R : " << R << endl;
cout << "B - Q*R : " << B - Q*R << endl;
Vector<Type> b(M);
b[0]= 1; b[1] = 0; b[2] = 1, b[3] = 2;
qr.dec(A);
if( qr.isFullRank() )
{
Vector<Type> x = qr.solve(b);
cout << "The constant vector b : " << b << endl;
cout << "The least squares solution of A * x = b : " << x << endl;
Matrix<Type> X = qr.solve(eye(M,Type(1)));
cout << "The least squares solution of A * X = I : " << X << endl;
cout << "The A * X: " << A*X << endl;
}
else
cout << " The matrix is rank deficient! " << endl;
return 0;
}
実行結果:
The original matrix B : size: 3 by 4
1.0000 1.0000 1.0000 1.0000
0.0000 2.0000 3.0000 3.0000
0.0000 4.0000 9.0000 9.0000
The orthogonal matrix Q : size: 3 by 3
-1.0000 0.0000 0.0000
0.0000 -0.4472 0.8944
0.0000 -0.8944 -0.4472
The upper triangular matrix R : size: 3 by 4
-1.0000 -1.0000 -1.0000 -1.0000
0.0000 -4.4721 -9.3915 -9.3915
0.0000 0.0000 -1.3416 -1.3416
B - Q*R : size: 3 by 4
0.0000 0.0000 0.0000 0.0000
0.0000 0.0000 0.0000 0.0000
0.0000 0.0000 0.0000 0.0000
The constant vector b : size: 4 by 1
1.0000
0.0000
1.0000
2.0000
The least squares solution of A * x = b : size: 3 by 1
1.0000
-1.8333
0.6667
The least squares solution of A * X = I : size: 3 by 4
1.0000 -0.0000 0.0000 -0.0000
-0.8333 1.5000 -0.3333 -0.3333
0.1667 -0.5000 0.1667 0.1667
The A * X: size: 4 by 4
1.0000 0.0000 0.0000 0.0000
0.0000 1.0000 -0.0000 0.0000
-0.0000 0.0000 0.5000 0.5000
-0.0000 0.0000 0.5000 0.5000
Process returned 0 (0x0) execution time : 0.099 s
Press any key to continue.