How can I condition the number of nested for loops in a function on an input argument?

9 views (last 30 days)
I'm trying to write a function that will generate an array of ones and zeros within which each matrix represents one possibility of ones and zeros such that each row of each matrix sums to one. I want the two input arguments to be m (rows) and n (columns). I can do it pretty easily for a fixed number of rows. For instance, here's sample coding for three rows and n columns:
function Y = sa3xn(n)
Z = zeros(3,n,n^3);
for q = 1:n
for w = 1:n
for e = 1:n
Z(1,q,1+n^2*(q-1)+n^1*(w-1)+n^0*(e-1)) = 1;
Z(2,w,1+n^2*(q-1)+n^1*(w-1)+n^0*(e-1)) = 1;
Z(3,e,1+n^2*(q-1)+n^1*(w-1)+n^0*(e-1)) = 1;
end
end
end
Y = Z;
end
As you can see, if I wanted to make the number of rows (3 above) an input variable, m, I would need to have m nested for loops, and the argument in the interior of all the for loops would be m lines. Also, the exponents in that argument inside of all the for loops are {m-1, m-2, ..., 0} on each line of the argument, so this would also need to be captured somehow.
I could obviously do this with an if statement for each m, and just do it for the range of m's that I want to work with, but it'd be great to have a more concise and aesthetic way of making m an input variable. I'm certainly open to different structures if you have other suggestions.
Thanks in advance for any suggestions you might have!

Accepted Answer

Jan
Jan on 23 Jun 2013
Edited: Jan on 23 Jun 2013
And now for larger m the function sub2ind is the bottleneck. So at first I've inlined the code, omitted all unnecessary error checks and finally boiled down the code. It is faster not to create a temporary matrix and copy it into the 3D array, but to create a 2D-matrix directly and reshape it at the end:
function Z = sa_mxn(m, n)
Z = zeros(m*n, n^m);
row = 1:m;
col = ones(1, m);
for q = 1:n^m
index = row + (col - 1) * m; % Inlined SUB2IND
Z(index, q) = 1;
% Update col:
for iCol = m:-1:1
if col(iCol) < n
col(iCol) = col(iCol) + 1;
break;
end
col(iCol) = 1;
end
end
Z = reshape(Z, m,n,[]);
end
Some timings (R2009a/64, Win7, Core2Duo):
tic; for i=1:10, q = sa_mxn(8,3); end; toc
% SUB2IND approach:
Elapsed time is 3.486803 seconds.
% Inlined SUB2IND and lean:
Elapsed time is 0.193955 seconds.
  1 Comment
Shane
Shane on 23 Jun 2013
Thanks so much, Jan, for your answers - all three of them look like they'll give me what I need to move forward. I was also thinking of asking at a later date for more efficient ways of running this process, and you've addressed that very helpfully here. I very much appreciate your help!

Sign in to comment.

More Answers (2)

Jan
Jan on 23 Jun 2013
Instead of creating something like this:
Z(:,:,5) =
1 0 0
0 1 0
0 1 0
you could store the indices of the ones:
Z(:, 5) = [1, 2, 2];
Then this is equivalent to choosing 3 values of the set {1,2,3} with repetitions. This can be done by FEX: combinator (M-file) or FEX: VChooseKRO (MEX-file).

Jan
Jan on 23 Jun 2013
Edited: Jan on 23 Jun 2013
Another approach, which keeps your original data format:
function Z = sa_mxn(m, n)
Z = zeros(m, n, n^m);
siz = [m, n];
row = 1:m;
col = ones(1, m);
M = zeros(m, n);
for q = 1:n^m
M(:) = 0;
M(sub2ind(siz, row, col)) = 1;
Z(:, :, q) = M;
% Update col:
for iCol = m:-1:1
if col(iCol) < n
col(iCol) = col(iCol) + 1;
break;
end
col(iCol) = 1;
end
end
end
The idea is to use a vector of indices instead of nested loops.

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!