实数矩阵SVD分解算法的C++实现

头文件:

/*
 * 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
 */


/*****************************************************************************
 *                                    svd.h
 *
 * Class template of Singular Value Decomposition.
 *
 * For an m-by-n matrix A, the singular value decomposition is an m-by-m
 * orthogonal matrix U, an m-by-n diagonal matrix S, and an n-by-n orthogonal
 * matrix V so that A = U*S*V^T.
 *
 * The singular values, sigma[k] = S[k][k], are ordered so that sigma[0] >=
 * sigma[1] >= ... >= sigma[n-1].
 *
 * For economy size, denotes p = min(m,n), then U is m-by-p, S is p-by-p,
 * and V is n-by-p, this file provides the economic decomposition format.
 *
 * The singular value decompostion always exists, so the constructor will
 * never fail. The matrix condition number and the effective numerical rank
 * can be computed from this decomposition.
 *
 * Adapted form Template Numerical Toolkit.
 *
 * Zhang Ming, 2010-01 (revised 2010-08), Xi'an Jiaotong University.
 *****************************************************************************/


#ifndef SVD_H
#define SVD_H


#include <matrix.h>


namespace splab
{

	template <typename Real>
	class SVD
	{

	public:

        SVD();
		~SVD();

        void dec( const Matrix<Real> &A );
		Matrix<Real> getU() const;
		Matrix<Real> getV() const;
		Matrix<Real> getSM();
		Vector<Real> getSV() const;

		Real norm2() const;
		Real cond() const;
		int  rank();

    private:

		// the orthogonal matrix and singular value vector
		Matrix<Real> U;
		Matrix<Real> V;
		Vector<Real> S;

        // docomposition for matrix with rows >= columns
		void decomposition( Matrix<Real>&, Matrix<Real>&,
                            Vector<Real>&, Matrix<Real>& );

	};
	// class SVD


    #include <svd-impl.h>

}
// namespace splab


#endif
// SVD_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
 */


/*****************************************************************************
 *                               svd-impl.h
 *
 * Implementation for SVD class.
 *
 * Zhang Ming, 2010-01 (revised 2010-08), Xi'an Jiaotong University.
 *****************************************************************************/


/**
 * constructor and destructor
 */
template<typename Real>
SVD<Real>::SVD()
{
}

template<typename Real>
SVD<Real>::~SVD()
{
}


/**
 * Making singular decomposition.
 */
template <typename Real>
void SVD<Real>::dec( const Matrix<Real> &A )
{
    int m = A.rows(),
        n = A.cols(),
        p = min( m, n );

    U = Matrix<Real>( m, p );
    V = Matrix<Real>( n, p );
    S = Vector<Real>( p );
    if( m >= n )
    {
        Matrix<Real> B(A);
        decomposition( B, U, S, V );
    }
    else
    {
        Matrix<Real> B( trT( A ) );
        decomposition( B, V, S, U );
    }
}


/**
 * Making singular decomposition of m >= n.
 */
