SVD(奇异值分解)算法_计算任意N*M矩阵_C语言代码

来源:互联网 发布:淘宝怎样用百联ok卡用 编辑:程序博客网 时间:2024/06/05 04:59

帮同学解决的一个问题。

SVD(奇异值分解算法)的C语言代码在网上可以找到,如《Numerical Recipes in C》一书所给出的代码:http://cacs.usc.edu/education/phys516/src/TB/svdcmp.c,同时也给出了Demo代码http://cacs.usc.edu/education/phys516/src/TB/singular.c。但是该Demo代码只能计算N*N矩阵,计算M*N(即M、N不相等)矩阵时就会出错。

我对singular.c进行了修改,svdcmp.c未修改,从而可以计算任意M*N矩阵。主要是发现了一个解决途径,在下面的代码中有作注释。因为对该算法研究不深入,不能保证一定正确,但测试了6组数据,本程序的运行结果与Matlab的运行结果是一致的。

文件singular.c中已给出了2组测试数据,测试数据是3X3矩阵,可以自行更改M、N值使得其在运行时变成2X3矩阵等。并且程序最后对结果进行了检验,是否正确一看便知。

将两个文件放在同一目录下,在 Linux 中可通过 gcc 命令进行编译:

gcc ./singular.c ./svdcmp.c -o ./singular

编译完成后,执行 ./singular 就可以运行了。

