在MATLAB中稳定准确的
MATLAB的内置函数accumarray
接受函数fun
作为第四个参数。
A = accumarray(subs,val,sz,fun);
这对val
中每个具有相同下标的元素的子集适用fun
。 但是文件指出:
如果
subs
中的下标不是按照它们的线性索引进行sorting的,fun
不应该依赖于其input数据中值的顺序。
我们怎样才能实现稳定的accumarray
版本,它没有这个限制,但是会保证子集采用与val
给出的相同的顺序?
例:
subs = [1:10,1:10]; val = 1:20; accumarray(subs(:), val(:), [], @(x)x(end)).'
如果accumarray
是稳定的话,预期的输出是11:20
。 其实输出是:
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
我们可以使用sortrows
作为预处理步骤,首先对索引和相应的值进行sorting,如其文档所述:
SORTROWS
使用一个稳定版本的quicksort。
由于子索引中的下标应该按照它们的线性索引进行sorting,所以我们需要按照逆向词典顺序对它们进行sorting。 这可以通过在使用sortrows
之前和之后翻转列顺序来sortrows
。
这给了我们一个稳定版本的accumarray
的以下代码:
function A = accumarrayStable(subs, val, varargin) [subs(:,end:-1:1), I] = sortrows(subs(:,end:-1:1)); A = accumarray(subs, val(I), varargin{:});
替代scheme:
正如路易斯·门多 ( Luis sortrows
所build议的那样,也可以用下标来生成线性索引,而不是使用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)]).'
用于转换为线性索引。 我们省略了+1
和-1
, sort
的结果仍然是一样的; 这节省了我们几个周期。
上述哪一种解决scheme更快取决于你的机器和MATLAB版本。 你可以testing例如通过:
A = randi(10, 1e4, 5); timeit(@()accumarrayStable(A(:,1:end-1), A(:,end), [], @(x)x(1))