template <typename Real>
void SVD<Real>::decomposition( Matrix<Real> &B, Matrix<Real> &U,
                               Vector<Real> &S, Matrix<Real> &V )
{
    int m = B.rows(),
        n = B.cols();

    Vector<Real> e(n);
    Vector<Real> work(m);

    // boolean
    int wantu = 1;
    int wantv = 1;

    // Reduce A to bidiagonal form, storing the diagonal elements
    // in s and the super-diagonal elements in e.
    int nct = min( m-1, n );
    int nrt = max( 0, n-2 );
    int i=0,
        j=0,
        k=0;

    for( k=0; k<max(nct,nrt); ++k )
    {
        if( k < nct )
        {
            // Compute the transformation for the k-th column and
            // place the k-th diagonal in s[k].
            // Compute 2-norm of k-th column without under/overflow.
            S[k] = 0;
            for( i=k; i<m; ++i )
                 S[k] = hypot( S[k], B[i][k] );

            if( S[k] != 0 )
            {
                if( B[k][k] < 0 )
                    S[k] = -S[k];

                for( i=k; i<m; ++i )
                    B[i][k] /= S[k];
                B[k][k] += 1;
            }
            S[k] = -S[k];
        }

        for( j=k+1; j<n; ++j )
        {
            if( (k < nct) && ( S[k] != 0 ) )
            {
                // apply the transformation
                Real t = 0;
                for( i=k; i<m; ++i )
                    t += B[i][k] * B[i][j];

                t = -t / B[k][k];
                for( i=k; i<m; ++i )
                    B[i][j] += t*B[i][k];
            }
            e[j] = B[k][j];
        }

        // Place the transformation in U for subsequent back
        // multiplication.
        if( wantu & (k < nct) )
            for( i=k; i<m; ++i )
                U[i][k] = B[i][k];

        if( k < nrt )
        {
            // Compute the k-th row transformation and place the
            // k-th super-diagonal in e[k].
            // Compute 2-norm without under/overflow.
            e[k] = 0;
            for( i=k+1; i<n; ++i )
                e[k] = hypot( e[k], e[i] );

            if( e[k] != 0 )
            {
                if( e[k+1] < 0 )
                    e[k] = -e[k];

                for( i=k+1; i<n; ++i )
                    e[i] /= e[k];
                e[k+1] += 1;
            }
            e[k] = -e[k];

            if( (k+1 < m) && ( e[k] != 0 ) )
            {
                // apply the transformation
                for( i=k+1; i<m; ++i )
                    work[i] = 0;

                for( j=k+1; j<n; ++j )
                    for( i=k+1; i<m; ++i )
                        work[i] += e[j] * B[i][j];

                for( j=k+1; j<n; ++j )
                {
                    Real t = -e[j]/e[k+1];
                    for( i=k+1; i<m; ++i )
                        B[i][j] += t * work[i];
                }
            }

            // Place the transformation in V for subsequent
            // back multiplication.
            if( wantv )
                for( i=k+1; i<n; ++i )
                    V[i][k] = e[i];
        }
    }

    // Set up the final bidiagonal matrix or order p.
    int p = n;

    if( nct < n )
        S[nct] = B[nct][nct];
    if( m < p )
        S[p-1] = 0;

    if( nrt+1 < p )
        e[nrt] = B[nrt][p-1];
    e[p-1] = 0;

    // if required, generate U
    if( wantu )
    {
        for( j=nct; j<n; ++j )
        {
            for( i=0; i<m; ++i )
                U[i][j] = 0;
            U[j][j] = 1;
        }

        for( k=nct-1; k>=0; --k )
            if( S[k] != 0 )
            {
                for( j=k+1; j<n; ++j )
                {
                    Real t = 0;
                    for( i=k; i<m; ++i )
                        t += U[i][k] * U[i][j];
                    t = -t / U[k][k];

                    for( i=k; i<m; ++i )
                        U[i][j] += t * U[i][k];
                }

                for( i=k; i<m; ++i )
                    U[i][k] = -U[i][k];
                U[k][k] = 1 + U[k][k];

                for( i=0; i<k-1; ++i )
                    U[i][k] = 0;
            }
            else
            {
                for( i=0; i<m; ++i )
                    U[i][k] = 0;
                U[k][k] = 1;
            }
    }

    // if required, generate V
    if( wantv )
        for( k=n-1; k>=0; --k )
        {
            if( (k < nrt) && ( e[k] != 0 ) )
                for( j=k+1; j<n; ++j )
                {
                    Real t = 0;
                    for( i=k+1; i<n; ++i )
                        t += V[i][k] * V[i][j];
                    t = -t / V[k+1][k];

                    for( i=k+1; i<n; ++i )
                        V[i][j] += t * V[i][k];
                }

            for( i=0; i<n; ++i )
                V[i][k] = 0;
            V[k][k] = 1;
        }

    // main iteration loop for the singular values
    int pp = p-1;
    int iter = 0;
    double eps = pow( 2.0, -52.0 );

    while( p > 0 )
    {
        int k = 0;
        int kase = 0;

        // Here is where a test for too many iterations would go.
        // This section of the program inspects for negligible
        // elements in the s and e arrays. On completion the
        // variables kase and k are set as follows.
        // kase = 1     if s(p) and e[k-1] are negligible and k<p
        // kase = 2     if s(k) is negligible and k<p
        // kase = 3     if e[k-1] is negligible, k<p, and
        //				s(k), ..., s(p) are not negligible
        // kase = 4     if e(p-1) is negligible (convergence).
        for( k=p-2; k>=-1; --k )
        {
            if( k == -1 )
                break;

            if( abs(e[k]) <= eps*( abs(S[k])+abs(S[k+1]) ) )
            {
                e[k] = 0;
                break;
            }
        }

        if( k == p-2 )
            kase = 4;
        else
        {
            int ks;
            for( ks=p-1; ks>=k; --ks )
            {
                if( ks == k )
                    break;

                Real t = ( (ks != p) ? abs(e[ks]) : 0 ) +
                         ( (ks != k+1) ? abs(e[ks-1]) : 0 );

                if( abs(S[ks]) <= eps*t )
                {
                    S[ks] = 0;
                    break;
                }
            }

            if( ks == k )
                kase = 3;
            else if( ks == p-1 )
                kase = 1;
            else
            {
                kase = 2;
                k = ks;
            }
        }
        k++;

        // Perform the task indicated by kase.
        switch( kase )
        {
            // deflate negligible s(p)
            case 1:
            {
                Real f = e[p-2];
                e[p-2] = 0;

                for( j=p-2; j>=k; --j )
                {
                    Real t = hypot( S[j], f );
                    Real cs = S[j] / t;
                    Real sn = f / t;
                    S[j] = t;

                    if( j != k )
                    {
                        f = -sn * e[j-1];
                        e[j-1] = cs * e[j-1];
                    }

                    if( wantv )
                        for( i=0; i<n; ++i )
                        {
                            t = cs*V[i][j] + sn*V[i][p-1];
                            V[i][p-1] = -sn*V[i][j] + cs*V[i][p-1];
                            V[i][j] = t;
                        }
                }
            }
            break;

            // split at negligible s(k)
            case 2:
            {
                Real f = e[k-1];
                e[k-1] = 0;

                for( j=k; j<p; ++j )
                {
                    Real t = hypot( S[j], f );
                    Real cs = S[j] / t;
                    Real sn = f / t;
                    S[j] = t;
                    f = -sn * e[j];
                    e[j] = cs * e[j];

                    if( wantu )
                        for( i=0; i<m; ++i )
                        {
                            t = cs*U[i][j] + sn*U[i][k-1];
                            U[i][k-1] = -sn*U[i][j] + cs*U[i][k-1];
                            U[i][j] = t;
                        }
                }
            }
            break;

            // perform one qr step
            case 3:
            {
                // calculate the shift
                Real scale = max( max( max( max(
                             abs(S[p-1]), abs(S[p-2]) ), abs(e[p-2]) ),
                             abs(S[k]) ), abs(e[k]) );
                Real sp = S[p-1] / scale;
                Real spm1 = S[p-2] / scale;
                Real epm1 = e[p-2] / scale;
                Real sk = S[k] / scale;
                Real ek = e[k] / scale;
                Real b = ( (spm1+sp)*(spm1-sp) + epm1*epm1 ) / 2.0;
                Real c = (sp*epm1) * (sp*epm1);
                Real shift = 0;

                if( ( b != 0 ) || ( c != 0 ) )
                {
                    shift = sqrt( b*b+c );
                    if( b < 0 )
                        shift = -shift;
                    shift = c / ( b+shift );
                }
                Real f = (sk+sp)*(sk-sp) + shift;
                Real g = sk * ek;

                // chase zeros
                for( j=k; j<p-1; ++j )
                {
                    Real t = hypot( f, g );
                    Real cs = f / t;
                    Real sn = g / t;
                    if( j != k )
                        e[j-1] = t;

                    f = cs*S[j] + sn*e[j];
                    e[j] = cs*e[j] - sn*S[j];
                    g = sn * S[j+1];
                    S[j+1] = cs * S[j+1];

                    if( wantv )
                        for( i=0; i<n; ++i )
                        {
                            t = cs*V[i][j] + sn*V[i][j+1];
                            V[i][j+1] = -sn*V[i][j] + cs*V[i][j+1];
                            V[i][j] = t;
                        }

                    t = hypot( f, g );
                    cs = f / t;
                    sn = g / t;
                    S[j] = t;
                    f = cs*e[j] + sn*S[j+1];
                    S[j+1] = -sn*e[j] + cs*S[j+1];
                    g = sn * e[j+1];
                    e[j+1] = cs * e[j+1];

                    if( wantu && ( j < m-1 ) )
                        for( i=0; i<m; ++i )
                        {
                            t = cs*U[i][j] + sn*U[i][j+1];
                            U[i][j+1] = -sn*U[i][j] + cs*U[i][j+1];
                            U[i][j] = t;
                        }
                }
                e[p-2] = f;
                iter = iter + 1;
            }
            break;

            // convergence
            case 4:
            {
                // Make the singular values positive.
                if( S[k] <= 0 )
                {
                    S[k] = ( S[k] < 0 ) ? -S[k] : 0;
                    if( wantv )
                        for( i=0; i<=pp; ++i )
                            V[i][k] = -V[i][k];
                }

                // Order the singular values.
                while( k < pp )
                {
                    if( S[k] >= S[k+1] )
                        break;

                    Real t = S[k];
                    S[k] = S[k+1];
                    S[k+1] = t;

                    if( wantv && ( k < n-1 ) )
                        for( i=0; i<n; ++i )
                            swap( V[i][k], V[i][k+1] );

                    if( wantu && ( k < m-1 ) )
                        for( i=0; i<m; ++i )
                            swap( U[i][k], U[i][k+1] );
                    k++;
                }
                iter = 0;
                p--;
            }
            break;
        }
    }
}


