矩阵SVD分解

1. 矩阵SVD分解: 代码主要来自(http://cacs.usc.edu/education/phys516/src/TB/svdcmp.c),此外,自己增加矩阵的释放函数和更加方便的接口函数,如下:

(1) 头文件

#ifndef CTMSVD_H
#define CTMSVD_H
/*******************************************************************************
Singular value decomposition program, svdcmp, from "Numerical Recipes in C"
(Cambridge Univ. Press) by W.H. Press, S.A. Teukolsky, W.T. Vetterling,
and B.P. Flannery
*******************************************************************************/

#include 
#include 
#include 

#define NR_END 1
#define FREE_ARG char*
#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
static double dmaxarg1,dmaxarg2;
#define DMAX(a,b) (dmaxarg1=(a),dmaxarg2=(b),(dmaxarg1) > (dmaxarg2) ? (dmaxarg1) : (dmaxarg2))
static int iminarg1,iminarg2;
#define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ? (iminarg1) : (iminarg2))

/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
double **dmatrix(int nrl, int nrh, int ncl, int nch)
{
    int i,nrow=nrh-nrl+1,ncol=nch-ncl+1;
    double **m;
    /* allocate pointers to rows */
    m= (double **) malloc((size_t)((nrow+NR_END)*sizeof(double*)));
    m += NR_END;
    m -= nrl;
    /* allocate rows and set pointers to them */
    m[nrl]=(double *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(double)));
    m[nrl] += NR_END;
    m[nrl] -= ncl;
    for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
    /* 初始化元素为0                                */
    for(int i = nrl; i <= nrh; ++i)
    {
        for(int j = ncl; j <= nch; ++j)
        {
            m[i][j] = 0;
        }
    }
    /* return pointer to array of pointers to rows */
    return m;
}

/* allocate a double vector with subscript range v[nl..nh] */
double *dvector(int nl, int nh)
{
    int i;
    double *v;
    v=(double *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(double)));
    /* 初始化元素为0    */
    for(i = nl; i <= nh; ++i)
    {
        v[i] = 0;
    }
    return v-nl+NR_END;
}

/* free a double vector allocated with dvector() */
void free_dvector(double *v, int nl, int nh)
{
    free((FREE_ARG) (v+nl-NR_END));
}

/* free a double matrix allocated with dmatrix() */
void free_dmatrix(double** m, int nrl)
{
    free((FREE_ARG) (m[nrl])); /* 释放元素部分 */
    free((FREE_ARG) *m);       /* 释放**      */
}

/* compute (a2 + b2)^1/2 without destructive underflow or overflow */
double pythag(double a, double b)
{
    double absa,absb;
    absa=fabs(a);
    absb=fabs(b);
    if (absa > absb) return absa*sqrt(1.0+(absb/absa)*(absb/absa));
    else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+(absa/absb)*(absa/absb)));
}

