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

5 次查看(过去 30 天)
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!

采纳的回答

Jan
Jan 2013-6-23
编辑:Jan 2013-6-23
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 个评论
Shane
Shane 2013-6-23
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!

请先登录,再进行评论。

更多回答(2 个)

Jan
Jan 2013-6-23
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 2013-6-23
编辑:Jan 2013-6-23
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.

类别

Help CenterFile Exchange 中查找有关 Loops and Conditional Statements 的更多信息

产品

Community Treasure Hunt

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

Start Hunting!

Translated by