Performance 在matlab中高效地计算双和?

Performance 在matlab中高效地计算双和?,performance,matlab,Performance,Matlab,我正在寻找一种优化的方法来编程这个求和比率。作为输入,我有两个向量v_mn和x_mn,每个向量都有(M*N)x1元素 比率的形式如下: 向量x_mn是0-1向量,因此当x_mn=1时,比率为r,当x_mn=0时,比率为0 向量v_mn是一个包含实数的向量 我像这样做分母,但它需要很多次 function r_ij = denominator(v_mn, M, N, i, j) %here x_ij=1, to get r_ij. S = []; for m = 1:M for n = 1:

我正在寻找一种优化的方法来编程这个求和比率。作为输入,我有两个向量
v_mn
x_mn
,每个向量都有
(M*N)x1
元素

比率的形式如下:

向量
x_mn
0-1
向量,因此当
x_mn=1
时,比率为
r
,当
x_mn=0
时,比率为
0

向量
v_mn
是一个包含实数的向量

我像这样做分母,但它需要很多次

function r_ij = denominator(v_mn, M, N, i, j)
%here x_ij=1, to get r_ij.
S = [];
for m = 1:M
  for n = 1:N
    if (m ~= i)
      if (n ~= j)
        S = [S v_mn(i, n)];
      else
        S = [S 0];
      end
    else
      S = [S 0];
    end
  end
end
r_ij = 1+S;
end
你能给出一个在matlab中实现的好方法吗。你可以忽略比率,给我分母,它更复杂


编辑:很抱歉我没有写得很好。
i
j
分别是介于
1..M
1..N
之间的一些数字。如您所见,比率
r
是许多值(
M*N
值)。所以我只计算了
I
j
的值。更准确地说,我假设
x_ij=1
。此外,我将向量
v\u mn
转换为矩阵,这就是我使用双索引的原因。

首先,您需要预先计算s矩阵。它会改变每个循环的大小,所以

S = zeros(m*n, 1) 
在您的功能开始时。这也将允许您删除else条件语句,即它们将简化为:

if (m ~= i)
   if (n ~= j)
      S(m*M + n) = v_mn(i, n);
否则,因为你必须访问每一个元素,我担心它可能无法更快

如果您迫切需要更高的速度,您可以考虑执行一些
mex
编码,这是c/c++中的代码,但在matlab中运行


您可能需要修改上述内容,以确保它符合您的要求,而不是首先跳入双循环的矢量化。在该代码中,没有数据求和,而是在每次迭代时调整向量
S
的大小。同样,签名可以包括矩阵V和X,这样乘法就如公式中所示发生(而不仅仅依赖于X的值为零或一,让我们将该矩阵传递进来)

该函数看起来更像以下内容(我已将I,j输入替换为m,n,更像方程式):


注意第一个
if
如何位于内部for循环之外,因为它不依赖于
j
。试试上面的方法,看看会发生什么

您可以从Matlab中进行矢量化,以加快计算速度。每次您使用诸如“^”或“*”之类的运算或任何矩阵运算时,Matlab都会并行执行这些运算,这比迭代每个项快得多

在这种情况下,看看你在矩阵方面做了什么。首先,在您的循环中,您只处理$V_{nm}$的第m行,我们可以将其本身用作向量

如果你仔细看一下你的公式,你会发现,如果你把这个行向量写成一个列向量,然后用标准的矩阵乘法从左边把矩阵$X_{nm}$乘以它,你几乎可以达到目的。结果向量包含所有n的和。要得到最终结果,只需将此向量求和

function result = denominator_vectorized(V,X,m,n)

% get the part of V with the first index m
Vm = V(m,:)';
% remove the parts of X you don't want to iterate over. Note that, since I
% am inside the function, I am only editing the value of X within the scope
% of this function.
X(m,:) = 0;
X(:,n) = 0;

%do the matrix multiplication and the summation at once
result = 1-sum(X*Vm);
为了向您展示这如何优化您的操作,我将把它与另一位评论者提出的代码进行比较:

function result = denominator(V,X,m,n)

% use the size of V to determine M and N
[M,N] = size(V);

% initialize the summed value to one (to account for one at the end)
result = 1;

% outer loop
for i=1:M
% ignore the case where m==i
if i~=m
    for j=1:N
        % ignore the case where n==j
        if j~=n
            result = result + V(m,j)*X(i,j);
        end
     end
 end