/******************************************************************************/
void svdcmp(double **a, int m, int n, double w[], double **v)
/*******************************************************************************
Given a matrix a[1..m][1..n], this routine computes its singular value
decomposition, A = U.W.VT.  The matrix U replaces a on output.  The diagonal
matrix of singular values W is output as a vector w[1..n].  The matrix V (not
the transpose VT) is output as v[1..n][1..n].
注意: (1) 要求a的矩阵元素必须满足: m = n
      (2) 不满足要求的矩阵可以用零填充
*******************************************************************************/
{
    int flag,i,its,j,jj,k,l,nm;
    double anorm,c,f,g,h,s,scale,x,y,z,*rv1;

    rv1=dvector(1,n);
    g=scale=anorm=0.0; /* Householder reduction to bidiagonal form */
    for (i=1;i<=n;i++) {
        l=i+1;
        rv1[i]=scale*g;
        g=s=scale=0.0;
        if (i <= m) {
            for (k=i;k<=m;k++) scale += fabs(a[k][i]);
            if (scale) {
                for (k=i;k<=m;k++) {
                    a[k][i] /= scale;
                    s += a[k][i]*a[k][i];
                }
                f=a[i][i];
                g = -SIGN(sqrt(s),f);
                h=f*g-s;
                a[i][i]=f-g;
                for (j=l;j<=n;j++) {
                    for (s=0.0,k=i;k<=m;k++) s += a[k][i]*a[k][j];
                    f=s/h;
                    for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
                }
                for (k=i;k<=m;k++) a[k][i] *= scale;
            }
        }
        w[i]=scale *g;
        g=s=scale=0.0;
        if (i <= m && i != n) {
            for (k=l;k<=n;k++) scale += fabs(a[i][k]);
            if (scale) {
                for (k=l;k<=n;k++) {
                    a[i][k] /= scale;
                    s += a[i][k]*a[i][k];
                }
                f=a[i][l];
                g = -SIGN(sqrt(s),f);
                h=f*g-s;
                a[i][l]=f-g;
                for (k=l;k<=n;k++) rv1[k]=a[i][k]/h;
                for (j=l;j<=m;j++) {
                    for (s=0.0,k=l;k<=n;k++) s += a[j][k]*a[i][k];
                    for (k=l;k<=n;k++) a[j][k] += s*rv1[k];
                }
                for (k=l;k<=n;k++) a[i][k] *= scale;
            }
        }
        anorm = DMAX(anorm,(fabs(w[i])+fabs(rv1[i])));
    }
    for (i=n;i>=1;i--) { /* Accumulation of right-hand transformations. */
        if (i < n) {
            if (g) {
                for (j=l;j<=n;j++) /* Double division to avoid possible underflow. */
                    v[j][i]=(a[i][j]/a[i][l])/g;
                for (j=l;j<=n;j++) {
                    for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j];
                    for (k=l;k<=n;k++) v[k][j] += s*v[k][i];
                }
            }
            for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0;
        }
        v[i][i]=1.0;
        g=rv1[i];
        l=i;
    }
    for (i=IMIN(m,n);i>=1;i--) { /* Accumulation of left-hand transformations. */
        l=i+1;
        g=w[i];
        for (j=l;j<=n;j++) a[i][j]=0.0;
        if (g) {
            g=1.0/g;
            for (j=l;j<=n;j++) {
                for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j];
                f=(s/a[i][i])*g;
                for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
            }
            for (j=i;j<=m;j++) a[j][i] *= g;
        } else for (j=i;j<=m;j++) a[j][i]=0.0;
        ++a[i][i];
    }

    for (k=n;k>=1;k--) { /* Diagonalization of the bidiagonal form. */
        for (its=1;its<=30;its++) {
            flag=1;
            for (l=k;l>=1;l--) { /* Test for splitting. */
                nm=l-1; /* Note that rv1[1] is always zero. */
                if ((double)(fabs(rv1[l])+anorm) == anorm) {
                    flag=0;
                    break;
                }
                if ((double)(fabs(w[nm])+anorm) == anorm) break;
            }
            if (flag) {
                c=0.0; /* Cancellation of rv1[l], if l > 1. */
                s=1.0;
                for (i=l;i<=k;i++) {
                    f=s*rv1[i];
                    rv1[i]=c*rv1[i];
                    if ((double)(fabs(f)+anorm) == anorm) break;
                    g=w[i];
                    h=pythag(f,g);
                    w[i]=h;
                    h=1.0/h;
                    c=g*h;
                    s = -f*h;
                    for (j=1;j<=m;j++) {
                        y=a[j][nm];
                        z=a[j][i];
                        a[j][nm]=y*c+z*s;
                        a[j][i]=z*c-y*s;
                    }
                }
            }
            z=w[k];
            if (l == k) { /* Convergence. */
                if (z < 0.0) { /* Singular value is made nonnegative. */
                    w[k] = -z;
                    for (j=1;j<=n;j++) v[j][k] = -v[j][k];
                }
                break;
            }
            if (its == 30) printf("no convergence in 30 svdcmp iterations\n");
            x=w[l]; /* Shift from bottom 2-by-2 minor. */
            nm=k-1;
            y=w[nm];
            g=rv1[nm];
            h=rv1[k];
            f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
            g=pythag(f,1.0);
            f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
            c=s=1.0; /* Next QR transformation: */
            for (j=l;j<=nm;j++) {
                i=j+1;
                g=rv1[i];
                y=w[i];
                h=s*g;
                g=c*g;
                z=pythag(f,h);
                rv1[j]=z;
                c=f/z;
                s=h/z;
                f=x*c+g*s;
                g = g*c-x*s;
                h=y*s;
                y *= c;
                for (jj=1;jj<=n;jj++) {
                    x=v[jj][j];
                    z=v[jj][i];
                    v[jj][j]=x*c+z*s;
                    v[jj][i]=z*c-x*s;
                }
                z=pythag(f,h);
                w[j]=z; /* Rotation can be arbitrary if z = 0. */
                if (z) {
                    z=1.0/z;
                    c=f*z;
                    s=h*z;
                }
                f=c*g+s*y;
                x=c*y-s*g;
                for (jj=1;jj<=m;jj++) {
                    y=a[jj][j];
                    z=a[jj][i];
                    a[jj][j]=y*c+z*s;
                    a[jj][i]=z*c-y*s;
                }
            }
            rv1[l]=0.0;
            rv1[k]=f;
            w[k]=x;
        }
    }
    free_dvector(rv1,1,n);
}


