【问题标题】:MATLAB: Multiplying elements of a cell array with another matrix to create a new cell array consisting of the productsMATLAB:将元胞数组的元素与另一个矩阵相乘以创建一个由乘积组成的新元胞数组
【发布时间】:2013-04-04 11:01:54
【问题描述】:

我有一个由许多 (2x1) 矩阵组成的元胞数组 A。 我有另一个矩阵 B (2x2),我打算将它与单元格 A 的每个元素相乘。换句话说,数组 A 中的每个矩阵都必须与 B 相乘以生成另一个单元格数组 C(与 A 大小相同),包括2x1 矩阵。

视觉上,

B*|A{1,1} A{1,2} ... A{1,n}| = |C{1,1} C{1,2} ... C{1,n}|
  |::::::::::::::::::::::::|   |::::::::::::::::::::::::|
  |A{m,1} A{m,2} ... A{m,n}|   |C{m,1} C{m,2} ... C{m,n}|

这里,C{i,j}=B*A{i,j}

使用 cellfun 执行此操作非常慢。 有没有一种方法可以在不使用显式循环的情况下对其进行矢量化,并且无论 A 和 B 的大小如何(只要它们是可乘的)都可以加快运算速度?

【问题讨论】:

  • 为什么要使用元胞数组?如果你不这样做,你可以使用 bsxfun

标签: matlab vectorization matrix-multiplication cells


【解决方案1】:

我能想到几种方法。首先,我通过

生成了一些虚假数据
A = cellfun(@(~)rand(2,1), cell(1000), 'uni', false);
B = rand(2);

Cellfun

正如你所说,这是你想要避免的。在任何情况下,我都会将其包含在此处进行比较:

C = cellfun(@(x)B*x, A, 'UniformOutput',false);

由于涉及匿名函数,它很慢。带有内置字符串函数(参见 help cellfun)的 Cellfun 正在迅速发展,但是,匿名函数会强制执行在每次迭代时通过 Matlab 解释器。尽管 JIT 可以在一定程度上提高效率,但远非最佳。

单元格扩展、num2cell、重塑

想法:将单元格扩展为 2x1000 矩阵,进行乘法运算,并将结果转换回正确大小的单元格数组。虽然原则上很优雅,但在实践中会变得有点混乱:

C = reshape( num2cell(B*[A{:}],1), size(A) );

请注意,从内存占用的角度来看,中间的 2x1000 临时文件有点浪费......此外,通过 num2cell(非内置)和“无用”reshape 会减慢速度执行相当多。

for循环

循环最容易阅读,最容易实现。内存占用小,有利于 JIT 等加速。

C = cell(size(A));
for ii = 1:numel(A)        
   C{ii} = B*A{ii};        
end

唯一的缺点就是它的名声不好 :)

比较

现在是踢球者:哪个最快?

tic
C = cellfun(@(x)B*x, A, 'uni',false);
toc

tic 
C = reshape( num2cell(B*[A{:}],1), size(A) );
toc

tic
C = cell(size(A));
for ii = 1:numel(A)    
    C{ii} = B*A{ii};    
end
toc

结果:

Elapsed time is 4.738791 seconds.  % cellfun
Elapsed time is 4.161515 seconds.  % cell expansion, num2cell, reshape
Elapsed time is 3.808822 seconds.  % loop

结论:自 R2008 引入 JIT 编译以来,循环不再是邪恶的;默认情况下不要试图避免它们。

【讨论】:

    【解决方案2】:

    你可以使用mat2cell/cell2mat来做到这一点

    A = rand(2, 10);
    B = rand(2, 2);
    Ac = mat2cell(A, 2, repmat(1, size(A, 2), 1)); % Convert to cell to generate your initial data
    
    Amat = cell2mat(Ac); % Now it's a matrix
    Cmat = B * Amat;
    C = mat2cell(Cmat, 2, repmat(1, size(A, 2), 1)); % Now it's a cell array again
    

    【讨论】:

    • +1:但有一件事:为了保持尺寸正确(以及规避潜在错误),您应该在 cell2mat 之前将 A 转换为行单元格数组,然后将其重新整形为Amat2cell 之后的原始大小。
    • 对于任意大小 (mxn) 的元胞数组 (A) 是否可以在不使用函数的情况下实现:在过程中的任何位置重塑?您基本上是建议将单元阵列设为 1D 并执行乘法并将其转换回原始形状...
    【解决方案3】:

    我刚刚尝试过,希望它是您想要的:

    clc,clear all,close all
    clc,clear all,close all
    A = cell(10,2);
    for jj = 1:numel(A)
     A{jj} = rand(2,1);
    end
    
    B = rand(2,2);
    C = cat(3,A{:});
    D = cell(size(A));
    
    for ii = 1:size(C,3)
     D{ii} = B*C(:,:,ii);
    end
    

    它包含一个显式的 for 循环(我知道),但在最新的 MATLAB 版本中,调用 for 循环不会对性能造成那么严重的影响。

    或者您可以使用此答案作为提示。 我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-17
      • 2018-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多