end
测试:

V=rand(10000,10000);
X=rand(10000,10000);
disp('looped version')
tic
denominator(V,X,1,1)
toc
disp('matrix operation')
tic
denominator_vectorized(V,X,1,1)
toc
结果是:

looped version

ans =

   2.5197e+07

Elapsed time is 4.648021 seconds.
matrix operation

ans =

   2.5197e+07

Elapsed time is 0.563072 seconds.
这几乎是循环迭代速度的十倍。因此,请始终注意代码中可能存在的矩阵操作。如果您安装了并行计算工具箱并安装了支持CUDA的图形卡,那么Matlab甚至可以在图形卡上执行这些操作,而无需您付出任何进一步的努力


编辑:最后一点并非完全正确。您仍然需要采取一些步骤在CUDA硬件上进行操作,但这些步骤并不多。请参阅Matlab文档。

如果重新塑造数据,那么求和就是重复的矩阵/向量乘法

下面是单个
m
n
的实现,以及一个简单的速度/平等性测试:

clc

%# some arbitrary test parameters
M = 250;
N = 1000;
v = rand(M,N);   %# (you call it v_mn)
x = rand(M,N);   %# (you call it x_mn)

m0 = randi(M,1); %# m of interest
n0 = randi(N,1); %# n of interest 


%# "Naive" version
tic
S1 = 0;
for mm = 1:M %# (you call this m')
    if mm == m0, continue; end
    for nn = 1:N %# (you call this n')
        if nn == n0, continue; end
        S1 = S1 + v(m0,nn) * x(mm,nn);
    end
end
r1 = v(m0,n0)*x(m0,n0) / (1+S1);
toc


%# MATLAB version: use matrix multiplication!
tic

ninds = [1:m0-1 m0+1:M];
minds = [1:n0-1 n0+1:N];
S2 = sum( x(minds, ninds) * v(m0, ninds).' );
r2 = v(m0,n0)*x(m0,n0) / (1+S2);

toc


%# Test if values are equal
abs(r1-r2) < 1e-12
因此,加速比约为133倍

现在这是针对单个值
m
n
。要对
m
n
的所有值执行此操作,可以在其周围使用(优化的)双循环:

r = zeros(M,N);
for m0 = 1:M   
    xx = x([1:m0-1 m0+1:M], :);
    vv = v(m0,:).';
    for n0 = 1:N
        ninds    = [1:n0-1 n0+1:N];        
        denom    = 1 + sum( xx(:,ninds) * vv(ninds) );
        r(m0,n0) = v(m0,n0)*x(m0,n0)/denom;        
    end
end
对于
M=250,N=1000
(R2010a),在我的电脑上大约15秒钟即可完成

编辑:实际上,经过一点思考,我能够将其简化为:

denom = zeros(M,N);
for mm = 1:M    
    xx = x([1:mm-1 mm+1:M],:);
    denom(mm,:) = sum( xx*v(mm,:).' ) - sum( bsxfun(@times, xx, v(mm,:)) );    
end
denom = denom + 1;

r_mn = x.*v./denom;

对于
N=250
M=1000
:)

您说
v_mn
M*N
x
1
,但您将其索引为
v_mn(i,N)
。它真的是
M*N
x
1
,还是
M
x
N
?另外,您的函数中的
i
j
是什么?在哪里(或如何)初始化
i
j
?函数中的返回值是
r
-设置在哪里?非常抱歉。我写得不好。我相应地修改了问题@路易斯门多和杰夫·海斯。谢谢。看我更新的答案。这个答案更好@x、 你应该把这个答案标对。
r = zeros(M,N);
for m0 = 1:M   
    xx = x([1:m0-1 m0+1:M], :);
    vv = v(m0,:).';
    for n0 = 1:N
        ninds    = [1:n0-1 n0+1:N];        
        denom    = 1 + sum( xx(:,ninds) * vv(ninds) );
        r(m0,n0) = v(m0,n0)*x(m0,n0)/denom;        
    end
end
denom = zeros(M,N);
for mm = 1:M    
    xx = x([1:mm-1 mm+1:M],:);
    denom(mm,:) = sum( xx*v(mm,:).' ) - sum( bsxfun(@times, xx, v(mm,:)) );    
end
denom = denom + 1;

r_mn = x.*v./denom;