matlab - español - Multiplicar una matriz 3D con una matriz 2D
concatenar matrices matlab (10)
Supongamos que tengo una matriz X
AxBxC y una matriz Y
BxD .
¿Hay un método sin bucle por el cual puedo multiplicar cada una de las matrices C AxB con Y
?
Aquí hay una solución de una línea (dos si desea dividirla en 3ra dimensión):
A = 2;
B = 3;
C = 4;
D = 5;
X = rand(A,B,C);
Y = rand(B,D);
%# calculate result in one big matrix
Z = reshape(reshape(permute(X, [2 1 3]), [A B*C]), [B A*C])'' * Y;
%''# split into third dimension
Z = permute(reshape(Z'',[D A C]),[2 1 3]);
Por lo tanto, ahora: Z(:,:,i)
contiene el resultado de X(:,:,i) * Y
Explicación:
Lo anterior puede parecer confuso, pero la idea es simple. Primero empiezo tomando la tercera dimensión de X
y hago una concatenación vertical a lo largo del primer dim:
XX = cat(1, X(:,:,1), X(:,:,2), ..., X(:,:,C))
... la dificultad era que C
es una variable, por lo tanto, no se puede generalizar esa expresión usando cat o vertcat . Luego multiplicamos esto por Y
:
ZZ = XX * Y;
Finalmente lo dividí nuevamente en la tercera dimensión:
Z(:,:,1) = ZZ(1:2, :);
Z(:,:,2) = ZZ(3:4, :);
Z(:,:,3) = ZZ(5:6, :);
Z(:,:,4) = ZZ(7:8, :);
Entonces puede ver que solo requiere una multiplicación de matrices, pero debe remodelar la matriz antes y después.
Como preferencia personal, me gusta que mi código sea lo más breve y legible posible.
Esto es lo que habría hecho, aunque no cumple con su requisito de ''no-bucles'':
for m = 1:C
Z(:,:,m) = X(:,:,m)*Y;
end
Esto da como resultado una matriz A x D x C Z.
Y, por supuesto, siempre puede preasignar Z para acelerar las cosas usando Z = zeros(A,D,C);
.
Le recomiendo que use la caja de herramientas MMX de matlab. Puede multiplicar matrices n-dimensionales lo más rápido posible.
Las ventajas de MMX son:
- Es fácil de usar
- Multiplicar matrices n-dimensionales (en realidad puede multiplicar matrices de matrices 2-D)
- Realiza otras operaciones matriciales (transposición, multiplicación cuadrática, descomposición de Chol y más)
- Utiliza el compilador de C y el cálculo de múltiples hilos para acelerar.
Para este problema, solo necesita escribir este comando:
C=mmx(''mul'',X,Y);
aquí hay un punto de referencia para todos los métodos posibles. Para más detalles, consulte esta question .
1.6571 # FOR-loop
4.3110 # ARRAYFUN
3.3731 # NUM2CELL/FOR-loop/CELL2MAT
2.9820 # NUM2CELL/CELLFUN/CELL2MAT
0.0244 # Loop Unrolling
0.0221 # MMX toolbox <===================
Me estoy acercando exactamente al mismo problema, con un ojo para el método más eficiente. Hay aproximadamente tres enfoques que veo a mi alrededor, mtimesx uso de bibliotecas externas (es decir, mtimesx ):
- Bucle a través de las rebanadas de la matriz 3D
- hechicería repmat-y-permute
- multiplicación de cellfun
Recientemente comparé los tres métodos para ver cuál fue el más rápido. Mi intuición fue que (2) sería el ganador. Aquí está el código:
% generate data
A = 20;
B = 30;
C = 40;
D = 50;
X = rand(A,B,C);
Y = rand(B,D);
% ------ Approach 1: Loop (via @Zaid)
tic
Z1 = zeros(A,D,C);
for m = 1:C
Z1(:,:,m) = X(:,:,m)*Y;
end
toc
% ------ Approach 2: Reshape+Permute (via @Amro)
tic
Z2 = reshape(reshape(permute(X, [2 1 3]), [A B*C]), [B A*C])'' * Y;
Z2 = permute(reshape(Z2'',[D A C]),[2 1 3]);
toc
% ------ Approach 3: cellfun (via @gnovice)
tic
Z3 = cellfun(@(x) x*Y,num2cell(X,[1 2]),''UniformOutput'',false);
Z3 = cat(3,Z3{:});
toc
Los tres enfoques produjeron el mismo resultado (¡uf!), Pero, sorprendentemente, el bucle fue el más rápido:
Elapsed time is 0.000418 seconds.
Elapsed time is 0.000887 seconds.
Elapsed time is 0.001841 seconds.
Tenga en cuenta que los tiempos pueden variar mucho de una prueba a otra, y en ocasiones (2) sale más lento. Estas diferencias se vuelven más dramáticas con datos más grandes. Pero con datos mucho más grandes, (3) latidos (2). El método de bucle sigue siendo el mejor.
% pretty big data...
A = 200;
B = 300;
C = 400;
D = 500;
Elapsed time is 0.373831 seconds.
Elapsed time is 0.638041 seconds.
Elapsed time is 0.724581 seconds.
% even bigger....
A = 200;
B = 200;
C = 400;
D = 5000;
Elapsed time is 4.314076 seconds.
Elapsed time is 11.553289 seconds.
Elapsed time is 5.233725 seconds.
Pero el método de bucle puede ser más lento que (2), si la dimensión del bucle es mucho más grande que las otras.
A = 2;
B = 3;
C = 400000;
D = 5;
Elapsed time is 0.780933 seconds.
Elapsed time is 0.073189 seconds.
Elapsed time is 2.590697 seconds.
Entonces (2) gana por un factor importante, en este caso (tal vez extremo). Puede que no haya un enfoque que sea óptimo en todos los casos, pero el ciclo sigue siendo bastante bueno, y el mejor en muchos casos. También es mejor en términos de legibilidad. ¡Bucle lejos!
Nop. Hay varias formas, pero siempre sale en un bucle, directo o indirecto.
Solo para complacer mi curiosidad, ¿por qué querrías eso de todos modos?
Para responder a la pregunta y para leer, consulte:
- ndmult , por ajuanpi (Juan Pablo Carbajal), 2013, GNU GPL
Entrada
- 2 matrices
- oscuro
Ejemplo
nT = 100;
t = 2*pi*linspace (0,1,nT)’;
# 2 experiments measuring 3 signals at nT timestamps
signals = zeros(nT,3,2);
signals(:,:,1) = [sin(2*t) cos(2*t) sin(4*t).^2];
signals(:,:,2) = [sin(2*t+pi/4) cos(2*t+pi/4) sin(4*t+pi/6).^2];
sT(:,:,1) = signals(:,:,1)’;
sT(:,:,2) = signals(:,:,2)’;
G = ndmult (signals,sT,[1 2]);
Fuente
Fuente original. Agregué comentarios en línea.
function M = ndmult (A,B,dim)
dA = dim(1);
dB = dim(2);
# reshape A into 2d
sA = size (A);
nA = length (sA);
perA = [1:(dA-1) (dA+1):(nA-1) nA dA](1:nA);
Ap = permute (A, perA);
Ap = reshape (Ap, prod (sA(perA(1:end-1))), sA(perA(end)));
# reshape B into 2d
sB = size (B);
nB = length (sB);
perB = [dB 1:(dB-1) (dB+1):(nB-1) nB](1:nB);
Bp = permute (B, perB);
Bp = reshape (Bp, sB(perB(1)), prod (sB(perB(2:end))));
# multiply
M = Ap * Bp;
# reshape back to original format
s = [sA(perA(1:end-1)) sB(perB(2:end))];
M = squeeze (reshape (M, s));
endfunction
Podrías "desenrollar" el ciclo, es decir, escribir todas las multiplicaciones secuencialmente que ocurrirían en el ciclo
Puede hacer esto en una línea usando las funciones NUM2CELL para dividir la matriz X
en una matriz de celdas y CELLFUN para operar a través de las celdas:
Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),''UniformOutput'',false);
El resultado Z
es una matriz de celdas 1 por C donde cada celda contiene una matriz A por D. Si desea que Z
sea una matriz A-by-D-by-C , puede usar la función CAT :
Z = cat(3,Z{:});
NOTA: Mi solución anterior usaba MAT2CELL lugar de NUM2CELL , que no era tan breve:
[A,B,C] = size(X);
Z = cellfun(@(x) x*Y,mat2cell(X,A,B,ones(1,C)),''UniformOutput'',false);
Tengo una pregunta similar, pero una matriz 3-D X AxBxC y una matriz 2-d Y CxD y quiero terminar con una matriz de datos AxBxD.Dimensiones:
A = 30 B = 70 C = 300 D = 100
La matriz 3-d, es una variable ficticia que toma valor =
1 en cada dimensión C en las instancias AxB si (...) (y suma de todas las Cs = 300), diferentes para cada C.
0 de lo contrario
La matriz 2-d es datos de series de tiempo.
Mi mayor problema es con la variable ficticia.
Yo pensaría en la recursividad, pero ese es el único otro método que no se puede repetir