5

MATLAB 的内置函数accumarray接受一个函数fun作为第四个参数。

A = accumarray(subs,val,sz,fun);

这适用于在中具有相同下标的fun元素的每个子集。然而,文档指出:valsubs

如果中的下标subs没有根据它们的线性索引排序,fun则不应依赖于其输入数据中值的顺序。

我们如何实现一个没有这个限制的稳定版本,但保证子集采用与给出的相同的顺序?accumarrayval

例子:

subs = [1:10,1:10];
val = 1:20;
accumarray(subs(:), val(:), [], @(x)x(end)).'

11:20如果accumarray是稳定的,则预期的输出将是。实际上输出是:

ans =
    11    12    13    14     5     6     7    18    19    20

我们的实现应该产生:

accumarrayStable(subs(:), val(:), [], @(x)x(end)).'`
ans =
    11    12    13    14    15    16    17    18    19    20
4

1 回答 1

5

正如其文档所述,我们可以将sortrows其用作预处理步骤,首先对索引和相应的值进行排序:

SORTROWS使用稳定版本的快速排序。

由于subs应该根据它们的线性索引对中的下标进行排序,因此我们需要以相反的字典顺序对它们进行排序。这可以通过在使用前后翻转列顺序来实现sortrows

这为我们提供了以下稳定版本的代码accumarray

function A = accumarrayStable(subs, val, varargin)
[subs(:,end:-1:1), I] = sortrows(subs(:,end:-1:1));
A = accumarray(subs, val(I), varargin{:});

选择:

正如Luis Mendo所建议的那样,sortrows也可以从下标生成线性索引并sort代替使用。

function A = accumarrayStable(subs, val, varargin)
if numel(varargin)>0 && ~isempty(varargin{1})
    sz = varargin{1};
else
    sz = max(subs,[],1);
end
[~, I] = sort(subs*cumprod([1,sz(1:end-1)]).');
A = accumarray(subs(I,:), val(I), sz, varargin{:});

请注意,我们应该1+(subs-1)*cumprod([1,sz(1:end-1)]).'用于转换为线性索引。我们省略了+1and-1结果sort仍然是一样的;这为我们节省了几个周期。

上述哪种解决方案更快将取决于您的机器和 MATLAB 版本。例如,您可以通过以下方式进行测试:

A = randi(10, 1e4, 5); 
timeit(@()accumarrayStable(A(:,1:end-1), A(:,end), [], @(x)x(1))
于 2015-02-11T20:03:21.000 回答