【问题标题】:Broadcasted summing on cell arrays - MATLAB元胞数组上的广播求和 - MATLAB
【发布时间】:2015-12-21 21:58:18
【问题描述】:

假设我有一个矩阵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 或任何其他不循环的方法

【问题讨论】:

  • 如果你想保持你的问题不公开:问一个很好的、主题性的问题:P 无论如何:cellfun(@(x) x+C2{1,1},C1,'uniformoutput',false) 可能。但是,如果 C1 确实是 nx2 cell,那么在您的示例中使用 C1{n,n} 会遇到问题。

标签: matlab vectorization cell-array


【解决方案1】:

对于具有常规大小数组的情况,您最好使用多维数组而不是慢元胞数组。不过,坚持使用元胞数组,这里有一个矢量化解决方案,使用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)

【讨论】:

  • n 在最后一行分别是 cell_nrowscell_ncols。对吧?:) 并且cat+bsxfun 是否仍然比cellfun 快?
  • @AndrasDeak 谢谢! OP 中的 C1{n,n} 让我感到困惑,编辑了解决方案。在执行实际求和之前和之后,这里涉及到一些设置,所以我想这在很大程度上取决于实际的测试用例。带来巨大的n,它可能会很有趣:)
  • (古代)R2012b:(m,n,nr,nc)==(5,5,10,10):0.003482 秒 cellfun 与 0.016824 秒 bsxfun(cat)。 (100,100,10,10): 0.006484 s vs 0.024914 s. (10,10,100,100)`:0.109620 秒与 1.158193 秒。 (我的内存使用......次优,所以我没有冒险检查更大的尺寸)
  • @AndrasDeak 嗯,感谢您的基准测试!似乎cellfun 是要走的路!或者只是将数据重组为多维数组,因为矢量化设置似乎并没有真正帮助它。
  • @AndrasDeak BTW 考虑发布 cellfun 方法作为答案。正如 OP 所说,这可能是他/她正在寻找的。​​span>
【解决方案2】:

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

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

这实际上将遍历单元格数组C1 的每个元素,并为每个元素应用匿名函数@(x) x+C2{1,1},即每个元素都将添加到C2{1,1}。结果元素以与C1 大小相同的元胞数组返回。


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

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

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

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

  1. 5x510x10 矩阵的单元格数组:
    • cellfun:             0.000452 s
    • cellfun+tmpvar:0.000287 s
    • bsxfun(cat):       0.002970 s
  2. 5x5100x100 矩阵的单元阵列:
    • cellfun:             0.000988 s
    • cellfun+tmpvar:0.000963 s
    • bsxfun(cat):       0.004661 s
  3. 10x105x5 矩阵的单元格数组:
    • 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) 矩阵和元胞数组检查相同的内容会很有趣。

【讨论】:

  • 访问元胞数组和索引到同一位置通常会重复有其自身的惩罚,因此tempVar 是一个聪明的补充。我很想亲自动手弄脏这个新的 JIT 东西,似乎人们在谈论它!
  • 谢谢,这正是我正在寻找的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-09
  • 1970-01-01
  • 2017-12-03
  • 2014-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多