This question pops up quite often in one form or another (see for example here or here). So I thought I\'d present it in a general form, and provide an answer which might se
Here's a do-it-yourself method that made me giggle with delight, using nchoosek
, although it's not better than @Luis Mendo's accepted solution.
For the example given, after 1,000 runs this solution took my machine on average 0.00065935 s, versus the accepted solution 0.00012877 s. For larger vectors, following @Luis Mendo's benchmarking post, this solution is consistently slower than the accepted answer. Nevertheless, I decided to post it in hopes that maybe you'll find something useful about it:
Code:
tic;
v = {[1 2], [3 6 9], [10 20]};
L = [0 cumsum(cellfun(@length,v))];
V = cell2mat(v);
J = nchoosek(1:L(end),length(v));
J(any(J>repmat(L(2:end),[size(J,1) 1]),2) | ...
any(J<=repmat(L(1:end-1),[size(J,1) 1]),2),:) = [];
V(J)
toc
gives
ans =
1 3 10
1 3 20
1 6 10
1 6 20
1 9 10
1 9 20
2 3 10
2 3 20
2 6 10
2 6 20
2 9 10
2 9 20
Elapsed time is 0.018434 seconds.
Explanation:
L
gets the lengths of each vector using cellfun
. Although cellfun
is basically a loop, it's efficient here considering your number of vectors will have to be relatively low for this problem to even be practical.
V
concatenates all the vectors for easy access later (this assumes you entered all your vectors as rows. v' would work for column vectors.)
nchoosek
gets all the ways to pick n=length(v)
elements from the total number of elements L(end)
. There will be more combinations here than what we need.
J =
1 2 3
1 2 4
1 2 5
1 2 6
1 2 7
1 3 4
1 3 5
1 3 6
1 3 7
1 4 5
1 4 6
1 4 7
1 5 6
1 5 7
1 6 7
2 3 4
2 3 5
2 3 6
2 3 7
2 4 5
2 4 6
2 4 7
2 5 6
2 5 7
2 6 7
3 4 5
3 4 6
3 4 7
3 5 6
3 5 7
3 6 7
4 5 6
4 5 7
4 6 7
5 6 7
Since there are only two elements in v(1)
, we need to throw out any rows where J(:,1)>2
. Similarly, where J(:,2)<3
, J(:,2)>5
, etc... Using L
and repmat
we can determine whether each element of J
is in its appropriate range, and then use any
to discard rows that have any bad element.
Finally, these aren't the actual values from v
, just indices. V(J)
will return the desired matrix.