【问题标题】:One-sided Jacobi implementation of SVDSVD 的单面 Jacobi 实现
【发布时间】:2018-11-17 14:49:48
【问题描述】:

我正在尝试编写奇异值分解 (SVD) 的简单实现。我正在使用单边 Jacobi 算法,因为它看起来很简单。该算法描述为here,并且有一个简单的Matlab 代码用于here(练习4)。我已经实现了相同的代码,它工作正常,我的意思是 SVD(A) = U * S * V' (或任何其他使用的符号),对于某些矩阵,结果与 Matlab 的 SVD 产生的结果相同(除了这个函数不对奇异值进行排序)。但我的问题是,当矩阵 A 的奇异值为 0 时,U 和 V 不再是应有的酉矩阵!

有没有办法更新这个算法,使其也适用于奇异值为 0 的情况?如果没有,是否还有另一种易于实现的 SVD 算法? 任何帮助表示赞赏。

这是我的 Matlab 代码,与上面链接中的代码基本相同,刚刚完成并稍作改动。

function [U,S,V] = jacobi_SVD(A)
  TOL=1.e-4;
  n=size(A,1);
  U=A';
  V=eye(n);
  converge=TOL+1;
  while converge>TOL
  converge=0;
  for i=1:n-1
    for j=i+1:n
      % compute [alpha gamma;gamma beta]=(i,j) submatrix of U*U'
      alpha=sumsqr(U(i, :)); 
      beta=sumsqr(U(j, :));
      gamma=sum(U(i, :).* U(j, :));
      converge=max(converge,abs(gamma)/sqrt(alpha*beta));

      % compute Jacobi rotation that diagonalizes 
      %    [alpha gamma;gamma beta]
      zeta=(beta-alpha)/(2*gamma);
      t=sign(zeta)/(abs(zeta)+sqrt(1+zeta^2));
      c= 1.0 / (sqrt(1 + t * t));
      s= c * t;

      % update columns i and j of U
      t=U(i, :);
      U(i, :)=c*t-s*U(j, :);
      U(j, :)=s*t+c*U(j, :);

      % update matrix V of right singular vectors
      t=V(i, :);
      V(i, :)=c*t-s*V(j, :);
      V(j, :)=s*t+c*V(j, :);
    end
  end
end

% the singular values are the norms of the columns of U
% the left singular vectors are the normalized columns of U
for j=1:n
  singvals(j)=norm(U(j, :));
  U(j, :)=U(j, :)/singvals(j);
end
S=diag(singvals);
U = U';
V = V'; %return V, not V'
end

【问题讨论】:

    标签: matlab svd


    【解决方案1】:

    我无法让您的代码运行,因为当您评估 alpha 和 beta 时,您有未定义的 sumsq。我在使用 QR 分解 (Gram-schmidt) 的 Matlab website. 上发现了一些简单的代码。

    function [u,s,v] = svdsim(a,tol)
    %SVDSIM  simple SVD program
    %
    % A simple program that demonstrates how to use the
    % QR decomposition to perform the SVD of a matrix.
    % A may be rectangular and complex.
    %
    % usage: [U,S,V]= SVDSIM(A)
    %     or      S = SVDSIM(A)
    %
    % with A = U*S*V' , S>=0 , U'*U = Iu  , and V'*V = Iv
    %
    % The idea is to use the QR decomposition on A to gradually "pull" U out from
    % the left and then use QR on A transposed to "pull" V out from the right.
    % This process makes A lower triangular and then upper triangular alternately.
    % Eventually, A becomes both upper and lower triangular at the same time,
    % (i.e. Diagonal) with the singular values on the diagonal.
    %
    % Matlab's own SVD routine should always be the first choice to use,
    % but this routine provides a simple "algorithmic alternative"
    % depending on the users' needs.
    % 
    %see also: SVD, EIG, QR, BIDIAG, HESS
    %
    
    % Paul Godfrey
    % October 23, 2006
    
    if ~exist('tol','var')
       tol=eps*1024;
    end
    
    %reserve space in advance
    sizea=size(a);
    loopmax=100*max(sizea);
    loopcount=0;
    
    % or use Bidiag(A) to initialize U, S, and V
    u=eye(sizea(1));
    s=a';
    v=eye(sizea(2));
    
    Err=realmax;
    while Err>tol & loopcount<loopmax ;
    %   log10([Err tol loopcount loopmax]); pause
        [q,s]=qr(s'); u=u*q;
        [q,s]=qr(s'); v=v*q;
    
    % exit when we get "close"
        e=triu(s,1);
        E=norm(e(:));
        F=norm(diag(s));
        if F==0, F=1;end
        Err=E/F;
        loopcount=loopcount+1;
    end
    % [Err/tol loopcount/loopmax]
    
    %fix the signs in S
    ss=diag(s);
    s=zeros(sizea);
    for n=1:length(ss)
        ssn=ss(n);
        s(n,n)=abs(ssn);
        if ssn<0
           u(:,n)=-u(:,n);
        end
    end
    
    if nargout<=1
       u=diag(s);
    end
    
    return
    

    根据我的经验,通常你实际上并没有零。数值精度使您得到的结果大致接近,例如,以下。如果我想创建一个 5 x 5 矩阵但它的等级为 3,我可以执行以下操作。

    A = randn(5,3)*randn(5,3);
    [U,S,Vt] = svdsim(A,1e-8);
    
    
    S =
    
        6.3812         0         0         0         0
             0    2.0027         0         0         0
             0         0    1.0240         0         0
             0         0         0    0.0000         0
             0         0         0         0    0.0000
    

    现在它看起来就像你有零。但如果你仔细观察。

    format long 
    >> S(4,4)
    
    ans =
    
         3.418057860623250e-16
    
    
       S(5,5)
    
    ans =
    
         9.725444388260210e-17
    

    我会注意到这是机器 epsilon,对于所有密集型目的,它是 0。

    【讨论】:

    • 我解决了这个问题,在 Matlab 中应该是 sumsqr,在 Octave 中应该是 sumsq。其余的应该是一样的。我知道奇异值可以为 0,但我的问题是当使用 Jacobi 方法时 U 的一列结果为 0。感谢您的代码,我宁愿有一个不使用其他功能(如 QR)的实现,但如果我能找到另一种方法,我会使用这个,因为结果似乎还可以!
    • 你能举个例子吗?
    • 只有当奇异值正好为 0 时才会发生。我想修正的一个例子是 A = 0(例如 0 的 3x3 矩阵),其中 U 和 V 必须是 I,但该代码返回 NaN 因为除以 0。这是另一个示例,其中 d 和 v 是正确的,但 U 的一列是 NaN A = 0.064 -0.53 -0.0 0.57 0.065 0.0 0.23 -0.23 0.0
    • 我不确定这与您的代码有什么关系,但与 jacobi 方法的工作方式是否正确。
    猜你喜欢
    • 2013-10-14
    • 2018-06-17
    • 1970-01-01
    • 2020-06-15
    • 2013-06-03
    • 2014-01-05
    • 2013-05-05
    • 2019-04-27
    • 1970-01-01
    相关资源
    最近更新 更多