使用在每次迭代中递增的变量向量化 foror 循环

Aur*_*dal 6 optimization matlab loops for-loop octave

我试图通过摆脱一些 for 循环来优化我的代码的运行时间。但是,我有一个在每次迭代中递增的变量,其中有时会重复索引。我在这里提供一个最小的例子:

a = [1 4 2 2 1 3 4 2 3 1]
b = [0.5 0.2 0.3 0.4 0.1 0.05 0.7 0.3 0.55 0.8]
c = [3 5 7 9]

for i = 1:10
    c(a(i)) = c(a(i)) + b(i)
end
Run Code Online (Sandbox Code Playgroud)

理想情况下,我想通过写作来计算它:

c(a) = c(a) + b 
Run Code Online (Sandbox Code Playgroud)

但显然它不会给我相同的结果,因为我必须多次重新计算相同索引的值,所以这种矢量化方式不起作用。另外,如果这很重要,我正在使用 Matlab 或 Octave。

非常感谢您的帮助,我不确定是否可以进行矢量化。


编辑:非常感谢您到目前为止的回答。我发现了 accumarray,我以前不知道它,并且也理解了为什么改变 Matlab 和 Octave 之间的 for 循环给了我如此不同的时间。我也更好地理解了我的问题。我举了一个太简单的例子,我认为我可以扩展它,但是,如果 b 是一个矩阵呢?

(让我们暂时忘记 c ):

a = [1 4 2 2 1 3 4 2 3 1]
b =[0.69  -0.41  -0.13  -0.13  -0.42  -0.14  -0.23  -0.17   0.22  -0.24;
   0.34  -0.39  -0.36   0.68  -0.66  -0.19  -0.58   0.78  -0.23   0.25;
  -0.68  -0.54   0.76  -0.58   0.24  -0.23  -0.44   0.09   0.69  -0.41;
   0.11  -0.14   0.32   0.65   0.26   0.82   0.32   0.29  -0.21  -0.13;
  -0.94  -0.15  -0.41  -0.56   0.15   0.09   0.38   0.58   0.72   0.45;
   0.22  -0.59  -0.11  -0.17   0.52   0.13  -0.51   0.28   0.15   0.19;
   0.18  -0.15   0.38  -0.29  -0.87   0.14  -0.13   0.23  -0.92  -0.21;
   0.79  -0.35   0.45  -0.28  -0.13   0.95  -0.45   0.35  -0.25  -0.61;
  -0.42   0.76   0.15   0.99  -0.84  -0.03   0.27   0.09   0.57   0.64;
   0.59   0.82  -0.39   0.13  -0.15  -0.71  -0.84  -0.43   0.93  -0.74]
Run Code Online (Sandbox Code Playgroud)

我现在明白我要做的是每组的 rowSum,鉴于我使用的是 Octave,我不能使用“splitapply”。我试图概括您的答案,但 accumarray 不适用于矩阵,而且我也无法概括 @rahnema1 解决方案。所需的输出是:

[0.34   0.26  -0.93  -0.56  -0.42  -0.76  -0.69  -0.02   1.87  -0.53; 
 0.22  -1.03   1.53  -0.21   0.37   1.54  -0.57   0.73   0.23  -1.15;
-0.20   0.17   0.04   0.82  -0.32   0.10  -0.24   0.37   0.72   0.83;
 0.52  -0.54   0.02   0.39  -1.53  -0.05  -0.71   1.01  -1.15   0.04]
Run Code Online (Sandbox Code Playgroud)

那是“等价于”

[sum(b([1 5 10],:))
 sum(b([3 4 8],:))
 sum(b([6 9],:))
 sum(b([2 7],:))]
Run Code Online (Sandbox Code Playgroud)

非常感谢,如果您认为我应该将其包含在另一个问题中而不是添加编辑,我会这样做。

Lui*_*ndo 6

原始问题

可以通过以下方式完成accumarray

a = [1 4 2 2 1 3 4 2 3 1];
b = [0.5 0.2 0.3 0.4 0.1 0.05 0.7 0.3 0.55 0.8];
c = [3 5 7 9];
c(:) = c(:) + accumarray(a(:), b(:));
Run Code Online (Sandbox Code Playgroud)

这会将b定义的组中的值相加a,并将其添加到原始c.

编辑的问题

如果b是矩阵,则可以使用

full(sparse(repmat(a, 1, size(b,1)), repelem(1:size(b,2), size(b,1)), b))
Run Code Online (Sandbox Code Playgroud)

或者

accumarray([repmat(a, 1, size(b,1)).' repelem(1:size(b,2), size(b,1)).'], b(:))
Run Code Online (Sandbox Code Playgroud)

  • 强制性基准:OP 的代码:6.5e-7,Luis 的代码:1.9e-6,rahnema1 的代码(稀疏):2.7e-6。对于较大的测试集(“c”为 100 个元素,“a”和“b”为 1e6 个元素),这些时间分别为 0.0052、0.0077、0.1141。因此,对于大型数组和小型数组来说,普通的旧循环都是最快的 (MATLAB R2017a)。到目前为止,它更容易阅读。 (4认同)
  • 八度计时(使用 Octave-online.net),对于较大的集合:12.05、0.0085、0.1843。没有对较小的集合进行计时,因为 Octave 不执行“timeit”,并且“tic”/“toc”对于微小时间没有意义。使用 Octave 时,矢量化绝对是值得的。有了 MATLAB,感觉就像回到了 90 年代!另外,Octave 的“accumarray”绝对是矢量化的(@Tasos)。 (3认同)