MATLAB中的mrdivide函数:它在做什么,我如何在Python中实现?

13 投票
4 回答
8029 浏览
提问于 2025-04-15 12:18

我有一行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 个回答

1

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' 是定义好的,并且会给出相同的结果。

7

简而言之: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连在一起使用。

13

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)) 

撰写回答