我有一个数组:
a = [109, 894, 566, 453, 342, 25]
和另一个a
子索引的单元格数组,表示为:
subs = { [1,3,4], [2,5,6], [1,3], [3,4], [2,3,4], [6] };
我想避免for循环通过MATLAB计算以下求和:
for i=1:6
sums_a(i) = sum(a(subs{i}));
end
有arrayfun
这样的快速方式来实现这个吗?感谢。
答案 0 :(得分:8)
使用cellfun
sums_a = cellfun( @(sx) sum( a(sx) ), subs );
答案 1 :(得分:2)
如果您正在寻找速度,arrayfun
可能会相当慢。正如Andrew Horchler所评论的那样,由于JIT acceleration,最新版本的MATLAB for循环可以非常快。如果你仍然坚持避免循环,这里是一个棘手的解决方案,没有使用accumarray
的循环:
idx = cumsum(cellfun('length', subs));
x = diff(bsxfun(@ge, [0; idx(:)], 1:max(idx)));
x = sum(bsxfun(@times, x', 1:numel(subs)), 2); %'// Produce subscripts
y = a([subs{:}]); % // Obtain values
sums_a = accumarray(x, y); % // Accumulate values
这实际上可以写成(相当长的)单行,但为了清晰起见,它被分成几行。
要累积的值如下所示:
y = a([subs{:}]);
在您的示例中,相应的索引应为:
1 1 1 2 2 2 3 3 4 4 5 5 5 6
那是:
y
的前3个值,结果存储为输出中的 first 元素。依旧......
以下几行可以产生这样的索引x
矢量:
idx = cumsum(cellfun('length', subs));
x = diff(bsxfun(@ge, [0; idx(:)], 1:max(idx)));
x = sum(bsxfun(@times, x', 1:numel(subs)), 2);
最后,x
和y
被输入accumarray
:
sums_a = accumarray(x, y);
瞧。
以下是基准测试代码:
a = [109,894,566,453,342,25];
subs = {[1,3,4], [2,5,6], [1,3], [3,4], [2,3,4], 6};
% // Solution with arrayfun
tic
for k = 1:1000
clear sums_a1
sums_a1 = cellfun( @(subs) sum( a(subs) ), subs );
end
toc
% // Solution with accumarray
tic
for k = 1:1000
clear sums_a2
idx = cumsum(cellfun('length', subs));
x = diff(bsxfun(@ge, [0; idx(:)], 1:max(idx)));
x = sum(bsxfun(@times, x', 1:numel(subs)), 2);
sums_a2 = accumarray(x, a([subs{:}]));
end
toc
%'// Solution with for loop
tic
for k = 1:1000
clear sums_a3
for n = 1:6
sums_a3(n) = sum(a(subs{n}));
end
end
toc
我机器上的结果是:
Elapsed time is 0.027746 seconds.
Elapsed time is 0.004228 seconds.
Elapsed time is 0.001959 seconds.
accumarray
与arrayfun
相比,加速几乎快了十倍,但请注意for循环仍然胜过两者。