/**
 * Get the left singular vectors.
 */
template<typename Real>
inline Matrix<Real> SVD<Real>::getU() const
{
    return U;
}


/**
 * Get the singular values matrix.
 */
template<typename Real>
inline Matrix<Real> SVD<Real>::getSM()
{
    int N = S.size();
    Matrix<Real> tmp( N, N );
    for( int i=0; i<N; ++i )
        tmp[i][i] = S[i];

    return tmp;
}


/**
 * Get the singular values vector.
 */
template<typename Real>
inline Vector<Real> SVD<Real>::getSV() const
{
    return S;
}


/**
 * Get the right singular vectors.
 */
template<typename Real>
inline Matrix<Real> SVD<Real>::getV() const
{
    return V;
}


/**
 * Two norm (max(S)).
 */
template <typename Real>
inline Real SVD<Real>::norm2() const
{
    return S[0];
}


/**
 * Two norm of condition number (max(S)/min(S)).
 */
template <typename Real>
inline Real SVD<Real>::cond() const
{
    return ( S[0] / S(S.size()) );
}


/**
 * Effective numerical matrix rank.
 */
template <typename Real>
int SVD<Real>::rank()
{
    int N = S.size();
    double tol = N * S[0] * EPS;
    int r = 0;

    for( int i=0; i<N; ++i )
        if( S[i] > tol )
            r++;

    return r;
}