文件 singular.c :

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #define M 2
  5. #define N 3 // 输入的矩阵规模
  6. #if (M>N) // 取M、N中的较大数
  7. #define MN M
  8. #else
  9. #define MN N
  10. #endif
  11.  
  12. double **dmatrix(int, int, int, int);
  13. double *dvector(int, int);
  14. void svdcmp(double **, int, int, double *, double **);
  15. void print_r(double **a, int m, int n) {
  16. int i, j;
  17. for (i = 1; i <= m; i++) {
  18. for (j = 1; j <= n; j++) {
  19. printf("%le ", a[i][j]);
  20. }
  21. printf("\n");
  22. }
  23. }
  24.  
  25. int main() {
  26. /*
  27. * 使用 ** 来声明,然后用dmatrix来申请空间
  28. * 这样在函数引用矩阵时,就不用声明矩阵大小了
  29. */
  30. double **a;
  31. double *w;
  32. double **u,**v;
  33. int i,j,k;
  34. double t;
  35. double t1[MN],t2[MN];
  36.  
  37. /* 矩阵均以M,N中的最大值来申请空间,避免越界 */
  38. a = dmatrix(1, MN, 1, MN);
  39. u = dmatrix(1, MN, 1, MN);
  40. w = dvector(1, MN);
  41. v = dmatrix(1, MN, 1, MN);
  42.  
  43. a[1][1] = 3.0;
  44. a[1][2] = 2.0;
  45. a[1][3] = 1.0;
  46. a[2][1] = 1.0;
  47. a[2][2] = 3.0;
  48. a[2][3] = 2.0;
  49. a[3][1] = 1.0;
  50. a[3][2] = 2.0;
  51. a[3][3] = 3.0;
  52.  
  53.  
  54. /* 备选验证数据 */
  55. // a[1][1] = 1.0;
  56. // a[1][2] = 2.0;
  57. // a[1][3] = 3.0;
  58. // a[2][1] = 4.0;
  59. // a[2][2] = 5.0;
  60. // a[2][3] = 6.0;
  61. // a[3][1] = 7.0;
  62. // a[3][2] = 8.0;
  63. // a[3][3] = 9.0;
  64.  
  65.  
  66. printf("=== a ===\n");
  67. print_r(a, M, N);
  68.  
  69. /*
  70. * 执行svdcmp后,结果U会存储在传入的矩阵a中,
  71. * 所以先复制矩阵a存入u中,然后在svdcmp中传入u,
  72. * 这样输出就保存在u中,矩阵a也不会变了
  73. */
  74. for (i = 1; i <= M; i++) {
  75. for (j = 1; j <= N; j++)
  76. u[i][j] = a[i][j];
  77. }
  78.  
  79. // svdcmp(u, M, N, w, v);
  80. /*
  81. * 这边理应传入的是上面的参数
  82. * 但当 M>N时,结果不正确,U最右边的几列会全为0
  83. * 应该是Householder那边计算的问题,因为Householder外循环参数为N?
  84. *
  85. * 使用下面这个参数,经过验证可以得到正确的结果...
  86. *
  87. * 矩阵初始化时,元素默认赋值0,那就是说,0元素不影响Housesholder计算?后续的迭代也不影响?
  88. * 比如 2X2矩阵A[1,2; 2,3] 计算结果是2x2矩阵U
  89. * 那把矩阵改成3X3矩阵A[1,2,0; 2,3,0; 0,0,0],即多出来的元素用0填充
  90. * 然后经householder计算和迭代计算后,结果U虽然是3X3矩阵,但我们还是只取2X2矩阵,两者结果是一样的?
  91. *
  92. * 用Matlab验证了下,还真的是一样的... 0元素不影响计算结果
  93. * 改成3x3矩阵后,U的第三列是有值的,但可以忽略掉,取前面的2x2矩阵,这样结果是一致的。
  94. */
  95. svdcmp(u, MN, MN, w, v);
  96.  
  97.  
  98. /* Sort the singular values in descending order */
  99. for (i = 1; i <= N; i++) {
  100. for (j = i+1; j <= N; j++) {
  101. if (w[i] < w[j]) { /* 对特异值排序 */
  102. t = w[i];
  103. w[i] = w[j];
  104. w[j] = t;
  105. /* 同时也要把矩阵U,V的列位置交换 */
  106. /* 矩阵U */
  107. for (k = 1; k <= M; k++) {
  108. t1[k] = u[k][i];
  109. }
  110. for (k = 1; k <= M; k++) {
  111. u[k][i] = u[k][j];
  112. }
  113. for (k = 1; k <= M; k++) {
  114. u[k][j] = t1[k];
  115. }
  116.  
  117. /* 矩阵V */
  118. for (k = 1; k <= N; k++) {
  119. t2[k] = v[k][i];
  120. }
  121. for (k = 1; k <= N; k++) {
  122. v[k][i] = v[k][j];
  123. }
  124. for (k = 1; k <= N; k++) {
  125. v[k][j] = t2[k];
  126. }
  127. }
  128. }
  129. }
  130.  
  131. /* U为MxM矩阵 */
  132. printf("=== U ===\n");
  133. print_r(u, M, M);
  134.  
  135. /* 奇异值有M个,存为W矩阵,后面会用到 */
  136. double **W;
  137. W = dmatrix(1, MN, 1, MN);
  138. printf("=== W ===\n");
  139. for (i = 1; i<= M; i++) {
  140. for (j =1; j <= N; j++) {
  141. if (i==j) {
  142. W[i][j] = w[i];
  143. } else {
  144. W[i][j] = 0.0;
  145. }
  146. }
  147. }
  148. print_r(W, M, N);
  149.  
  150. /* V为NxN矩阵 */
  151. printf("=== V ===\n");
  152. print_r(v, N, N);
  153.  
  154. /* 验证结果,即计算U*W*V’看是否等于a */
  155. printf("=== U*W*V' ===\n");
  156. double **temp;
  157. double sum;
  158. temp = dmatrix(1, MN, 1, MN);
  159. /* 先算 U*W */
  160. for(i = 1; i <= M; i++){
  161. for(j = 1; j <= M; j++){
  162. sum = 0;
  163. for(k = 1; k <= N; k++){
  164. sum += u[i][k] * W[k][j];
  165. }
  166. temp[i][j] = sum;
  167. }
  168. }
  169. /* 再算 temp*V */
  170. /* 先对v进行矩阵转置,存为V */
  171. double ** V;
  172. V = dmatrix(1, MN, 1, MN);
  173. for(i = 1; i <= N; i++) {
  174. for(j = 1; j <= N; j++) {
  175. V[i][j] = v[j][i];
  176. }
  177. }
  178. for(i = 1; i <= M; i++){
  179. for(j = 1; j <= N; j++){
  180. sum = 0;
  181. for(k = 1; k <= N; k++){
  182. sum += temp[i][k] * V[k][j];
  183. }
  184. printf("%le ", sum);
  185. }
  186. printf("\n");
  187. }
  188.  
  189.  
  190.  
  191. for (i = 1; i <= N; i++) {
  192. printf("Sigular value %d = %le\n",i,w[i]);
  193. printf(" vector = %le %le %le\n",u[1][i],u[2][i],u[3][i]);
  194. }
  195.  
  196.  
  197. system("pause");
  198. return 0;
  199. }
C 语言

