Как умножить тензоры в MATLAB без цикла?

предположим, что я:

A = rand(1,10,3);
B = rand(10,16);

и я хочу к вам:

C(:,1) = A(:,:,1)*B;
C(:,2) = A(:,:,2)*B;
C(:,3) = A(:,:,3)*B;

могу ли я как-то умножить это в одной строке, чтобы это было быстрее?

Что делать, если я создам новый тензор b, как это

for i = 1:3
    b(:,:,i) = B;
end

могу ли я умножить A и b, чтобы получить тот же C, но быстрее? Время, затраченное на создание B циклом выше, не имеет значения, так как мне понадобится C для многих разных A-s, в то время как B остается тем же.

4 ответов


переставить размеры A и B а затем применить умножение матрицы:

C = B.'*permute(A, [2 3 1]);

если A это правда 3D массив, что-то вроде A = rand(4,10,3) и предполагая, что B остается в виде 2D-массива, затем каждый A(:,:,1)*B даст 2D-массив.

Итак, предполагая, что вы хотите сохранить эти 2D-массивы в виде срезов в третьем измерении выходного массива,C вот так -

C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B; and so on.

чтобы решить это векторизованным образом, одним из подходов было бы использование reshape A в 2D массив, объединяющий первое и третье измерения и после выполнения матрицы-muliplication. Наконец, чтобы привести размер вывода такой же, как и ранее перечисленные C, нам нужен последний шаг перестройки.

реализация будет выглядеть примерно так -

%// Get size and then the final output C
[m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);

образец выполнения -

>> A = rand(4,10,3);
B = rand(10,16);

C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B;
>> [m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
>> all(C(:)==out(:)) %// Verify results
ans =
     1

на comments, если A представляет собой 3D-массив с всегда одноэлементным измерением в начале, вы можете просто использовать squeeze а затем матрица-умножение, как так -

C = B.'*squeeze(A)

EDIT: @LuisMendo указывает, что это действительно возможно для этого конкретного случая использования. Однако это (в целом) невозможно, если первое измерение A не равно 1.

Я боролся с этим некоторое время, и я никогда не был в состоянии придумать решение. Выполнение элементарных вычислений производится хорошо по bsxfun, но тензорное умножение-это то, что, к сожалению, не поддерживается. Извините, и удачи!

вы можете проверить этот файл обмена файлами mathworks, что облегчит вам и поддержит поведение, которое вы ищете, но я считаю, что он также полагается на циклы. редактировать: он полагается на MEX/C++, поэтому это не чистое решение MATLAB, если это то, что вы ищете.


Я должен согласиться с @GJSein, цикл for действительно быстрый

time
    0.7050    0.3145

вот функция таймера

function time

    n = 1E7;
    A = rand(1,n,3);
    B = rand(n,16);
    t = [];
    C = {};

    tic
        C{length(C)+1} = squeeze(cell2mat(cellfun(@(x) x*B,num2cell(A,[1 2]),'UniformOutput',false)));
    t(length(t)+1) = toc;

    tic
        for i = 1:size(A,3)
            C{length(C)+1}(:,i) = A(:,:,i)*B;
        end
    t(length(t)+1) = toc;

    disp(t)
end