MATLAB中的稳定acumarray
MATLAB的内置函数接受函数MATLAB中的稳定acumarray,matlab,accumarray,stable-sort,Matlab,Accumarray,Stable Sort,MATLAB的内置函数接受函数fun作为第四个参数 A = accumarray(subs,val,sz,fun); 这将fun应用于val中的每个元素子集,这些元素在subs中具有相同的下标。然而,文件指出: 如果subs中的下标未根据其线性索引进行排序,fun不应取决于其输入数据中值的顺序 我们如何实现一个稳定版本的accumarray,它没有这个限制,但可以保证子集采用与val相同的顺序 示例: subs = [1:10,1:10]; val = 1:20; accumarray(sub
fun
作为第四个参数
A = accumarray(subs,val,sz,fun);
这将fun
应用于val
中的每个元素子集,这些元素在subs
中具有相同的下标。然而,文件指出:
如果subs
中的下标未根据其线性索引进行排序,fun
不应取决于其输入数据中值的顺序
我们如何实现一个稳定版本的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
作为预处理步骤,首先对索引和相应的值进行排序,正如其文档所述:
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{:});
备选方案:
subs = [1:10,1:10];
val = 1:20;
accumarray(subs(:), val(:), [], @(x)x(end)).'
正如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)])。
转换为线性指数。我们省略了+1
和-1
,因为排序的结果仍然相同;这为我们节省了一些周期
以上哪一种解决方案更快将取决于您的机器和MATLAB版本。例如,您可以通过以下方式进行测试:
A = randi(10, 1e4, 5);
timeit(@()accumarrayStable(A(:,1:end-1), A(:,end), [], @(x)x(1))
我打算建议sortrows
关于(它也在中使用)。好的问答@路易斯门多:我想有人可能需要这个,所以为什么不改变一下回答我自己的问题呢?:-)需要在馈送到sortrows
之前反转索引,以便在线性索引意义上完成排序,对吗?也许你可以把它添加到你的answer@LuisMendo:我将添加第二个版本。这似乎与我使用fun=@(x)x(end)
进行测试有关,无论出于何种原因,这会使版本2比版本1慢很多。。。我们可能应该使用一个测试用例,其中fun~=sum
,否则稳定的东西就没有任何意义了。现在我看到了你关于不使用sum
错误:-)的评论,所以我们同意稳定性对于“非标准”函数是有意义的