#endif // CTMSVD_H

(2)函数接口:


int matrix_svd(double *matrix1, int M, int N, bool isSort, bool isT, double *matrixU, double *matrixD, double *matrixV)
{
    int i,j, k;
    double t;
    int MN = M > N ? M : N;
    double** u = dmatrix(1, MN, 1, MN);
    double*  w = dvector(1, MN);
    double** v = dmatrix(1, MN, 1, MN);
    double*  t1 = dvector(1, MN);
    double*  t2 = dvector(1, MN);

    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < N; ++j)
        {
            u[i + 1][j + 1] = matrix1[i * N + j];
        }
    }


    svdcmp(u, MN, MN, w, v);

    /* Sort the singular values in descending order */
    if(isSort)
    {
        for (i = 1; i <= N; i++)
        {
            for (j = i+1; j <= N; j++)
            {
                if (w[i] < w[j])
                { /* 对特异值排序 */
                    t = w[i];
                    w[i] = w[j];
                    w[j] = t;
                    /* 同时也要把矩阵U,V的列位置交换 */
                    /* 矩阵U */
                    for (k = 1; k <= M; k++)
                    {
                        t1[k] = u[k][i];
                    }
                    for (k = 1; k <= M; k++)
                    {
                        u[k][i] = u[k][j];
                    }
                    for (k = 1; k <= M; k++)
                    {
                        u[k][j] = t1[k];
                    }

                    /* 矩阵V */
                    for (k = 1; k <= N; k++)
                    {
                        t2[k] = v[k][i];
                    }
                    for (k = 1; k <= N; k++)
                    {
                        v[k][i] = v[k][j];
                    }
                    for (k = 1; k <= N; k++)
                    {
                        v[k][j] = t2[k];
                    }
                }
            }
        }
    }

    /* 构造matrixD */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < N; ++j)
        {
            if(i == j)
            {
                matrixD[i * N + j] = w[i + 1];
            }
            else
            {
                matrixD[i * N + j]  = 0.0;
            }
            cout << matrixD[i * N + j] << ", ";
        }
        cout << endl;
    }


    /* 构造matrixU */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < M; ++j)
        {
            matrixU[i * M + j] = u[i + 1][j + 1];
            cout << matrixU[i * M + j] << ",";
        }
        cout << endl;
    }

    /* 构造matrixV: isT确定是否进行转置 */
    if(isT)
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[j * N + i] = v[i + 1][j + 1];  /* 转置 */
                cout << matrixV[i * N + j] << ",";
            }
            cout << endl;
        }
    }
    else
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[i * N + j] = v[i + 1][j + 1];
                cout << matrixV[i * N + j] << ",";
            }
            cout << endl;
        }
    }


    free_dmatrix(u, 1);
    free_dmatrix(v, 1);
    free_dvector(w,  1, MN);
    free_dvector(t1, 1, MN);
    free_dvector(t2, 1, MN);
    return 0;
}

2. 参考CSDN博主(http://blog.csdn.net/fengbingchun/article/details/72853757), 实现了另一个版本的SVD求解算法,同样包含一个头文件和接口函数,如下:

(1) 头文件

#ifndef CTMSVD2_H
#define CTMSVD2_H
#include 
#include 
#include 
#include 
#include 

template
static inline _Tp hypot(_Tp a, _Tp b)
{/* sqrt(x^2 + y^2): 但是可以避免上溢或下溢 */
    a = ABS(a);
    b = ABS(b);
    if (a > b)
    {
        b /= a;
        return a*sqrt(1 + b*b);
    }
    if (b > 0)
    {
        a /= b;
        return b*sqrt(1 + a*a);
    }
    return 0;
}

template
static void transpose(std::vector > src, std::vector >& dst)
{/* 转置 */
    if(!src.empty())
    {
        int M = src.size();     // 行数
        int N = src[0].size();  // 列数
        int i,j;
        for(i = 0; i < N; ++i)
        {
            std::vector<_Tp> rowData(M, 0); // dst: N x M
            dst.push_back(rowData);
        }

        for(i = 0; i < M; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                dst[j][i] = src[i][j];
            }
        }

    }
}

