Frage

Angenommen, ich habe eine AxBxC Matrix X und eine BXD Matrix Y.

Gibt es eine nicht-Schleife Methode, mit der ich kann mehrfach jeweils die C A · B Matrizen mit Y?

War es hilfreich?

Lösung

Sie können dies mit den Funktionen in einer Zeile tun NUM2CELL die Matrix X in eine Zellenanordnung zu brechen und CELLFUN über die Zellen arbeiten:

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

Das Ergebnis ist eine Z 1-by-C Zellenfeld, wobei jede Zelle enthält ein A-by-D Matrix. Wenn Sie möchten, Z ein A-by-D-by-C sein Matrix, können Sie die CAT Funktion:

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



Hinweis: Meine alte Lösung MAT2CELL statt NUM2CELL , die nicht so knapp war:

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

Andere Tipps

Als persönliche Präferenz, ich mag meinen Code so prägnant und lesbar wie möglich sein.

Hier ist, was ich getan hätte, wenn es Sich nicht erfüllt "No-Schleifen Anforderung:

for m = 1:C

    Z(:,:,m) = X(:,:,m)*Y;

end

Dies führt zu einem A x D x C Matrix Z .

Und natürlich kann man immer von Z nach Geschwindigkeit Dingen vorbelegt up von Z = zeros(A,D,C); verwenden.

Hier ist eine einzeilige Lösung (zwei, wenn Sie in der 3. Dimension geteilt werden sollen):

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

So jetzt: Z(:,:,i) enthält das Ergebnis X(:,:,i) * Y


Erklärung:

Die oben gezeigt aussehen kann verwirrend, aber die Idee ist einfach. Zunächst beginne ich durch die dritte Dimension von X nehmen und tun eine vertikale Verkettung entlang der ersten dim:

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

... war die Schwierigkeit, dass C eine Variable ist, daher kann man nicht verallgemeinern, dass die Expression mit Katze oder vertcat . Als nächstes werden wir multiplizieren Sie diese mit Y:

ZZ = XX * Y;

Schließlich teile ich es zurück in die dritte Dimension:

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

So können Sie sehen, es erfordert nur eine Matrixmultiplikation, aber Sie müssen reshape die Matrix vor und nach.

Ich bin Annäherung an das exakt gleiche Problem, mit einem Auge für die effizienteste Methode. Es gibt ungefähr drei Ansätze, die ich sehe, um, kurz außerhalb Bibliotheken verwenden (dh mtimesx ):

  1. Schleife durch Scheiben der 3D-Matrix
  2. repmat-and-permute wizardry
  3. cellfun Multiplikation

ich alle drei Methoden vor kurzem im Vergleich zu sehen, welche war schnellste. Meine Intuition war, dass (2) würde der Sieger sein. Hier ist der Code:

% 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

Alle drei Ansätze erzeugen die gleiche Leistung, aber überraschenderweise die Schleife war die schnellste (puh!):

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

Beachten Sie, dass die Zeiten ziemlich viel von einem Versuch zum anderen variieren kann, und manchmal (2) kommt die langsamste aus. Diese Unterschiede werden noch dramatischer mit größeren Daten. Aber mit viel größer Daten, (3) Schläge (2). Die Schleife Methode ist immer noch am besten.

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

Aber die Schleife Methode können langsamer als (2), wenn die geschlungenen Dimension ist viel größer als die anderen.

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.

So (2) gewinnt durch einen großen Faktor in diesem (vielleicht extremen) Fall. Es kann nicht ein Ansatz sein, der optimal in allen Fällen, aber die Schleife ist immer noch ziemlich gut, und am besten in vielen Fällen. Es ist auch am besten in Bezug auf die Lesbarkeit. Schleife weg!

Nein. Es gibt mehrere Möglichkeiten, aber es kommt immer heraus in einer Schleife, direkt oder indirekt.

Nur meine Neugier gefallen, warum soll man das überhaupt will?

Um die Frage zu beantworten, und für Lesbarkeit finden Sie unter:

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

Eingabe

  • 2 Arrays
  • dim

Beispiel

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

Quelle

Original-Quelle. Ich fügte hinzu, Inline-Kommentare.

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

Ich empfehle Ihnen, verwenden Sie die MMX Toolbox von Matlab. Es kann mehrfach n-dimensionale Matrix so schnell wie möglich.

Die Vorteile von MMX sind:

  1. Es ist leicht verwenden.
  2. Multiply n-dimensionale Matrizen (tatsächlich kann es multiplizieren Arrays von 2-D-Matrizen)
  3. Sie führt andere Matrix-Operationen (transponieren, Quadratic Multiplizieren, Chol Zersetzung und mehr)
  4. Sie verwendet C Compiler und Multi-Thread Berechnung für Geschwindigkeit auf.

Für dieses Problem, Sie müssen nur diesen Befehl schreiben:

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

Hier ist ein Benchmark für alle möglichen Methoden. Weitere Einzelheiten finden Sie in diesem Frage .

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

würde ich denken, Rekursion, aber das ist die einzige andere nicht-Loop-Methode, die Sie tun können,

Sie könnten „entrollen“ die Schleife, dh Schreib aus allen Multiplikationen sequentiell das würde in der Schleife

auftreten

Ich möchte meine Antwort auf die Probleme von teilen:

1) die an den Tensorprodukt von zwei Tensoren (jeglicher Valenz);

2) macht die Kontraktion von zwei Tensoren entlang jeder Dimension.

Hier sind meine Subroutinen für die ersten und zweiten Aufgaben:

1) Tensorprodukt:

function [C] = tensor(A,B)
   C = squeeze( reshape( repmat(A(:), 1, numel(B)).*B(:).' , [size(A),size(B)] ) );
end

2) Schrumpfung: Hier A und B sind die Tensoren jeweils entlang der dimesions i und j zusammengezogen werden. Die Längen dieser Dimensionen sollten gleich sein, natürlich. Es gibt keine Prüfung für diesen (dies den Code verschleiern würde), aber abgesehen davon, es funktioniert gut.

   function [C] = tensorcontraction(A,B, i,j)
      sa = size(A);
      La = length(sa);
      ia = 1:La;
      ia(i) = [];
      ia = [ia i];

      sb = size(B);
      Lb = length(sb);
      ib = 1:Lb;
      ib(j) = [];
      ib = [j ib];

      % making the i-th dimension the last in A
      A1 = permute(A, ia);
      % making the j-th dimension the first in B
      B1 = permute(B, ib);

      % making both A and B 2D-matrices to make use of the
      % matrix multiplication along the second dimension of A
      % and the first dimension of B
      A2 = reshape(A1, [],sa(i));
      B2 = reshape(B1, sb(j),[]);

      % here's the implicit implication that sa(i) == sb(j),
      % otherwise - crash
      C2 = A2*B2;

      % back to the original shape with the exception
      % of dimensions along which we've just contracted
      sa(i) = [];
      sb(j) = [];
      C = squeeze( reshape( C2, [sa,sb] ) );
   end

Jeder Kritiker?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top