繁体   English   中英

在matlab中有效地计算双和?

[英]Compute double sum in matlab efficiently?

我正在寻找一种编程这个总和比率的最佳方法。 作为输入,我有两个向量v_mnx_mn ,每个向量带有(M*N)x1元素。

比例的形式如下:

式

向量x_mn0-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中做一个好方法吗? 你可以忽略这个比例,给我一个更复杂的分母。

编辑:对不起,我写的不是很好。 ij分别是1..M1..N之间的一些数字。 如您所见,比率r是许多值( M*N值)。 所以我只计算了值ij 更准确地说,我认为x_ij=1 此外,我将矢量v_mn转换为矩阵,这就是我使用双索引的原因。

如果重塑数据,则总和只是重复的矩阵/向量乘法。

这是单个mn ,以及简单的速度/相等性测试:

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

我机器上的输出:

Elapsed time is 0.327004 seconds.   %# loop-version
Elapsed time is 0.002455 seconds.   %# version with matrix multiplication
ans =  
     1                              %# and yes, both are equal

所以加速是~133×

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

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

在我的PC上在~15秒内完成M = 250, N= 1000 (R2010a)。

编辑:实际上,经过多一点思考,我能够将其全部减少到这个:

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 = 250M = 1000 ,在不到1秒的时间内完成

首先,您需要预先分配您的S矩阵。 它会改变每个循环的大小

S = zeros(m*n, 1) 

在你的功能开始。 这也将允许您取消您的其他条件语句,即它们将减少到:

if (m ~= i)
   if (n ~= j)
      S(m*M + n) = v_mn(i, n);

否则,因为你必须访问每个元素,我担心它可能无法更快。

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

http://www.mathworks.com.au/help/matlab/matlab_external/introducing-mex-files.html

您可能需要修改上述内容以确保它能够执行您想要的操作,而不是首先跳转到双循环的矢量化。 在此代码中,没有数据的求和,而是在每次迭代时调整矢量S的大小。 同样,签名可以包括矩阵V和X,以便乘法发生在公式中(而不是仅仅依赖于X的值为零或一,让我们传递该矩阵)。

该函数可能看起来更像下面(我用m替换了i,j输入,n更像是等式):

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

注意第一个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.

这几乎是循环迭代速度的十倍。 因此,请始终注意代码中可能的矩阵运算。 如果您安装了Parallel Computing Toolbox并安装了支持CUDA的图形卡,Matlab甚至可以在您的图形卡上执行这些操作,而无需您做任何进一步的努力!

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM