Moltiplicare una matrice 3D con una matrice 2D

Supponiamo che io abbia una matrice AxBxC X e una matrice BxD Y

Esiste un metodo non loop con cui posso moltiplicare ciascuna delle matrici C AxB con Y ?

Puoi farlo in una riga usando le funzioni NUM2CELL per interrompere la matrice X in un array di celle e CELLFUN per operare attraverso le celle:

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

Il risultato Z è un array di celle 1-by-C in cui ogni cella contiene una matrice A-D . Se vuoi che Z sia una matrice A-by-D-by-C , puoi usare la funzione CAT :

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


NOTA: la mia vecchia soluzione utilizza MAT2CELL anziché NUM2CELL , che non era così succinta:

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

Come preferenza personale, mi piace che il mio codice sia il più sintetico e leggibile ansible.

Ecco cosa avrei fatto, anche se non soddisfa i requisiti del “no-loops”:

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

Ciò risulta in una matrice Z A x D x C.

E, naturalmente, puoi sempre pre-allocare Z per accelerare le cose usando Z = zeros(A,D,C); .

Ecco una soluzione a una riga (due se vuoi dividere in 3a dimensione):

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

Quindi ora: Z(:,:,i) contiene il risultato di X(:,:,i) * Y


Spiegazione:

Quanto sopra può sembrare confuso, ma l’idea è semplice. Per prima cosa inizio prendendo la terza dimensione di X e faccio una concatenazione verticale lungo la prima dim:

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

… la difficoltà era che C è una variabile, quindi non è ansible generalizzare quell’espressione usando cat o vertcat . Successivamente lo moltiplichiamo per Y :

 ZZ = XX * Y; 

Alla fine l’ho diviso nella terza dimensione:

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

Quindi puoi vederlo richiede solo una moltiplicazione di matrice, ma devi rimodellare la matrice prima e dopo.

Mi sto avvicinando allo stesso identico problema, con un occhio per il metodo più efficiente. Ci sono circa tre approcci che vedo in giro, a meno di usare le librerie esterne (es. Mtimesx ):

  1. Passare attraverso le sezioni della matrice 3D
  2. wizardry repmat-and-permute
  3. moltiplicazione cellfun

Ho recentemente confrontato tutti e tre i metodi per vedere quale è stato il più veloce. La mia intuizione era che (2) sarebbe stato il vincitore. Ecco il codice:

 % 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 

Tutti e tre gli approcci hanno prodotto la stessa uscita (phew!), Ma, sorprendentemente, il ciclo è stato il più veloce:

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

Si noti che i tempi possono variare molto da una prova all’altra, e a volte (2) risulta il più lento. Queste differenze diventano più drammatiche con dati più grandi. Ma con dati molto più grandi, (3) battiti (2). Il metodo loop è ancora il migliore.

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

Ma il metodo loop può essere più lento di (2), se la dimensione del loop è molto più grande delle altre.

 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. 

Quindi (2) vince per un grande fattore, in questo caso (forse estremo). Potrebbe non esserci un approccio ottimale in tutti i casi, ma il ciclo è ancora abbastanza buono e il migliore in molti casi. È anche il migliore in termini di leggibilità. Andate via!

No. Ci sono diversi modi, ma esce sempre in un ciclo, diretto o indiretto.

Solo per compiacere la mia curiosità, perché lo vorresti comunque?

Per rispondere alla domanda e per leggibilità, vedere:

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

Ingresso

  • 2 array
  • offuscare

Esempio

  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 originale. Ho aggiunto commenti in linea.

 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 

Consiglio vivamente di utilizzare la toolbox MMX di MATLAB. Può moltiplicare matrici n-dimensionali il più velocemente ansible.

I vantaggi di MMX sono:

  1. È facile da usare
  2. Matrici multidimensionali moltiplicate (in realtà può moltiplicare matrici di matrici 2D)
  3. Esegue altre operazioni sulla matrice (trasposizione, moltiplicazione quadratica, decomposizione di Chol e altro)
  4. Usa il compilatore C e il calcolo multithread per accelerare.

Per questo problema, devi solo scrivere questo comando:

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

ecco un punto di riferimento per tutti i possibili metodi. Per maggiori dettagli, fai riferimento a questa domanda .

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

Penserei la ricorsione, ma questo è l’unico altro metodo non ciclico che puoi fare

Si potrebbe “srotolare” il ciclo, cioè scrivere tutte le moltiplicazioni in sequenza che si verificherebbero nel ciclo

Ho una domanda simile, ma con una matrice 3D X AxBxC e una matrice 2x Y CxD e voglio finire con una matrice dati AxBxD. Dimensioni:

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

La matrice 3-d è una variabile fittizia che assume valore =

1 in ogni dimensione C nelle istanze AxB if (…) (e sum di tutti i C = 300), diversi per ogni C.

0 altrimenti

La matrice 2-d è dati di serie di volte.

Il mio più grande problema è con la variabile dummy.