测试代码:

/*****************************************************************************
 *                               svd_test.cpp
 *
 * SVD class testing.
 *
 * Zhang Ming, 2010-01 (revised 2010-08), Xi'an Jiaotong University.
 *****************************************************************************/


#define BOUNDS_CHECK

#include <iostream>
#include <iomanip>
#include <svd.h>


using namespace std;
using namespace splab;


typedef double  Type;


int main()
{
//    Matrix<Type> A(4,4);
//	A(1,1) = 16;    A(1,2) = 2;     A(1,3) = 3;     A(1,4) = 13;
//	A(2,1) = 5;     A(2,2) = 11;    A(2,3) = 10;    A(2,4) = 8;
//	A(3,1) = 9;     A(3,2) = 7;     A(3,3) = 6;     A(3,4) = 12;
//	A(4,1) = 4;     A(4,2) = 14;    A(4,3) = 15;    A(4,4) = 1;

//    Matrix<Type> A(4,2);
//	A(1,1) = 1;     A(1,2) = 2;
//	A(2,1) = 3;     A(2,2) = 4;
//	A(3,1) = 5;     A(3,2) = 6;
//	A(4,1) = 7;     A(4,2) = 8;

    Matrix<Type> A(2,4);
	A(1,1) = 1;     A(1,2) = 3;     A(1,3) = 5;     A(1,4) = 7;
	A(2,1) = 2;     A(2,2) = 4;     A(2,3) = 6;     A(2,4) = 8;

	SVD<Type> svd;
	svd.dec(A);

	Matrix<Type> U = svd.getU();
	Matrix<Type> V = svd.getV();
	Matrix<Type> S = svd.getSM();

    cout << setiosflags(ios::fixed) << setprecision(4);
    cout << "Matrix--A: " << A << endl;
	cout << "Matrix--U: " << U << endl;
	cout << "Vector--S: " << S << endl;
	cout << "Matrix--V: " << V << endl;
    cout << "Matrix--A - U * S * V^T:  "
         << A- U*S*trT(V) << endl;
//         << A- U*multTr(S,V) << endl;

	cout << "The rank of A : " << svd.rank() << endl << endl;
	cout << "The condition number of A : " << svd.cond() << endl << endl;

	return 0;
}

运行结果:

Matrix--A: size: 2 by 4
1.0000  3.0000  5.0000  7.0000
2.0000  4.0000  6.0000  8.0000

Matrix--U: size: 2 by 2
0.6414  -0.7672
0.7672  0.6414

Vector--S: size: 2 by 2
14.2691 0.0000
0.0000  0.6268

Matrix--V: size: 4 by 2
0.1525  0.8226
0.3499  0.4214
0.5474  0.0201
0.7448  -0.3812

Matrix--A - U * S * V^T:  size: 2 by 4
-0.0000 0.0000  -0.0000 0.0000
-0.0000 -0.0000 -0.0000 -0.0000

The rank of A : 2

The condition number of A : 22.7640


Process returned 0 (0x0)   execution time : 0.094 s
Press any key to continue.

你可能感兴趣的:(实数矩阵SVD分解算法的C++实现)