Discover MakerZone

MATLAB and Simulink resources for Arduino, LEGO, and Raspberry Pi

Learn more

Discover what MATLAB® can do for your career.

Opportunities for recent engineering grads.

Apply Today

To resolve issues starting MATLAB on Mac OS X 10.10 (Yosemite) visit: http://www.mathworks.com/matlabcentral/answers/159016

Get rid of nested for loops?

Asked by Sid D on 9 May 2013

I am trying to use the following code, which has way too many nested for loops. However, I'm a newbie, and don't have an obvious way to make this code more efficient. Does anyone have any suggestions?

Basically, I have a structural array (v) with four 2441x226 components: vx, vy, x, and y. vx and vy contain the x and y components of a vector field, and x and y label the position of each vector in that field. The code calculates how the correlation between the vectors (finalplot(:,2)) decays with Cartesian distance (finalplot(:,1)).

Thanks for your help!

finalplot=zeros(2452,2);
norm=zeros(2452,1);
for b=1:226  
    for a=1:2441
for j=1:226
    for i=1:2441
if isnan(v.vx(a,b)*v.vx(i,j)+v.vy(a,b)*v.vy(i,j)) %this line just gets rid of all the NaN's that are in the vector field
else
      norm(1+round(sqrt((i-a)^2+(j-b)^2)),1)=norm(1+round(sqrt((i-a)^2+(j-b)^2)),1)+1;
      finalplot(1+round(sqrt((i-a)^2+(j-b)^2)),2)=finalplot(1+round(sqrt((i-a)^2+(j-b)^2)),2)+(v.vx(a,b)*v.vx(i,j)+v.vy(a,b)*v.vy(i,j))/(sqrt((v.vx(a,b))^2+(v.vy(a,b))^2)*sqrt((v.vx(i,j))^2+(v.vy(i,j))^2));
end
      end
  end
      end
  end
for i=1:2452
finalplot(i,1)=i;
finalplot(i,2)=finalplot(i,2)/norm(i,1);
end

0 Comments

Sid D

Products

No products are associated with this question.

2 Answers

Answer by Teja Muppirala on 10 May 2013
Accepted answer

A few observations:

1. A lot of the operations you are doing can me written more efficiently as matrix operations (dot products, etc.), or operations on entire matrices at once, without needing to loop over elements. For example, normalizing the vectors does not need any explicit loops at all.

2. You are checking every point with every other point. But checking point A with point B is the same as checking point B with point A, so you really only need to do half the calculations.

3. Not pertinent to speeding this up, but "norm" is the name of a very useful MATLAB command. It is generally a good idea to avoid calling your variables the same thing as a built-in MATLAB function.

This gives the same results as your code, but is much faster:

V = [v.vx(:) v.vy(:)]; % Line up the vectors in a big vertival matrix
V1 = bsxfun(@rdivide,V,sqrt(v.vx(:).^2+v.vy(:).^2)); %Normalize it
P = [v.x(:) v.y(:)]; % Matrix of all the (x,y) points
finalplot=zeros(NumOut,1);
loccount=zeros(NumOut,1); %What you called "norm"
notnan = find(~isnan(v.vx(:)) & ~isnan(v.vy(:))); % Find the NaNs
P = P(notnan,:); %Get rid of the NaN locations from the start
V1 = V1(notnan,:);
N = numel(notnan);
for n = 1:N  %Loop only once
    locations = 1+round(sqrt(sum(bsxfun(@minus,P(n,:),...
                P(n+1:N,:)).^2,2)));
    loccount =  loccount + accumarray(locations,1,[NumOut 1]);
    finalplot = finalplot + accumarray(locations,...
                V1(n+1:N,:)*V1(n,:)',[NumOut 1]);
end
finalplot = [(1:numel(finalplot))' finalplot./loccount];
finalplot(1,2) = 1; %Fix the first value 

Notes:

1. The code above assumes your x and y data is like NDGRID, not like MESHGRID. Compare

[x,y] = ndgrid(1:5)
[x,y] = meshgrid(1:5)

If your data is like MESHGRID, then you'd need to change:

P = [v.x(:) v.y(:)]; % Matrix of all the (x,y) points

to

P = [reshape(v.x',[],1) reshape(v.y',[],1)];

to make it match your code exactly.

2. The remaining one FOR loop is highly parallelizable. Every iteration can be done independently. If you have the Parallel Computing Toolbox, or MATLAB Distributed Computing Server, and access to a computer with multiple CPU cores or a computing cluster, you can change the FOR loop into a PARFOR loop and run the calculation in parallel, greatly reducing calculation time.

0 Comments

Teja Muppirala
Answer by Roger Stafford on 9 May 2013
Edited by Roger Stafford on 9 May 2013

The nested loops are difficult to avoid for this type of procedure. However, there are other aspects of your computation that can be made more efficient. There is too much repetition of operations, in particular too many square root operations. It is better to process the vector field first so that all vectors are of unit magnitude. I have placed the results of doing so in arrays nx and ny. I put a NaN in vx when needed to inhibit calculations later. Also you should only compute the index

 1+round(sqrt((i-a)^2+(j-b)^2))

once for each a,b,i,j combination and save it (in t2) rather than four times for each combination.

 nx = zeros(2441,226);
 ny = zeros(2441,226);
 for b = 1:226
  for a = 1:2441
   if isnan(v.vx(a,b)) | isnan(v.vy(a,b))
    nx(a,b) = NaN;
   else
    t1 = sqrt((v.vx(a,b))^2+(v.vy(a,b))^2);
    nx(a,b) = v.vx(a,b)/t1;
    ny(a,b) = v.vy(a,b)/t1;
   end
  end
 end
 finalplot = zeros(2452,2);
 norm = zeros(2452,1);
 for b = 1:226  
  for a = 1:2441
   if ~isnan(nx(a,b))
    for j = 1:226
     for i = 1:2441
      if ~isnan(nx(i,j)
       t2 = 1+round(sqrt((i-a)^2+(j-b)^2));
       norm(t2) = norm(t2)+1;
       finalplot(t2,2) = finalplot(t2,2) + nx(a,b)*nx(i,j)+ny(a,b)*ny(i,j);
      end
     end
    end
   end
  end
 end
 finalplot(:,1) = 1:2452;
 finalplot(:,2) = finalplot(:,2)./norm(:,1)

0 Comments

Roger Stafford

Contact us