Умножьте 3D-матрицу на 2D-матрицу
Предположим, у меня есть AxBxC матрица X
и BxD матрица Y
.
есть ли метод без цикла, с помощью которого я могу умножить каждый из C AxB матриц с Y
?
11 ответов
вы можете сделать это в одной строке, используя функции NUM2CELL разбить матрицу X
в массив ячеек и CELLFUN для работы в клетках:
Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);
результат Z
это 1-by-C массив ячеек, где каждая ячейка содержит элемент A-by-D матрица. Если вы хотите Z
быть A-by-D-by-C матрицы, вы можете использовать кошки функция:
Z = cat(3,Z{:});
Примечание: мое старое решение используется MAT2CELL вместо NUM2CELL, что было не так лаконично:
[A,B,C] = size(X);
Z = cellfun(@(x) x*Y,mat2cell(X,A,B,ones(1,C)),'UniformOutput',false);
в качестве личного предпочтения мне нравится, чтобы мой код был как можно более кратким и читаемым.
вот что я бы сделал, хотя это не соответствует вашему требованию "без петель":
for m = 1:C
Z(:,:,m) = X(:,:,m)*Y;
end
в результате а х D х c матрица Z.
и, конечно, вы всегда можете предварительно выделить Z, чтобы ускорить процесс, используя Z = zeros(A,D,C);
.
вот одно решение (два, если вы хотите разделить на 3-е измерение):
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]);
значит так: Z(:,:,i)
С результатом X(:,:,i) * Y
объяснение:
выше может показаться запутанным, но идея проста.
Сначала я начну с третьего измерения X
и сделайте вертикальную конкатенацию вдоль первого тусклого:
XX = cat(1, X(:,:,1), X(:,:,2), ..., X(:,:,C))
... трудность заключалась в том, что C
является переменной, следовательно, вы не можете обобщите это выражение, используя кошки или vertcat. Далее умножим это на Y
:
ZZ = XX * Y;
наконец я разделил его обратно в третье измерение:
Z(:,:,1) = ZZ(1:2, :);
Z(:,:,2) = ZZ(3:4, :);
Z(:,:,3) = ZZ(5:6, :);
Z(:,:,4) = ZZ(7:8, :);
таким образом, вы можете видеть, что для этого требуется только одно умножение матрицы, но вы должны изменить матрица до и после.
Я приближаюсь к той же проблеме, с прицелом на наиболее эффективный метод. Есть примерно три подхода, которые я вижу вокруг, за исключением использования внешних библиотек (т. е. mtimesx):
- петля через ломтики 3D-матрицы
- repmat-и-permute волшебство
- cellfun умножения
недавно я сравнил все три метода, чтобы увидеть, что было быстрее всего. Интуиция подсказывала мне, что победителем будет (2). Вот код:
% 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
все три подхода произвели один и тот же результат (фу!), но, на удивление, петля оказалась самой быстрой:
Elapsed time is 0.000418 seconds.
Elapsed time is 0.000887 seconds.
Elapsed time is 0.001841 seconds.
обратите внимание, что может значительно изменяться от одного испытания к другому, и иногда (2) выходит медленнее. Эти различия становятся более резкими с увеличением объема данных. Но с много большие данные, (3) удары (2). Метод loop по-прежнему лучше всего.
% 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.
но метод loop can будьте медленнее, чем (2), Если зацикленное измерение намного больше других.
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.
Итак (2) выигрывает большой фактор, в этом (возможно, крайнем) случае. Возможно, нет оптимального подхода во всех случаях, но цикл все еще довольно хорош, и лучше всего во многих случаях. Это также лучше всего с точки зрения читаемости. Петля прочь!
Неа. Есть несколько способов, но он всегда выходит в цикле, прямой или косвенный.
просто чтобы удовлетворить мое любопытство, зачем вам нужен этот ?
чтобы ответить на вопрос, и для удобства чтения см.:
- ndmult, ajuanpi (Juan Pablo Carbajal), 2013, GNU GPL
вход
- 2 массивы
- Дим
пример
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]);
источник
первоисточник. Я добавил встроенные комментарии.
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
я настоятельно рекомендую вам использовать MMX toolbox из matlab. Он может умножать n-мерные матрицы как можно быстрее.
преимущества MMX являются:
- это легко использовать.
- умножение n-мерных матриц (на самом деле он может умножать массивы 2-D матриц)
- выполняет другие операции с матрицами. (транспонировать, квадратичное умножение, Chol разложение и многое другое)
- он использует компилятор C и Multi-резьбы вычисления для ускорения.
для этой проблемы вам просто нужно написать эту команду:
C=mmx('mul',X,Y);
вот ориентир для всех возможных методов. Для более подробной информации обратитесь к этому вопрос.
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 <===================
вы можете "развернуть" цикл, то есть записать все умножения последовательно, которые будут происходить в цикле
У меня есть аналогичный вопрос, но 3-d матрица X AxBxC и 2-d матрица Y CxD и хотите закончить с AxBxD матрица данных.Размеры:
A = 30 B = 70 C = 300 D = 100
3-d матрица, является фиктивной переменной, которая принимает значение =
1 в каждом измерении C в экземплярах AxB if (...) (и сумма всех Cs = 300), различная для каждого C.
0 в противном случае
2-d матрица-это данные временных рядов.
моя самая большая проблема с фиктивной переменной.
Я написал функцию общего назначения для произвольных тензорных сокращений в MATLAB, используя функцию умножения внутренней матрицы, поэтому она очень эффективна и распараллелена. Он доступен на FileExchange и github