template
static void JacobiSVD(std::vector>& At,
    std::vector>& _W, std::vector>& Vt)
{
    double minval = FLT_MIN;
    _Tp eps = (_Tp)(FLT_EPSILON * 2);
    const int m = At[0].size();
    const int n = _W.size();
    const int n1 = m; // urows
    std::vector W(n, 0.);

    for (int i = 0; i < n; i++) {
        double sd{0.};
        for (int k = 0; k < m; k++) {
            _Tp t = At[i][k];
            sd += (double)t*t;
        }
        W[i] = sd;

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

    int max_iter = std::max(m, 30);
    for (int iter = 0; iter < max_iter; iter++) {
        bool changed = false;
        _Tp c, s;

        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                _Tp *Ai = At[i].data(), *Aj = At[j].data();
                double a = W[i], p = 0, b = W[j];

                for (int k = 0; k < m; k++)
                    p += (double)Ai[k] * Aj[k];

                if (std::abs(p) <= eps * std::sqrt((double)a*b))
                    continue;

                p *= 2;
                double beta = a - b, gamma = hypot((double)p, beta); // hypot_
                if (beta < 0) {
                    double delta = (gamma - beta)*0.5;
                    s = (_Tp)std::sqrt(delta / gamma);
                    c = (_Tp)(p / (gamma*s * 2));
                } else {
                    c = (_Tp)std::sqrt((gamma + beta) / (gamma * 2));
                    s = (_Tp)(p / (gamma*c * 2));
                }

                a = b = 0;
                for (int k = 0; k < m; k++) {
                    _Tp t0 = c*Ai[k] + s*Aj[k];
                    _Tp t1 = -s*Ai[k] + c*Aj[k];
                    Ai[k] = t0; Aj[k] = t1;

                    a += (double)t0*t0; b += (double)t1*t1;
                }
                W[i] = a; W[j] = b;

                changed = true;

                _Tp *Vi = Vt[i].data(), *Vj = Vt[j].data();

                for (int k = 0; k < n; k++) {
                    _Tp t0 = c*Vi[k] + s*Vj[k];
                    _Tp t1 = -s*Vi[k] + c*Vj[k];
                    Vi[k] = t0; Vj[k] = t1;
                }
            }
        }

        if (!changed)
            break;
    }

    for (int i = 0; i < n; i++) {
        double sd{ 0. };
        for (int k = 0; k < m; k++) {
            _Tp t = At[i][k];
            sd += (double)t*t;
        }
        W[i] = std::sqrt(sd);
    }

    for (int i = 0; i < n - 1; i++) {
        int j = i;
        for (int k = i + 1; k < n; k++) {
            if (W[j] < W[k])
                j = k;
        }
        if (i != j) {
            std::swap(W[i], W[j]);

            for (int k = 0; k < m; k++)
                std::swap(At[i][k], At[j][k]);

            for (int k = 0; k < n; k++)
                std::swap(Vt[i][k], Vt[j][k]);
        }
    }

    for (int i = 0; i < n; i++)
        _W[i][0] = (_Tp)W[i];

    srand(time(nullptr));

    for (int i = 0; i < n1; i++) {
        double sd = i < n ? W[i] : 0;

        for (int ii = 0; ii < 100 && sd <= minval; ii++) {
            // if we got a zero singular value, then in order to get the corresponding left singular vector
            // we generate a random vector, project it to the previously computed left singular vectors,
            // subtract the projection and normalize the difference.
            const _Tp val0 = (_Tp)(1. / m);
            for (int k = 0; k < m; k++) {
                unsigned int rng = rand() % 4294967295; // 2^32 - 1
                _Tp val = (rng & 256) != 0 ? val0 : -val0;
                At[i][k] = val;
            }
            for (int iter = 0; iter < 2; iter++) {
                for (int j = 0; j < i; j++) {
                    sd = 0;
                    for (int k = 0; k < m; k++)
                        sd += At[i][k] * At[j][k];
                    _Tp asum = 0;
                    for (int k = 0; k < m; k++) {
                        _Tp t = (_Tp)(At[i][k] - sd*At[j][k]);
                        At[i][k] = t;
                        asum += std::abs(t);
                    }
                    asum = asum > eps * 100 ? 1 / asum : 0;
                    for (int k = 0; k < m; k++)
                        At[i][k] *= asum;
                }
            }

            sd = 0;
            for (int k = 0; k < m; k++) {
                _Tp t = At[i][k];
                sd += (double)t*t;
            }
            sd = std::sqrt(sd);
        }

        _Tp s = (_Tp)(sd > minval ? 1 / sd : 0.);
        for (int k = 0; k < m; k++)
            At[i][k] *= s;
    }
}