文件 svdcmp.c ,未作修改:

  1. /*******************************************************************************
  2. Singular value decomposition program, svdcmp, from "Numerical Recipes in C"
  3. (Cambridge Univ. Press) by W.H. Press, S.A. Teukolsky, W.T. Vetterling,
  4. and B.P. Flannery
  5. *******************************************************************************/
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <math.h>
  9.  
  10. #define NR_END 1
  11. #define FREE_ARG char*
  12. #define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
  13. static double dmaxarg1,dmaxarg2;
  14. #define DMAX(a,b) (dmaxarg1=(a),dmaxarg2=(b),(dmaxarg1) > (dmaxarg2) ? (dmaxarg1) : (dmaxarg2))
  15. static int iminarg1,iminarg2;
  16. #define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ? (iminarg1) : (iminarg2))
  17.  
  18. double **dmatrix(int nrl, int nrh, int ncl, int nch)
  19. /* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
  20. {
  21. int i,nrow=nrh-nrl+1,ncol=nch-ncl+1;
  22. double **m;
  23. /* allocate pointers to rows */
  24. m=(double **) malloc((size_t)((nrow+NR_END)*sizeof(double*)));
  25. m += NR_END;
  26. m -= nrl;
  27. /* allocate rows and set pointers to them */
  28. m[nrl]=(double *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(double)));
  29. m[nrl] += NR_END;
  30. m[nrl] -= ncl;
  31. for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
  32. /* return pointer to array of pointers to rows */
  33. return m;
  34. }
  35.  
  36. double *dvector(int nl, int nh)
  37. /* allocate a double vector with subscript range v[nl..nh] */
  38. {
  39. double *v;
  40. v=(double *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(double)));
  41. return v-nl+NR_END;
  42. }
  43.  
  44. void free_dvector(double *v, int nl, int nh)
  45. /* free a double vector allocated with dvector() */
  46. {
  47. free((FREE_ARG) (v+nl-NR_END));
  48. }
  49.  
  50. double pythag(double a, double b)
  51. /* compute (a2 + b2)^1/2 without destructive underflow or overflow */
  52. {
  53. double absa,absb;
  54. absa=fabs(a);
  55. absb=fabs(b);
  56. if (absa > absb) return absa*sqrt(1.0+(absb/absa)*(absb/absa));
  57. else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+(absa/absb)*(absa/absb)));
  58. }
  59.  
  60. /******************************************************************************/
  61. void svdcmp(double **a, int m, int n, double w[], double **v)
  62. /*******************************************************************************
  63. Given a matrix a[1..m][1..n], this routine computes its singular value
  64. decomposition, A = U.W.VT. The matrix U replaces a on output. The diagonal
  65. matrix of singular values W is output as a vector w[1..n]. The matrix V (not
  66. the transpose VT) is output as v[1..n][1..n].
  67. *******************************************************************************/
  68. {
  69. int flag,i,its,j,jj,k,l,nm;
  70. double anorm,c,f,g,h,s,scale,x,y,z,*rv1;
  71.  
  72. rv1=dvector(1,n);
  73. g=scale=anorm=0.0; /* Householder reduction to bidiagonal form */
  74. for (i=1;i<=n;i++) {
  75. l=i+1;
  76. rv1[i]=scale*g;
  77. g=s=scale=0.0;
  78. if (i <= m) {
  79. for (k=i;k<=m;k++) scale += fabs(a[k][i]);
  80. if (scale) {
  81. for (k=i;k<=m;k++) {
  82. a[k][i] /= scale;
  83. s += a[k][i]*a[k][i];
  84. }
  85. f=a[i][i];
  86. g = -SIGN(sqrt(s),f);
  87. h=f*g-s;
  88. a[i][i]=f-g;
  89. for (j=l;j<=n;j++) {
  90. for (s=0.0,k=i;k<=m;k++) s += a[k][i]*a[k][j];
  91. f=s/h;
  92. for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
  93. }
  94. for (k=i;k<=m;k++) a[k][i] *= scale;
  95. }
  96. }
  97. w[i]=scale *g;
  98. g=s=scale=0.0;
  99. if (i <= m && i != n) {
  100. for (k=l;k<=n;k++) scale += fabs(a[i][k]);
  101. if (scale) {
  102. for (k=l;k<=n;k++) {
  103. a[i][k] /= scale;
  104. s += a[i][k]*a[i][k];
  105. }
  106. f=a[i][l];
  107. g = -SIGN(sqrt(s),f);
  108. h=f*g-s;
  109. a[i][l]=f-g;
  110. for (k=l;k<=n;k++) rv1[k]=a[i][k]/h;
  111. for (j=l;j<=m;j++) {
  112. for (s=0.0,k=l;k<=n;k++) s += a[j][k]*a[i][k];
  113. for (k=l;k<=n;k++) a[j][k] += s*rv1[k];
  114. }
  115. for (k=l;k<=n;k++) a[i][k] *= scale;
  116. }
  117. }
  118. anorm = DMAX(anorm,(fabs(w[i])+fabs(rv1[i])));
  119. }
  120. for (i=n;i>=1;i--) { /* Accumulation of right-hand transformations. */
  121. if (i < n) {
  122. if (g) {
  123. for (j=l;j<=n;j++) /* Double division to avoid possible underflow. */
  124. v[j][i]=(a[i][j]/a[i][l])/g;
  125. for (j=l;j<=n;j++) {
  126. for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j];
  127. for (k=l;k<=n;k++) v[k][j] += s*v[k][i];
  128. }
  129. }
  130. for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0;
  131. }
  132. v[i][i]=1.0;
  133. g=rv1[i];
  134. l=i;
  135. }
  136. for (i=IMIN(m,n);i>=1;i--) { /* Accumulation of left-hand transformations. */
  137. l=i+1;
  138. g=w[i];
  139. for (j=l;j<=n;j++) a[i][j]=0.0;
  140. if (g) {
  141. g=1.0/g;
  142. for (j=l;j<=n;j++) {
  143. for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j];
  144. f=(s/a[i][i])*g;
  145. for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
  146. }
  147. for (j=i;j<=m;j++) a[j][i] *= g;
  148. } else for (j=i;j<=m;j++) a[j][i]=0.0;
  149. ++a[i][i];
  150. }
  151.  
  152. for (k=n;k>=1;k--) { /* Diagonalization of the bidiagonal form. */
  153. for (its=1;its<=30;its++) {
  154. flag=1;
  155. for (l=k;l>=1;l--) { /* Test for splitting. */
  156. nm=l-1; /* Note that rv1[1] is always zero. */
  157. if ((double)(fabs(rv1[l])+anorm) == anorm) {
  158. flag=0;
  159. break;
  160. }
  161. if ((double)(fabs(w[nm])+anorm) == anorm) break;
  162. }
  163. if (flag) {
  164. c=0.0; /* Cancellation of rv1[l], if l > 1. */
  165. s=1.0;
  166. for (i=l;i<=k;i++) {
  167. f=s*rv1[i];
  168. rv1[i]=c*rv1[i];
  169. if ((double)(fabs(f)+anorm) == anorm) break;
  170. g=w[i];
  171. h=pythag(f,g);
  172. w[i]=h;
  173. h=1.0/h;
  174. c=g*h;
  175. s = -f*h;
  176. for (j=1;j<=m;j++) {
  177. y=a[j][nm];
  178. z=a[j][i];
  179. a[j][nm]=y*c+z*s;
  180. a[j][i]=z*c-y*s;
  181. }
  182. }
  183. }
  184. z=w[k];
  185. if (l == k) { /* Convergence. */
  186. if (z < 0.0) { /* Singular value is made nonnegative. */
  187. w[k] = -z;
  188. for (j=1;j<=n;j++) v[j][k] = -v[j][k];
  189. }
  190. break;
  191. }
  192. if (its == 30) printf("no convergence in 30 svdcmp iterations\n");
  193. x=w[l]; /* Shift from bottom 2-by-2 minor. */
  194. nm=k-1;
  195. y=w[nm];
  196. g=rv1[nm];
  197. h=rv1[k];
  198. f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
  199. g=pythag(f,1.0);
  200. f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
  201. c=s=1.0; /* Next QR transformation: */
  202. for (j=l;j<=nm;j++) {
  203. i=j+1;
  204. g=rv1[i];
  205. y=w[i];
  206. h=s*g;
  207. g=c*g;
  208. z=pythag(f,h);
  209. rv1[j]=z;
  210. c=f/z;
  211. s=h/z;
  212. f=x*c+g*s;
  213. g = g*c-x*s;
  214. h=y*s;
  215. y *= c;
  216. for (jj=1;jj<=n;jj++) {
  217. x=v[jj][j];
  218. z=v[jj][i];
  219. v[jj][j]=x*c+z*s;
  220. v[jj][i]=z*c-x*s;
  221. }
  222. z=pythag(f,h);
  223. w[j]=z; /* Rotation can be arbitrary if z = 0. */
  224. if (z) {
  225. z=1.0/z;
  226. c=f*z;
  227. s=h*z;
  228. }
  229. f=c*g+s*y;
  230. x=c*y-s*g;
  231. for (jj=1;jj<=m;jj++) {
  232. y=a[jj][j];
  233. z=a[jj][i];
  234. a[jj][j]=y*c+z*s;
  235. a[jj][i]=z*c-y*s;
  236. }
  237. }
  238. rv1[l]=0.0;
  239. rv1[k]=f;
  240. w[k]=x;
  241. }
  242. }
  243. free_dvector(rv1,1,n);
  244. }
0 0
原创粉丝点击