Multiplique uma matriz 3D com uma matriz 2D

Suponha que eu tenha uma matriz AxBxC X e uma matriz BxD Y

Existe um método não loop pelo qual eu possa multiplicar cada uma das matrizes C AxB com Y ?

Você pode fazer isso em uma linha usando as funções NUM2CELL para dividir a matriz X em uma matriz de células e CELLFUN para operar através das células:

 Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false); 

O resultado Z é uma matriz de células 1 por C em que cada célula contém uma matriz A por D. Se você quiser que Z seja uma matriz A-por-D-por-C , você pode usar a function CAT :

 Z = cat(3,Z{:}); 


NOTA: Minha solução antiga usava MAT2CELL em vez de NUM2CELL , o que não era tão sucinto:

 [A,B,C] = size(X); Z = cellfun(@(x) x*Y,mat2cell(X,A,B,ones(1,C)),'UniformOutput',false); 

Como uma preferência pessoal, gosto que meu código seja o mais sucinto e legível possível.

Aqui está o que eu teria feito, embora não atenda ao seu requisito de “sem loops”:

 for m = 1:C Z(:,:,m) = X(:,:,m)*Y; end 

Isso resulta em uma matriz A x D x C Z.

E, claro, você pode sempre pré-alocar Z para acelerar as coisas usando Z = zeros(A,D,C); .

Aqui está uma solução de uma linha (dois se você quiser dividir em 3ª dimensão):

 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]), [AB*C]), [BA*C])' * Y; %'# split into third dimension Z = permute(reshape(Z',[DAC]),[2 1 3]); 

Portanto, agora: Z(:,:,i) contém o resultado de X(:,:,i) * Y


Explicação:

O acima pode parecer confuso, mas a ideia é simples. Primeiro eu começo pegando a terceira dimensão de X e faço uma concatenação vertical ao longo da primeira dim:

 XX = cat(1, X(:,:,1), X(:,:,2), ..., X(:,:,C)) 

… a dificuldade era que C é uma variável, portanto você não pode generalizar essa expressão usando cat ou vertcat . Em seguida, multiplicamos isso por Y :

 ZZ = XX * Y; 

Finalmente eu divido de volta na terceira dimensão:

 Z(:,:,1) = ZZ(1:2, :); Z(:,:,2) = ZZ(3:4, :); Z(:,:,3) = ZZ(5:6, :); Z(:,:,4) = ZZ(7:8, :); 

Então você pode ver que isso requer apenas uma multiplicação de matrizes, mas você tem que reformular a matriz antes e depois.

Estou me aproximando exatamente do mesmo problema, com um olho para o método mais eficiente. Existem aproximadamente três abordagens que eu vejo por aí, além de usar bibliotecas externas (isto é, mtimesx ):

  1. Passe pelas fatias da matriz 3D
  2. feitiçaria repmat-and-permute
  3. multiplicação cellfun

Eu recentemente comparei todos os três methods para ver qual era o mais rápido. Minha intuição era que (2) seria o vencedor. Aqui está o 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]), [AB*C]), [BA*C])' * Y; Z2 = permute(reshape(Z2',[DAC]),[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 

Todas as três abordagens produziram o mesmo resultado (phew!), Mas, surpreendentemente, o loop foi o mais rápido:

 Elapsed time is 0.000418 seconds. Elapsed time is 0.000887 seconds. Elapsed time is 0.001841 seconds. 

Note que os tempos podem variar bastante de uma tentativa para outra, e algumas vezes (2) sai mais devagar. Essas diferenças se tornam mais dramáticas com dados maiores. Mas com dados muito maiores, (3) bate (2). O método de loop ainda é melhor.

 % 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. 

Mas o método de loop pode ser mais lento que (2), se a dimensão em loop for muito maior que as outras.

 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. 

Então (2) ganha por um fator grande, neste caso (talvez extremo). Pode não haver uma abordagem que seja ideal em todos os casos, mas o loop ainda é muito bom e o melhor em muitos casos. Também é melhor em termos de legibilidade. Loop away!

Não. Existem várias maneiras, mas sempre sai em um loop, direto ou indireto.

Só para agradar a minha curiosidade, por que você quer isso de qualquer maneira?

Para responder à pergunta e por legibilidade, consulte:

  • ndmult , por ajuanpi (Juan Pablo Carbajal), 2013, GNU GPL

Entrada

  • 2 matrizes
  • escurecer

Exemplo

  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]); 

Fonte

Fonte original. Eu adicionei comentários inline.

 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 

Eu recomendo que você use a checkbox de ferramentas MMX do Matlab. Pode multiplicar as matrizes n-dimensionais o mais rápido possível.

As vantagens da MMX são:

  1. É fácil de usar.
  2. Multiplique matrizes n-dimensionais (na verdade, pode multiplicar matrizes de matrizes 2D)
  3. Realiza outras operações de matriz (transposição, multiplicação quadrática, decomposição de colesterol e mais)
  4. Ele usa o compilador C e computação multi-thread para acelerar.

Para este problema, você só precisa escrever este comando:

 C=mmx('mul',X,Y); 

aqui está uma referência para todos os methods possíveis. Para mais detalhes, consulte esta questão .

  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 <=================== 

Eu pensaria em recursion, mas esse é o único outro método não-loop que você pode fazer

Você poderia “desenrolar” o loop, ou seja, escrever todas as multiplicações seqüencialmente que ocorreriam no loop

Eu tenho uma pergunta semelhante, mas uma matriz de 3-d X AxBxC e uma matriz de 2-D Y CxD e quero acabar com uma matriz de dados AxBxD.Dimensões:

A = 30 B = 70 C = 300 D = 100

A matriz 3-d, é uma variável dummy que leva valor =

1 em cada dimensão C nas instâncias AxB if (…) (e sum de todos Cs = 300), diferente para cada C.

0 caso contrário

A matriz 2-d é dados de séries temporais.

Meu maior problema é com a variável dummy.