// matSrc为原始矩阵,支持非方阵,
// matD存放奇异值
// matU存放左奇异向量
// matVt存放转置的右奇异向量
template
int svdcmp2(const std::vector>& matSrc,
    std::vector>& matD, std::vector>& matU, std::vector>& matVt)
{
    int m = matSrc.size();
    int n = matSrc[0].size();
    for (const auto& sz : matSrc) {
        if (n != sz.size()) {
            fprintf(stderr, "matrix dimension dismatch\n");
            return -1;
        }
    }

    bool at = false;
    if (m < n) {
        std::swap(m, n);
        at = true;
    }

    matD.resize(n);
    for (int i = 0; i < n; ++i) {
        matD[i].resize(1, (_Tp)0);
    }
    matU.resize(m);
    for (int i = 0; i < m; ++i) {
        matU[i].resize(m, (_Tp)0);
    }
    matVt.resize(n);
    for (int i = 0; i < n; ++i) {
        matVt[i].resize(n, (_Tp)0);
    }
    std::vector> tmp_u = matU, tmp_v = matVt;

    std::vector> tmp_a, tmp_a_;
    if (!at)
        transpose(matSrc, tmp_a);
    else
        tmp_a = matSrc;

    if (m == n) {
        tmp_a_ = tmp_a;
    } else {
        tmp_a_.resize(m);
        for (int i = 0; i < m; ++i) {
            tmp_a_[i].resize(m, (_Tp)0);
        }
        for (int i = 0; i < n; ++i) {
            tmp_a_[i].assign(tmp_a[i].begin(), tmp_a[i].end());
        }
    }
    JacobiSVD(tmp_a_, matD, tmp_v);

    if (!at) {
        transpose(tmp_a_, matU);
        matVt = tmp_v;
    } else {
        transpose(tmp_v, matU);
        matVt = tmp_a_;
    }

    return 0;
}


#endif // CTMSVD2_H

(2)接口函数


int matrix_svd2(double *matrix1, int M, int N, bool isSort, bool isT, double *matrixU, double *matrixD, double *matrixV)
{
    vector< vector > A;
    vector< vector > U;  // M x M
    vector< vector > Vt; // N x N
    vector< vector > D;  // M x N

    int i, j;
    for(i = 0; i < M; ++i)
    {
        vector rowData(N, 0);
        double* pRow = rowData.data();
        memcpy(pRow, matrix1 + i * N, sizeof(double) * N);
        A.push_back(rowData);
    }

    svdcmp2(A, D, U, Vt);

    int minMN = M > N ? N : M;

    /* 构造matrixD */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < N; ++j)
        {
            if(i < minMN && j < minMN && i == j)
            {
                matrixD[i * N + j] = D[i][0];
            }
            else
            {
                matrixD[i * N + j] = 0;
            }
            // cout << matrixD[i * N + j] << ", ";
        }
        // cout << endl;
    }

    /* 构造matrixU */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < M; ++j)
        {
            matrixU[i * M + j] = U[i][j];
            // cout << matrixU[i * M + j] << ", ";
        }
        // cout << endl;
    }

    /* 构造matrixV */
    if(isT)
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[i * N + j] = Vt[i][j]; /*  返回Vt */
                // cout << matrixV[i * N + j] << ", ";
            }
            // cout << endl;
        }
    }
    else
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[j * N + i] = Vt[i][j]; /*  返回V */
                // cout << matrixV[i * N + j] << ", ";
            }
            // cout << endl;
        }
    }

    if(isSort)
    {
        for(i = 0; i < minMN; ++i)
        {
            double svdVal = matrixD[i];
            int    index  = i;
            for(j = 0; j < minMN; ++j)
            {
                if(svdVal < matrixD[i * minMN + i])
                {
                    index = j;
                }
            }
            if(i != j)
            {
                int k;
                // matrixD 对角元素交换
                std::swap(matrixD[i * M + i], matrixD[index * M + index]);

                // matrixU 行交换
                for(k = 0; k < M; ++k)
                {
                    std::swap(matrixU[i * M + k], matrixU[index * M + k]);
                }

                // matrixV 列变换
                for(k = 0; k < N; ++k)
                {
                    std::swap(matrixV[k * N + i], matrixV[k * N + index]);
                }
            }
        }
    }

    return 0;
}

