计算马哈拉诺比斯点集与参考点集之间的距离

时间:2015-08-09 13:12:55

标签: matlab optimization vectorization linear-algebra

我有一个n x p矩阵 - mX,它由R ^ p中的n个点组成。

我有另一个m x p矩阵 - mY,它由R ^ p中的m个参考点组成。

我想创建一个n x m矩阵 - mD,它是Mahalanobis Distance矩阵。

D(i,j)表示mX中的点i之间的Mahalanobis Distance,mX(i,:)和mY中的点j,mY(j,:)。

即,计算以下内容:

mD(i, j) = (mX(i, :) - mY(j, :)) * inv(mC) * (mX(i, :) - mY(j, :)).';

其中mC是给定的Mahalanobis距离PSD矩阵。

在循环中很容易完成,有没有办法对其进行矢量化?

即,是一个函数,它的输入是mX,mY和mC,它的输出是mD并且完全矢量化而不使用任何MATLAB工具箱?

谢谢。

4 个答案:

答案 0 :(得分:1)

这是一个消除一个循环的解决方案

function d = mahalanobis(mX, mY)

    n = size(mX, 2);
    m = size(mY, 2);
    data = [mX, mY];
    mc = cov(transpose(data));

    dist = zeros(n,m);
    for i = 1 : n
        diff = repmat(mX(:,i), 1, m) - mY;
        dist(i,:) = sum((mc\diff).*diff , 1);
    end
    d = sqrt(dist);

end

您可以将其调用为:

d = mahalanobis(transpose(X),transpose(Y))

答案 1 :(得分:1)

方法#1

假设无限资源,这是使用bsxfunmatrix-multiplication的一个矢量化解决方案 -

