如何在MATLAB中不使用循环乘以张量?

8
假设我有以下内容:
A = rand(1,10,3);
B = rand(10,16);

And I want to get:

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吗?由于我需要在许多不同的A情况下使用相同的B,因此上述循环创建b所需的时间并不重要。


如果 A = rand(4,10,3),那么它就是一个没有单例维度的 3D 数组。那么输出结果应该是什么? - Divakar
@Divakar 在我的问题中它总是单例模式。 - Alex Azazel
4
那么,只需要压缩矩阵A并使用矩阵乘法 - C = B.'*squeeze(A) - Divakar
@Divakar 这就是LuisMendo给出的答案,它确实加快了程序的速度,不管怎样谢谢你! - Alex Azazel
4个回答

7

交换 AB 的维度,然后进行矩阵乘法:

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

3
你说得完全正确。然而,楼主需要注意的是,这个解决方案之所以有效,是因为A的第一维是单例维。 - GJStein
1
执行时间减少了10%!非常感谢! - Alex Azazel
2
非常好,但我想知道为什么OP使用具有单例维度的三维数组。 - JustinBlaber

6
如果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合并第一和第三维度变成一个二维数组然后执行矩阵乘法。最后,为了使输出大小与之前列出的C相同,我们需要进行最后一步的reshape操作。
实现应该类似于以下内容 -
%// 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

根据评论,如果A是一个三维数组且始终在开头具有单例维度,则可以使用squeeze然后像下面这样进行矩阵乘法-
C = B.'*squeeze(A)

2
编辑:@LuisMendo指出,对于这个特定的用例,这是可能的。但是,如果A的第一个维度不是1,则通常情况下不可能。

我已经苦苦思索了一段时间,但始终无法找到解决方法。使用bsxfun可以很好地进行逐元素计算,但张量乘法却是极不支持的。抱歉,祝你好运!

您可以查看这个mathworks文件交换文件,它将使您更容易并支持您要查找的行为,但我相信它也依赖于循环。 编辑:它依赖于MEX/C++,因此如果您正在寻找纯MATLAB解决方案,则不是。


你说得对,我已经更新了我的答案。我会把它留在这里,以指出,一般来说,你的解决方案是不可行的。然而,它确实解决了这里提出的问题。 - GJStein
2
在你的编辑之后,我已经取消了我的踩。然而,我并不确定张量乘法是否是 OP 所需要的。如果 A 的第一个维度不是 1,那么问题中的赋值语句 C(:,1) = A(:,:,1)*B 就没有意义,因为 A(:,:,1)*B 将会是一个矩阵,而不是一个向量。 - Luis Mendo

0

我必须同意@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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接