3. 测试准确性

#include  
#include 
#include 
#include 
#include 
#include 
#include 
// #include 
#include 
#include "ctmsvd.h"
#include "ctmsvd2.h"

using namespace std;


int vector_conv(double *vec1, int M, double *vec2, int N, double *vec3)
{
    int nlen = M + N - 1;
    double* xx = new double[nlen];
    int i,j;
    for(i = 0; i < nlen; ++i)
    {
        xx[i] = 0;
        for(j = 0; j < M; ++j)
        {
            if(i - j >= 0 && (i - j) < N)
            {
                xx[i] += vec1[j] * vec2[i - j];
            }
        }
    }

    for(i = 0; i < nlen; ++i)
    {
        vec3[i] = xx[i];
    }

    return 0;
}


int matrix_svd(double *matrix1, int M, int N, bool isSort, bool isT, double *matrixU, double *matrixD, double *matrixV)
{
    int i,j, k;
    double t;
    int MN = M > N ? M : N;
    double** u = dmatrix(1, MN, 1, MN);
    double*  w = dvector(1, MN);
    double** v = dmatrix(1, MN, 1, MN);
    double*  t1 = dvector(1, MN);
    double*  t2 = dvector(1, MN);

    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < N; ++j)
        {
            u[i + 1][j + 1] = matrix1[i * N + j];
        }
    }


    svdcmp(u, MN, MN, w, v);

    /* Sort the singular values in descending order */
    if(isSort)
    {
        for (i = 1; i <= N; i++)
        {
            for (j = i+1; j <= N; j++)
            {
                if (w[i] < w[j])
                { /* 对特异值排序 */
                    t = w[i];
                    w[i] = w[j];
                    w[j] = t;
                    /* 同时也要把矩阵U,V的列位置交换 */
                    /* 矩阵U */
                    for (k = 1; k <= M; k++)
                    {
                        t1[k] = u[k][i];
                    }
                    for (k = 1; k <= M; k++)
                    {
                        u[k][i] = u[k][j];
                    }
                    for (k = 1; k <= M; k++)
                    {
                        u[k][j] = t1[k];
                    }

                    /* 矩阵V */
                    for (k = 1; k <= N; k++)
                    {
                        t2[k] = v[k][i];
                    }
                    for (k = 1; k <= N; k++)
                    {
                        v[k][i] = v[k][j];
                    }
                    for (k = 1; k <= N; k++)
                    {
                        v[k][j] = t2[k];
                    }
                }
            }
        }
    }

    /* 构造matrixD */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < N; ++j)
        {
            if(i == j)
            {
                matrixD[i * N + j] = w[i + 1];
            }
            else
            {
                matrixD[i * N + j]  = 0.0;
            }
            cout << matrixD[i * N + j] << ", ";
        }
        cout << endl;
    }


    /* 构造matrixU */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < M; ++j)
        {
            matrixU[i * M + j] = u[i + 1][j + 1];
            cout << matrixU[i * M + j] << ",";
        }
        cout << endl;
    }

    /* 构造matrixV: isT确定是否进行转置 */
    if(isT)
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[j * N + i] = v[i + 1][j + 1];  /* 转置 */
                cout << matrixV[i * N + j] << ",";
            }
            cout << endl;
        }
    }
    else
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[i * N + j] = v[i + 1][j + 1];
                cout << matrixV[i * N + j] << ",";
            }
            cout << endl;
        }
    }


    free_dmatrix(u, 1);
    free_dmatrix(v, 1);
    free_dvector(w,  1, MN);
    free_dvector(t1, 1, MN);
    free_dvector(t2, 1, MN);
    return 0;
}