A = reshape(bsxfun(@minus,permute(mX,[1 3 2]),permute(mY,[3 1 2])),[],p);
out = reshape(diag(A*inv(mC)*A.'),n,m);

方法#2

这是一个试图降低循环复杂度的解决方案 -

A = reshape(bsxfun(@minus,permute(mX,[1 3 2]),permute(mY,[3 1 2])),[],p);
imC = inv(mC);
out = zeros(n*m,1);
for ii = 1:n*m
    out(ii) = A(ii,:)*imC*A(ii,:).';
end
out = reshape(out,n,m);

示例运行 -

>> n = 3;  m = 4;   p = 5;
mX = rand(n,p);
mY = rand(m,p);
mC = rand(p,p);
imC = inv(mC);
>> %// Original solution
for i = 1:n
    for j = 1:m
        mD(i, j) = (mX(i, :) - mY(j, :)) * inv(mC) * (mX(i, :) - mY(j, :)).'; %//'
    end
end
>> mD
mD =
      -8.4256       10.032       2.8929       7.1762
      -44.748      -4.3851      -13.645      -9.6702
      -4.5297       3.2928      0.11132       2.5998
>> %// Approach #1
A = reshape(bsxfun(@minus,permute(mX,[1 3 2]),permute(mY,[3 1 2])),[],p);
out = reshape(diag(A*inv(mC)*A.'),n,m);  %//'
>> out
out =
      -8.4256       10.032       2.8929       7.1762
      -44.748      -4.3851      -13.645      -9.6702
      -4.5297       3.2928      0.11132       2.5998
>> %// Approach #2
A = reshape(bsxfun(@minus,permute(mX,[1 3 2]),permute(mY,[3 1 2])),[],p);
imC = inv(mC);
out1 = zeros(n*m,1);
for ii = 1:n*m
    out1(ii) = A(ii,:)*imC*A(ii,:).';  %//'
end
out1 = reshape(out1,n,m);
>> out1
out1 =
      -8.4256       10.032       2.8929       7.1762
      -44.748      -4.3851      -13.645      -9.6702
      -4.5297       3.2928      0.11132       2.5998

相反,如果你有:

mD(j, i) = (mX(i, :) - mY(j, :)) * inv(mC) * (mX(i, :) - mY(j, :)).';

解决方案将转换为下一个列出的版本。

方法#1

A = reshape(bsxfun(@minus,permute(mY,[1 3 2]),permute(mX,[3 1 2])),[],p);
out = reshape(diag(A*inv(mC)*A.'),m,n);

方法#2

A = reshape(bsxfun(@minus,permute(mY,[1 3 2]),permute(mX,[3 1 2])),[],p);
imC = inv(mC);
out1 = zeros(m*n,1);
for i = 1:n*m
    out(i) = A(i,:)*imC*A(i,:).';  %//'
end
out = reshape(out,m,n);

示例运行 -

>> n = 3; m = 4; p = 5;
mX = rand(n,p);    mY = rand(m,p);     mC = rand(p,p);  imC = inv(mC);
>> %// Original solution
for i = 1:n
    for j = 1:m
        mD(j, i) = (mX(i, :) - mY(j, :)) * inv(mC) * (mX(i, :) - mY(j, :)).'; %//'
    end
end
>> mD
mD =
      0.81755      0.33205      0.82254
       1.7086       1.3363       2.4209
      0.36495      0.78394     -0.33097
      0.17359       0.3889      -1.0624
>> %// Approach #1
A = reshape(bsxfun(@minus,permute(mY,[1 3 2]),permute(mX,[3 1 2])),[],p);
out = reshape(diag(A*inv(mC)*A.'),m,n);  %//'
>> out
out =
      0.81755      0.33205      0.82254
       1.7086       1.3363       2.4209
      0.36495      0.78394     -0.33097
      0.17359       0.3889      -1.0624
>> %// Approach #2
A = reshape(bsxfun(@minus,permute(mY,[1 3 2]),permute(mX,[3 1 2])),[],p);
imC = inv(mC);
out1 = zeros(m*n,1);
for i = 1:n*m
    out1(i) = A(i,:)*imC*A(i,:).';  %//'
end
out1 = reshape(out1,m,n);
>> out1
out1 =
      0.81755      0.33205      0.82254
       1.7086       1.3363       2.4209
      0.36495      0.78394     -0.33097
      0.17359       0.3889      -1.0624

答案 2 :(得分:1)

降低到L2

如果允许预处理矩阵mC并且你不怕数字差异,似乎马哈拉诺比斯距离可以减少到普通的L2距离。

首先,计算mC的Cholesky分解:

mR = chol(mC)      % C = R^t * R, where R is upper-triangular

现在我们可以用这些因素来重新表述马哈拉诺比斯距离:

(Xi-Yj) * inv(C) * (Xi-Yj)^t = || (Xi-Yj) inv(R) ||^2 = ||TXi - TYj||^2
where:  TXi = Xi * inv(R)
        TYj = Yj * inv(R)

因此,我们的想法是首先将点XiYj转换为TXiTYj,然后计算它们之间的欧氏距离。这是算法大纲:

  1. 计算mR - 协方差矩阵mC的Cholesky因子( O(p ^ 3)时间。)
  2. 反转三角矩阵mR O(p ^ 3)时间。
  3. mXmY乘以右侧的inv(mR)(取 O(p ^ 2(m + n))时间。
  4. 计算两对点之间的L2距离(取 O(m n p)时间)。
  5. 总时间 O(m n p +(m + n)p ^ 2 + p ^ 3)与原始 O(m n p ^ 2)。当 1&lt;&lt; p <&lt; N,M 。在这种情况下,步骤4将花费大部分时间并且应该进行矢量化。

    矢量

    我对MATLAB没什么经验,但在x86 CPU上有很多SIMD矢量化。在原始计算中,沿着一个足够大的数组维度进行向量化就足够了,并为其他维度制作简单的循环。

    如果您希望p足够大,可能可以沿着点的坐标进行矢量化,并为i <= nj <= m创建两个嵌套循环。这与@Daniel发布的相似。

    如果p不够大,您可以改为沿着其中一个点序列进行矢量化。这与@dpmcmlxxvi发布的解决方案类似:您必须从第二个矩阵的所有行中减去一行矩阵的单行,然后计算结果行的平方范数。重复n次( 或m次)。

    至于我,完全矢量化(这意味着用矩阵运算而不是MATLAB中的循环重写)听起来并不像一个聪明的性能目标。最有可能部分矢量化解决方案的速度最快。

答案 3 :(得分:0)

我得出结论,矢量化这个问题效率不高。我对矢量化这个问题的最好想法是需要m x n x p x p工作内存,至少如果一次处理所有内容。这意味着当n = m = p = 152时,代码已经需要4GB Ram。在这些维度上,我的系统可以在不到一秒的时间内运行循环:

mD=zeros(size(mX,1),size(mY,1));
ImC=inv(mC);
for i=1:size(mX,1)
    for j=1:size(mY,1)
        d=mX(i, :) - mY(j, :);
        mD(i, j) = (d) * ImC * (d).';
    end
end