元胞数组的广播求和 - MATLAB

Broadcasted summing on cell arrays - MATLAB

假设我有一个矩阵 C1,C2 如下:

C1 = nx2 cell 每个单元格是 [5x5 double].

C2 = 1x1 cell 包含一个 [5x5 double].

如何计算C3为:

C3{1,1} =  C1{1,1}+C2{1,1};
C3{1,2} =  C1{1,2}+C2{1,1};
          .
          .
C3{n,2} =  C1{n,2}+C2{1,1};

使用cellfun或任何其他不循环的方法

对于那些具有常规大小数组的情况,您应该最好使用多维数组而不是慢速元胞数组。不过,坚持使用元胞数组,这是一个矢量化解决方案,在将它们转换为多维数组后,使用 bsxfun 作为矢量化广播求和的基础 -

%// Store size of each cell and size of C1 for later reshaping purposes
[m,n] = size(C1{1,1})
[nr,nc] = size(C1);

%// Perform broadcasted summations with BSXFUN
sums = bsxfun(@plus,cat(3,C1{:}),C2{1,1})

%// Reshape and convert back to cell arrays as desired output 
out = reshape(mat2cell(sums,m,n,ones(1,nr*nc)),nr,nc)

cellfun 的直接使用完全符合您的要求:

C3 = cellfun(@(x) x+C2{1,1},C1,'uniformoutput',false);

这将实质上遍历元胞数组 C1 的每个元素,并为每个元素应用匿名函数 @(x) x+C2{1,1},即每个元素都将添加到 C2{1,1}。结果元素以与 C1.

大小相同的元胞数组形式返回

对于一些较小的测试用例,我将此解决方案与 进行了比较。结果并非微不足道,因为 cellfuncat(在 Divakar 的解决方案中使用)都非常慢。我投入了第三个版本来检查,其中我在我的匿名函数中定义了一个临时变量:

tmpvar=C2{1,1};
C3a2=cellfun(@(x) x+tmpvar,C1,'uniformoutput',false);

这背后的基本原理是访问单个单元格元素应该有一些开销,我不确定是否将整个单元格拉入匿名函数的工作区。

我定义了一个单独的函数来测试这三种情况,以便让 JIT 完成它的工作(但请注意,我使用的是 R2012b,较新的版本可能会给出完全不同的结果,the new execution engine and everything)。我 运行 所有三种情况都在同一个 运行dom 输入上(具有 C2 大小为 [1,1] 的元胞数组并包含与 C1{k,l} 大小相同的数组),从 10 次尝试中选择最短的运行时间(基于 tic/toc)。

  1. 5x5 10x10 矩阵的元胞数组:
    • cellfun: 0.000452 s
    • cellfun+tmpvar: 0.000287 s
    • bsxfun(cat)0.002970 s
  2. 5x5 100x100 矩阵的元胞数组:
    • cellfun: 0.000988 s
    • cellfun+tmpvar: 0.000963 s
    • bsxfun(cat)0.004661 s
  3. 10x10 5x5 矩阵的元胞数组:
    • cellfun: 0.001580 s
    • cellfun+tmpvar: 0.000945 s
    • bsxfun(cat)0.011358 s
  4. 100x100 5x5 矩阵的元胞数组:
    • cellfun: 0.108276 s
    • cellfun+tmpvar: 0.082675 s
    • bsxfun(cat)1.132417 s

基于这些小测试用例,我很想得出结论

  1. 在给定的匿名函数中使用临时变量 cellfun 确实可以计算较大的单元格数组是否很大,这是有道理的(从那时起函数被评估的次数更多)。
  2. 基于 bsxfun(cat) 的解决方案对于小型元胞阵列来说有点慢,而对于较大的元胞阵列来说要慢得多。我怀疑 cat 是罪魁祸首: cat 需要很多时间才能将额外的维度放在一起。我什至可以想象使用带预分配的循环可能会优于 cat.
  3. 对新的执行引擎以及更大的 (n>1000) 矩阵和元胞数组进行同样的检查会很有趣。