int matrix_svd2(double *matrix1, int M, int N, bool isSort, bool isT, double *matrixU, double *matrixD, double *matrixV)
{
    vector< vector > A;
    vector< vector > U;  // M x M
    vector< vector > Vt; // N x N
    vector< vector > D;  // M x N

    int i, j;
    for(i = 0; i < M; ++i)
    {
        vector rowData(N, 0);
        double* pRow = rowData.data();
        memcpy(pRow, matrix1 + i * N, sizeof(double) * N);
        A.push_back(rowData);
    }

    svdcmp2(A, D, U, Vt);

    int minMN = M > N ? N : M;

    /* 构造matrixD */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < N; ++j)
        {
            if(i < minMN && j < minMN && i == j)
            {
                matrixD[i * N + j] = D[i][0];
            }
            else
            {
                matrixD[i * N + j] = 0;
            }
            // cout << matrixD[i * N + j] << ", ";
        }
        // cout << endl;
    }

    /* 构造matrixU */
    for(i = 0; i < M; ++i)
    {
        for(j = 0; j < M; ++j)
        {
            matrixU[i * M + j] = U[i][j];
            // cout << matrixU[i * M + j] << ", ";
        }
        // cout << endl;
    }

    /* 构造matrixV */
    if(isT)
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[i * N + j] = Vt[i][j]; /*  返回Vt */
                // cout << matrixV[i * N + j] << ", ";
            }
            // cout << endl;
        }
    }
    else
    {
        for(i = 0; i < N; ++i)
        {
            for(j = 0; j < N; ++j)
            {
                matrixV[j * N + i] = Vt[i][j]; /*  返回V */
                // cout << matrixV[i * N + j] << ", ";
            }
            // cout << endl;
        }
    }

    if(isSort)
    {
        for(i = 0; i < minMN; ++i)
        {
            double svdVal = matrixD[i];
            int    index  = i;
            for(j = 0; j < minMN; ++j)
            {
                if(svdVal < matrixD[i * minMN + i])
                {
                    index = j;
                }
            }
            if(i != j)
            {
                int k;
                // matrixD 对角元素交换
                std::swap(matrixD[i * M + i], matrixD[index * M + index]);

                // matrixU 行交换
                for(k = 0; k < M; ++k)
                {
                    std::swap(matrixU[i * M + k], matrixU[index * M + k]);
                }

                // matrixV 列变换
                for(k = 0; k < N; ++k)
                {
                    std::swap(matrixV[k * N + i], matrixV[k * N + index]);
                }
            }
        }
    }

    return 0;
}

int main()
{
    double vec1[5] = {1, 2, 3, 4, 5};
    double vec2[6] = {1, 2, 3, 4, 5, 6};
    double vec3[10] = {0};
    vector_conv(vec1, 5, vec2, 6, vec3);
    int i;
    for(i = 0; i < 10; ++i)
    {
        cout << vec3[i] << ",";
    }
    cout << endl;

    // 场景1: M > N
    double matrix[12] = { 1.4090 , 0.7172  , 0.7269,
                          1.4172 , 1.6302, -0.3034,
                          0.6715 ,  0.4889 ,  0.2939,
                          -1.2075  , 1.0347 , -0.7873};
    double matrixU1[16] = {0};
    double matrixD1[12] = {0};
    double matrixV1[9] = {0};

    std::cout << "========== case 1: M > N ==========" << std::endl;
    matrix_svd(matrix, 4, 3, true, false, matrixU1, matrixD1, matrixV1);
    std::cout << "===================================" << std::endl;
    matrix_svd2(matrix, 4, 3, true, false, matrixU1, matrixD1, matrixV1);
    std::cout << "============= case 1 End =============" << std::endl;

    // 场景2: M < N
    double matrix2[12] = { 1.4090 , 0.7172  , 0.7269,
                           1.4172 , 1.6302, -0.3034,
                           0.6715 ,  0.4889 ,  0.2939,
                           -1.2075  , 1.0347 , -0.7873};
    double matrixU2[16] = {0};
    double matrixD2[12] = {0};
    double matrixV2[9]  = {0};

    std::cout << "========== case 1: M < N ==========" << std::endl;
    matrix_svd(matrix2, 3, 4, true, false, matrixU2, matrixD2, matrixV2);
    std::cout << "===================================" << std::endl;
    matrix_svd2(matrix2, 3, 4, true, false, matrixU1, matrixD1, matrixV1);
    std::cout << "============= case 2 End =============" << std::endl;

    return 0;
}
结果可以同matlab的svd函数进行对比:


你可能感兴趣的:(c++)