Matlab - 加速嵌套For循环

use*_*301 6 matlab

一个简单的问题,但我对MATLAB并不是那么好.我有矢量x,(nx 1)y,(mx 1)和w = [x;y].我想将M(n + mx 1)定义为M(i)= x的元素数小于或等于w(i)(w被排序).这只是没有削减它:

N = n + m;
M = zeros(N,1);
for i = 1:N
  for j = 1:n
    if x(j) <= w(i)
      M(i) = M(i) + 1;
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

这不是一种特别聪明的方法,我的一些数据向量m和n大约是100000.

谢谢!

gno*_*ice 9

这可能看起来很神秘,但它应该给你与嵌套循环相同的结果:

M = histc(-x(:)',[-fliplr(w(:)') inf]);
M = cumsum(fliplr(M(1:N)))';
Run Code Online (Sandbox Code Playgroud)

以上假设w已按升序排序.

说明

您的排序向量w可以被认为是用于创建具有HISTC函数的直方图的bin边.一旦计算了每个bin中(即边缘之间)的值的数量,使用CUMSUM函数对这些bin的累积总和将为您提供向量M.上面的代码看起来如此混乱(带有否定和函数FLIPLR)的原因是因为你想要找到x 小于或等于每个值的值w,但函数HISTC以下列方式存储数据:

n(k)计算值x(i)if edges(k) <= x(i) < edges(k+1).

请注意,小于每个箱的上限使用.您可能希望翻转行为,以便根据规则进行分类edges(k) < x(i) <= edges(k+1),这可以通过否定要分箱的值来实现,否定边缘,翻转边缘(因为边缘输入到HISTC必须单调非减少),然后翻转返回的bin计数.该值inf用作边值,用于计算小于w第一个bin中最低值的所有内容.

如果你想要找到的值x只是小于每个值w,代码就会简单得多:

M = histc(x(:)',[-inf w(:)']);
M = cumsum(M(1:N))';
Run Code Online (Sandbox Code Playgroud)

  • 这个解决方案是正确的,使用histc.但是,如果w不在排序顺序中,它将失败.然后histc将返回错误.如果在w中有重复,如果它会失败,我也不会感到惊讶. (2认同)

Mar*_*iot 5

内环至少可以替换为:

M(i)=sum(x<=w(i))
Run Code Online (Sandbox Code Playgroud)

这将提供显着的性能改进.您可以考虑使用arrayfun:

M = arrayfun(@(wi)( sum( x <= wi ) ), w);
Run Code Online (Sandbox Code Playgroud)

arrayfun不太可能在外部for循环上提供大量增益,但可能值得一试.

编辑:我应该注意,这个操作都没有wx需要排序才能正常工作.

编辑:fwiw,我决定做一些实际的性能测试,所以我运行了这个程序:

n = 100000; m = n;

N = n + m;

x = rand(n, 1);
w = [x; rand(m, 1)];

tic;
M = zeros(N,1);
for i = 1:N
  for j = 1:n
    if x(j) <= w(i)
      M(i) = M(i) + 1;
    end
  end
end
perf = toc;
fprintf(1, 'Original : %4.3f sec\n', perf);

w = sort(w); % presorted, so don't incur time cost;
tic;
M = histc(-x(:)',[-fliplr(w(:)') inf]);
M = cumsum(fliplr(M(1:N)))';
perf = toc;
fprintf(1, 'gnovice : %4.3f sec\n', perf);

tic;
M = zeros(N,1);
for i = 1:N
    M(i)=sum(x<=w(i));
end
perf = toc;
fprintf(1, 'mine/loop : %4.3f sec\n', perf);

tic;
M = arrayfun(@(wi)( sum( x <= wi ) ), w);
perf = toc;
fprintf(1, 'mine/arrayfun : %4.3f sec\n', perf);
Run Code Online (Sandbox Code Playgroud)

并获得n = 1000的这些结果:

Original : 0.086 sec
gnovice : 0.002 sec
mine/loop : 0.042 sec
mine/arrayfun : 0.070 sec
Run Code Online (Sandbox Code Playgroud)

对于n = 100000:

Original : too long to tell ( >> 1m )
gnovice : 0.050 sec
mine/loop : too long to tell ( >> 1m )
mine/arrayfun : too long to tell ( >> 1m )
Run Code Online (Sandbox Code Playgroud)