R ginv和Matlab pinv产生不同的结果

Far*_*ghi 6 matlab r matrix-inverse

ginv()MASS与MATLAB pinv()函数相比,R中的函数生成完全不同的值.他们都宣称生产矩阵的Moore-Penrose广义逆.

我试图为R实现设置相同的容差,但差异仍然存在.

  • MATLAB默认tol: max(size(A)) * norm(A) * eps(class(A))
  • R默认tol: sqrt(.Machine$double.eps)

再生产:

R:

library(MASS)
A <- matrix(c(47,94032,149, 94032, 217179406,313679,149,313679,499),3,3)
ginv(A)
Run Code Online (Sandbox Code Playgroud)

输出:

              [,1]          [,2]          [,3]
[1,]  1.675667e-03 -8.735203e-06  5.545605e-03
[2,] -8.735203e-06  5.014084e-08 -2.890907e-05
[3,]  5.545605e-03 -2.890907e-05  1.835313e-02
Run Code Online (Sandbox Code Playgroud)

svd(A)

输出:

$d
[1] 2.171799e+08 4.992800e+01 2.302544e+00

$u
              [,1]         [,2]          [,3]
[1,] -0.0004329688  0.289245088 -9.572550e-01
[2,] -0.9999988632 -0.001507826 -3.304234e-06
[3,] -0.0014443299  0.957253888  2.892454e-01

$v
              [,1]         [,2]          [,3]
[1,] -0.0004329688  0.289245088 -9.572550e-01
[2,] -0.9999988632 -0.001507826 -3.304234e-06
[3,] -0.0014443299  0.957253888  2.892454e-01
Run Code Online (Sandbox Code Playgroud)

MATLAB:

A = [47 94032 149; 94032 217179406 313679; 149 313679 499]
pinv(A)
Run Code Online (Sandbox Code Playgroud)

输出:

ans =

    0.3996   -0.0000   -0.1147
   -0.0000    0.0000   -0.0000
   -0.1147   -0.0000    0.0547
Run Code Online (Sandbox Code Playgroud)

SVD:

[U, S, V] = svd(A)

U =

   -0.0004    0.2892   -0.9573
   -1.0000   -0.0015   -0.0000
   -0.0014    0.9573    0.2892


S =

  1.0e+008 *

    2.1718         0         0
         0    0.0000         0
         0         0    0.0000


V =

   -0.0004    0.2892   -0.9573
   -1.0000   -0.0015   -0.0000
   -0.0014    0.9573    0.2892
Run Code Online (Sandbox Code Playgroud)

Mic*_*ico 5

运行debugonce(MASS::ginv),我们看到不同之处在于奇异值分解做了什么。

具体来说,R 检查以下内容:

Xsvd <- svd(A)
Positive <- Xsvd$d > max(tol * Xsvd$d[1L], 0)
Positive
# [1]  TRUE  TRUE FALSE
Run Code Online (Sandbox Code Playgroud)

如果第三个元素为真(我们可以通过设置强制tol = 0,正如@nicola 所建议的那样),MASS::ginv将返回:

Xsvd$v %*% (1/Xsvd$d * t(Xsvd$u))
#               [,1]          [,2]          [,3]
# [1,]  3.996430e-01 -7.361507e-06 -1.147047e-01
# [2,] -7.361507e-06  5.014558e-08 -2.932415e-05
# [3,] -1.147047e-01 -2.932415e-05  5.468812e-02
Run Code Online (Sandbox Code Playgroud)

(即,与 MATLAB 相同)。

相反,它返回:

Xsvd$v[, Positive, drop = FALSE] %*% ((1/Xsvd$d[Positive]) * 
  t(Xsvd$u[, Positive, drop = FALSE]))
#               [,1]          [,2]          [,3]
# [1,]  1.675667e-03 -8.735203e-06  5.545605e-03
# [2,] -8.735203e-06  5.014084e-08 -2.890907e-05
# [3,]  5.545605e-03 -2.890907e-05  1.835313e-02
Run Code Online (Sandbox Code Playgroud)

感谢@FaridCher 指出pinv.

我不确定我是否 100% 理解 MATLAB 代码,但我认为这归结为使用方式的不同tolPositiveR中的MATLAB对应为:

`r = sum(s>tol)`
Run Code Online (Sandbox Code Playgroud)

tol用户提供的东西在哪里;如果没有提供,我们得到:

m = 0;
% I don't get the point of this for loop -- why not just `m = max(size(A))`?
for i = 1:n 
   m = max(m,length(A(:,i)));
end
% contrast with simply `tol * Xsvd$d[1L]` in R
%   (note: i believe the elements of d are sorted largest to smallest)
tol = m*eps(max(s)); 
Run Code Online (Sandbox Code Playgroud)

  • 我想这是来自“R”方面的公差问题和“MATLAB”中的印刷问题。正如我在评论中所说,`ginv(A,tol=0)` 与 wolphram 一致,`all.equal(A,A %*% ginv(A,tol=0) %*% A)` 给出了 `TRUE` ,所以结果是正确的。看起来伪逆对实际值非常敏感;`A` 的微小差异可能会产生完全不同的伪逆。 (2认同)