我不知道如何在完全没有 for 循环的情况下做到这一点,但您可以使用 、 和 的组合sort
来diff
组织find
和划分等价类标识符。O(n)
这将为您提供一个主要矢量化的解决方案,其中M 代码级 for 循环是n
类的数量,而不是整个输入数组的长度。这在实践中应该很快。
这是一个使用一些索引修改的粗略示例。当心; 由于我刚刚解决了这个问题,因此某处可能存在一个错误的边缘案例错误。
function [eqVals,eqIx] = equivsets(a,x)
%EQUIVSETS Find indexes of equivalent values
[b,ix] = sort(x);
ixEdges = find(diff(b)); % identifies partitions between equiv classes
ix2 = [0 ixEdges numel(ix)];
eqVals = cell([1 numel(ix2)-1]);
eqIx = cell([1 numel(ix2)-1]);
% Map back to original input indexes and values
for i = 1:numel(ix2)-1
eqIx{i} = ix((ix2(i)+1):ix2(i+1));
eqVals{i} = a(eqIx{i});
end
我在输出中包含了索引,因为它们通常比值本身更有用。你会这样称呼它。
% Get indexes of occurrences of each class
equivs = equivsets(array, classes)
% You can expand that to get equivalences for each input element
equivsByValue = equivs(classes)
首先为每个类构建列表然后扩展它们以匹配输入索引会更有效。您不仅只需执行一次工作,而且当您使用 将b = a(ix)
小单元阵列扩展到更大的单元阵列时,Matlab 的写时复制优化最终将重用底层数字 mxArrays 的内存,因此您得到一个内存中更紧凑的表示。
unique()
在使用或数据库时,这种转换会经常出现。对于我使用过的决策支持系统和数据仓库风格的东西,它无处不在。我希望它内置在 Matlab 中。(最近几年它可能已被添加到数据库或时间序列工具箱之一中;我落后了几个版本。)
实际上,如果此性能对您的代码至关重要,您还可以考虑使用 Java 或 C MEX 函数并在那里实现它。但是,如果您的数据集是低基数——也就是说,有少量的类/不同的值,比如numel(unique(classes)) / numel(array)
往往小于 0.1 左右——M 代码实现可能会很好。