MATLAB中没有循环的矩阵计算

时间:2016-03-20 19:31:48

标签: arrays matlab for-loop matrix vectorization

执行某些数组操作的代码存在问题。它太慢了,因为我使用循环和输入数据都很大。对我来说这是最简单的方法,但现在我正在寻找比循环更快的东西。我试图优化或重写代码,但不成功。我真的很帮助你。

在我的代码中,我有三个数组x1y1(网格中的点坐标),g1(点中的值),例如它们的大小为300 x 300。我将每个矩阵视为9的组合,并计算中间点的点数。例如,我从g1(101,101)开始,但我使用的是来自g1(1:201,1:201)=g2的数据。我需要计算从g1(1:201,1:201)g1(101,101)ll矩阵)的每个点的距离,然后我在代码中计算nn,然后我找到g1(101,101)的值来自nn并将其放入N数组中。然后我转到g1(101,102),依此类推,直到g1(200,200),最后一个案例g2=g1(99:300,99:300)

正如我所说,这段代码效率不高,即使我必须使用比我在示例中给出的更大的数组,也需要花费太多时间。我希望我能够清楚地解释我对代码的期望。我正在考虑使用arrayfun,但我从未使用过这个函数,因此我不知道应该如何使用它,但在我看来它无法处理。也许有其他解决方案,但我找不到合适的东西。

tic
x1=randn(300,300);
y1=randn(300,300);
g1=randn(300,300);
m=size(g1,1);
n=size(g1,2);
w=1/3*m;
k=1/3*n;
N=zeros(w,k);
for i=w+1:2*w 
    for j=k+1:2*k 
        x=x1(i,j);
        y=y1(i,j);
        x2=y1(i-k:i+k,j-w:j+w);
        y2=y1(i-k:i+k,j-w:j+w);
        g2=g1(i-k:i+k,j-w:j+w);
        ll=1./sqrt((x2-x).^2+(y2-y).^2);
        ll(isinf(ll))=0;
        nn=ifft2(fft2(g2).*fft2(ll));
        N(i-w,j-k)=nn(w+1,k+1);
      end
  end
  czas=toc;

1 个答案:

答案 0 :(得分:2)

对于它的价值,arrayfun()只是for循环的包装器,因此它不会导致任何性能改进。此外,您可能在x2的定义中有拼写错误,我假设它取决于x1。否则它将是一个多余的变量。此外,您的i<->w/kj<->k/w配对似乎不一致,您也应该检查一下。 另外,只是tic/toc的时间安排很难准确。分析代码时,将其放在函数中并多次运行定时,并从时序中排除变量生成。更好的是:使用内置的分析器。

免责声明:由于内存需求巨大,此解决方案可能对您的实际问题无济于事。对于300x300矩阵的输入,这适用于尺寸为300x300x100x100的阵列,这通常是不合适的。不过,它在这里以较小的输入尺寸进行参考。我想添加一个基于nlfilter()的解决方案,但是你的问题似乎太复杂了,无法使用它。

与矢量化一样,如果你可以为它节省内存,你可以更快地完成它。您正在尝试使用每个[2*k+1,2*w+1]索引的大小为[i,j]的矩阵。这需要形状[2*k+1,2*w+1,w,k]的4d数组。对于每个元素[i,j],您都有一个索引为[:,:,i,j]的矩阵,以便与x1y1的相应元素一起处理。它还有助于fft2接受多维数组。

这就是我的意思:

tic
x1 = randn(30,30);  %// smaller input for tractability
y1 = randn(30,30);
g1 = randn(30,30);
m = size(g1,1);
n = size(g1,2);
w = 1/3*m;
k = 1/3*n;

%// these will be indexed on the fly:    
%//x = x1(w+1:2*w,k+1:2*k);     %// size [w,k]
%//y = x1(w+1:2*w,k+1:2*k);     %// size [w,k]

x2 = zeros(2*k+1,2*w+1,w,k); %// size [2*k+1,2*w+1,w,k]
y2 = zeros(2*k+1,2*w+1,w,k); %// size [2*k+1,2*w+1,w,k]
g2 = zeros(2*k+1,2*w+1,w,k); %// size [2*k+1,2*w+1,w,k]

%// manual definition for now, maybe could be done smarter:
for ii=w+1:2*w       %// don't use i and j as variables
    for jj=k+1:2*k   %// don't use i and j as variables
        x2(:,:,ii-w,jj-k) = x1(ii-k:ii+k,jj-w:jj+w);  %// check w vs k here
        y2(:,:,ii-w,jj-k) = y1(ii-k:ii+k,jj-w:jj+w);  %// check w vs k here
        g2(:,:,ii-w,jj-k) = g1(ii-k:ii+k,jj-w:jj+w);  %// check w vs k here
    end
end

%// use bsxfun to operate on [2*k+1,2*w+1,w,k] vs [w,k]-sized arrays
%// need to introduce leading singletons with permute() in the latter
%// in order to have shape [1,1,w,k] compatible with the first array
ll = 1./sqrt(bsxfun(@minus,x2,permute(x1(w+1:2*w,k+1:2*k),[3,4,1,2])).^2 ...
           + bsxfun(@minus,y2,permute(y1(w+1:2*w,k+1:2*k),[3,4,1,2])).^2);
ll(isinf(ll)) = 0;

%// compute fft2, operating on [2*k+1,2*w+1,w,k]
%// will return fft2 for each index in the [w,k] subspace
nn = ifft2(fft2(g2).*fft2(ll));

%// we need nn(w+1,k+1,:,:) which is exactly of size [w,k] as needed
N = reshape(nn(w+1,k+1,:,:),[w,k]);  %// quicker than squeeze()
N = real(N);  %// this solution leaves an imaginary part of around 1e-12

czas=toc;