Create a Cartesian product with user-specified constraints

Hello,
I'm trying to figure out how to code a simple and flexible cartesian product function
To abstract how it works conceptually, let's imagine we have a 4 X 10 matrix, with each column having ordered values 1 through 4, like this
1 1 1 1 1 1 etc
2 2 2 2 2 2 etc
3 3 3 3 3 3 etc
4 4 4 4 4 4 etc
The user specifies the number of rows and columns to be used to make the cartesian product. For example, if they specify 2 rows and 3 columns, the result should be (although the order doesn't matter)
1 1 1
1 1 2
1 2 1
2 1 1
1 2 2
2 1 2
2 2 1
2 2 2
The closest function that I've found is
function C = cartesian(varargin)
% This function creates cartesian products from input arrays
args = varargin;
n = nargin;
[F{1:n}] = ndgrid(args{:});
for i=n:-1:1
G(:,i) = F{i}(:);
end
C = unique(G , 'rows');
end
However, it asks for a series of arrays as input. In my case, I need the input to be a matrix of variable dimensions. Thank you for any help!

4 Kommentare

The question is a bit ambiguous as to what portion of the array is to be selected. Is it leading rows and leading columns, or tailing rows and trailing columns? It matters if for example
M = magic(5)
M = 5×5
17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Emmanuel
Emmanuel am 3 Mär. 2026 um 22:00
Thank you for your help. Selection always begins from the top left. Also, the columns always begin with '1' and increment down.
Stephen23
Stephen23 am 4 Mär. 2026 um 4:13
Bearbeitet: Stephen23 am 4 Mär. 2026 um 11:00
"I just had to transpose my 'cartesian_index' matrix before splitting it."
You don't have to transpose anything, just split on the correct dimension:
nR = 2;
nC = 3;
X = repmat(1:nR,nC,1);
C = num2cell(X,2);
D = cartesian1(C{:})
D = 8×3
1 1 1 2 1 1 1 2 1 2 2 1 1 1 2 2 1 2 1 2 2 2 2 2
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
The function is simpler and likely more efficient without UNIQUE and the FOR loop:
function M = cartesian1(varargin)
[varargin{:}] = ndgrid(varargin{:});
M = reshape(cat(nargin+1, varargin{:}), [], nargin);
end
As posed creating that full matrix is superfluous anyway, you don't even need to duplicate all of those vectors, so you could make the whole thing simpler with one single vector:
nR = 2;
nC = 3;
D = cartesian2(1:nR,nC)
D = 8×3
1 1 1 2 1 1 1 2 1 2 2 1 1 1 2 2 1 2 1 2 2 2 2 2
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
function M = cartesian2(V,nC)
[C{1:nC}] = ndgrid(V);
M = reshape(cat(nC+1, C{:}), [], nC);
end
Emmanuel
Emmanuel am 5 Mär. 2026 um 17:13
Very clean and efficient solution! Thank you for taking the time to share your expertise.

Melden Sie sich an, um zu kommentieren.

 Akzeptierte Antwort

M = [1 1 1 1 1 1
2 2 2 2 2 2
3 3 3 3 3 3
4 4 4 4 4 4]
M = 4×6
1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Split = num2cell(M,2)
Split = 4×1 cell array
{[1 1 1 1 1 1]} {[2 2 2 2 2 2]} {[3 3 3 3 3 3]} {[4 4 4 4 4 4]}
cartesian(Split{:})

2 Kommentare

Thank you so much for your help. I like the idea of breaking up the matrix using num2cell. I tried implementing your suggestion with
search_rank = 2;
search_depth = 3;
cartesian_index = repmat((1:search_rank)', 1, search_depth)
cart_split = num2cell(cartesian_index,2)
cartesian(cart_split{:})
but the result was
ans =
1 2
whereas, I'd like to get back (the order doesn't matter)
ans =
1 1 1
1 1 2
1 2 1
2 1 1
1 2 2
2 1 2
2 2 1
2 2 2
Emmanuel
Emmanuel am 3 Mär. 2026 um 22:18
I got it to work! I just had to transpose my 'cartesian_index' matrix before splitting it. Thanks again for your great suggestion - I was stumped all morning on this.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (0)

Kategorien

Produkte

Version

R2025b

Gefragt:

am 3 Mär. 2026 um 20:12

Kommentiert:

am 5 Mär. 2026 um 17:13

Community Treasure Hunt

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

Start Hunting!

Translated by