MATLAB中的mrdivide函数:它在做什么,我如何在Python中实现?
我有一行MATLAB代码:
a/b
我使用了这些输入:
a = [1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9]
b = ones(25, 18)
这是结果(一个1x25的矩阵):
[5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
MATLAB在做什么呢?我想在Python中复制这个行为,但MATLAB的mrdivide
文档对我没有帮助。这个5是从哪里来的,为什么其他的值都是0呢?
我尝试用其他输入进行测试,结果也差不多,通常只是第一个元素不同,剩下的都是0。在Python中,当我使用linalg.lstsq(b.T,a.T)
时,返回的第一个矩阵中的所有值(也就是不是奇异的那个)都是0.2。我已经尝试过在Python中进行右除法,但结果完全不对,维度也不对。
我知道什么是最小二乘法近似,我只是想知道mrdivide
到底在做什么。
相关内容:
4 个回答
a/b 是用来找到线性方程组 bx = a 的最小二乘解的方法。
如果 b 是可逆的(也就是说可以反转),那么解就是 a 乘以 b 的反转。但如果 b 不是可逆的,那么解就是使得 bx 和 a 之间的差距最小的 x。
你可以在 维基百科 上了解更多关于最小二乘法的内容。
根据 MATLAB 的文档,mrdivide 最多会返回 k 个非零值,其中 k 是 b 的计算秩。我猜在你的情况下,MATLAB 通过将 b 替换为 b(:1) 来解决最小二乘问题(这两个的秩是相同的)。在这种情况下,moore-penrose 逆 b2 = b(1,:); inv(b2*b2')*b2*a'
是定义好的,并且会给出相同的结果。
简而言之:A/B = np.linalg.solve(B.conj().T, A.conj().T).conj().T
我没有找到之前的回答能让我满意,所以我深入研究了Matlab的参考文档,特别是关于mrdivide的部分,最终找到了这个解决方案。我不能在这里解释具体的数学原理,也不想占这个答案的功劳。我只是按照Matlab的解释来做的。此外,我想把Matlab的具体内容分享出来,以示感谢。如果这涉及版权问题,请告诉我,我会把具体的内容删掉。
%/ Slash or right matrix divide.
% A/B is the matrix division of B into A, which is roughly the
% same as A*INV(B) , except it is computed in a different way.
% More precisely, A/B = (B'\A')'. See MLDIVIDE for details.
%
% C = MRDIVIDE(A,B) is called for the syntax 'A / B' when A or B is an
% object.
%
% See also MLDIVIDE, RDIVIDE, LDIVIDE.
% Copyright 1984-2005 The MathWorks, Inc.
注意,'
这个符号表示复共轭转置。在Python中使用numpy时,需要把.conj()和.T连在一起使用。
MRDIVIDE 或者说 /
这个符号其实是用来解决 xb = a
这种线性方程组的,而 MLDIVIDE 或者 \
这个符号则是用来解决 bx = a
这种方程组的。
如果你想用一个不对称且不可逆的矩阵 b
来解决 xb = a
这个方程组,你可以使用 mridivide()
,这个方法是通过高斯消元法对 b
进行分解,或者使用 pinv()
,这个方法是通过奇异值分解,并将小于某个(默认)容忍度的奇异值归零来实现的。
这里有个区别(以 mldivide
为例): 当我解方程 A*x=b 时,PINV 和 MLDIVIDE 有什么区别?
当方程组是过定的时,这两种算法会给出相同的答案。而当方程组是欠定的时,PINV 会返回一个最小范数的解(也就是最小的 NORM(x)),而 MLDIVIDE 会选择非零元素最少的解。
在你的例子中:
% solve xb = a
a = [1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9];
b = ones(25, 18);
这个方程组是欠定的,两个不同的解会是:
x1 = a/b; % MRDIVIDE: sparsest solution (min L0 norm)
x2 = a*pinv(b); % PINV: minimum norm solution (min L2)
>> x1 = a/b
Warning: Rank deficient, rank = 1, tol = 2.3551e-014.
ans =
5.0000 0 0 ... 0
>> x2 = a*pinv(b)
ans =
0.2 0.2 0.2 ... 0.2
在这两种情况下,xb-a
的近似误差是不可忽视的(也就是说不是精确解),而且是相同的,也就是说 norm(x1*b-a)
和 norm(x2*b-a)
会返回相同的结果。
MATLAB 在做什么?
关于 \
这个符号所调用的算法(以及对矩阵 b
属性的检查)的详细解释,可以在这个帖子中找到: scicomp.stackexchange.com。我假设 /
符号也适用类似的选项。
在你的例子中,MATLAB 很可能在进行高斯消元,给出在无穷多个解中最稀疏的解(这就是为什么会出现 5 的原因)。
Python 在做什么?
在 linalg.lstsq
中,Python 使用伪逆/SVD,正如上面所示(这就是为什么你会得到一串 0.2 的原因)。实际上,下面的代码会给你和 MATLAB 的 pinv()
相同的结果:
from numpy import *
a = array([1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9])
b = ones((25, 18))
# xb = a: solve b.T x.T = a.T instead
x2 = linalg.lstsq(b.T, a.T)[0]
x2 = dot(a, linalg.pinv(b))