Genere una matriz que contenga todas las combinaciones de elementos de n vectores (producto cartesiano)

Resuelto Luis Mendo asked hace 10 años • 4 respuestas

Esta pregunta surge con bastante frecuencia de una forma u otra (ver, por ejemplo, aquí o aquí ). Así que pensé en presentarlo de forma general y proporcionar una respuesta que podría servir como referencia futura.

Dado un número arbitrario nde vectores de tamaños posiblemente diferentes, genere una nmatriz de columnas cuyas filas describan todas las combinaciones de elementos tomados de esos vectores (producto cartesiano).

Por ejemplo,

vectors = { [1 2], [3 6 9], [10 20] }

debería dar

combs = [ 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 ]
Luis Mendo avatar Feb 20 '14 07:02 Luis Mendo
Aceptado

La ndgridfunción casi da la respuesta, pero tiene una advertencia: nlas variables de salida deben definirse explícitamente para llamarla. Dado que nes arbitrario, la mejor manera es utilizar una lista separada por comas (generada a partir de una matriz de celdas con nceldas) para que sirva como salida. Las nmatrices resultantes luego se concatenan en la nmatriz de columnas deseada:

vectors = { [1 2], [3 6 9], [10 20] }; %// input data: cell array of vectors

n = numel(vectors); %// number of vectors
combs = cell(1,n); %// pre-define to generate comma-separated list
[combs{end:-1:1}] = ndgrid(vectors{end:-1:1}); %// the reverse order in these two
%// comma-separated lists is needed to produce the rows of the result matrix in
%// lexicographical order 
combs = cat(n+1, combs{:}); %// concat the n n-dim arrays along dimension n+1
combs = reshape(combs,[],n); %// reshape to obtain desired matrix
Luis Mendo avatar Feb 20 '2014 00:02 Luis Mendo

Un poco más simple... si tienes la caja de herramientas Neural Network, simplemente puedes usar combvec:

vectors = {[1 2], [3 6 9], [10 20]};
combs = combvec(vectors{:}).' % Use cells as arguments

que devuelve una matriz en un orden ligeramente diferente:

combs =

     1     3    10
     2     3    10
     1     6    10
     2     6    10
     1     9    10
     2     9    10
     1     3    20
     2     3    20
     1     6    20
     2     6    20
     1     9    20
     2     9    20

Si quieres la matriz que está en la pregunta, puedes usar sortrows:

combs = sortrows(combvec(vectors{:}).')
% Or equivalently as per @LuisMendo in the comments: 
% combs = fliplr(combvec(vectors{end:-1:1}).') 

lo que da

combs =

     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

Si observa las partes internas de combvec(escriba edit combvecen la ventana de comandos), verá que usa un código diferente al de la respuesta de @LuisMendo. No puedo decir cuál es más eficiente en general.

Si tiene una matriz cuyas filas son similares a la matriz de celdas anterior, puede usar:

vectors = [1 2;3 6;10 20];
vectors = num2cell(vectors,2);
combs = sortrows(combvec(vectors{:}).')
horchler avatar Feb 20 '2014 00:02 horchler