表示与等价相关的类的数组

时间:2014-03-15 19:50:44

标签: arrays matlab indexing cell-array equivalence-classes

我在Matlab中有一个数组。我用自然数编号数组中的每个条目。所以我在数组中形成了等价关系。

例如,

array   = [1 2 3 5 6 7]
classes = [1 2 1 1 3 3].

我想得到单元格数组:i - 单元格数组的位置与i - 初始数组的条目相关联并显示,哪个元素在这个条目的一个类中。对于上面的例子,我会得到:

{[1 3 5], [2], [1 3 5], [1 3 5], [6 7], [6 7]}

可以使用for循环轻松完成,但还有其他解决方案吗?如果它的工作速度比O(n^2)快,那么n就是初始数组的大小。

Edit. 如果我知道将排序数组拆分为O(n)的具有相等元素的单元格的方法,问题就会得到解决。

array  = [1 1 1 2 3 3]
groups = {[1 2 3], [4], [5 6]}

3 个答案:

答案 0 :(得分:3)

不确定复杂性,但带有单元格输出的accumarray对于根据类的唯一值拆分数组非常有用:

data = sortrows([classes; array].',1) %' stable w.r.t. array
arrayPieces = accumarray(data(:,1),data(:,2)',[],@(x){x.'})
classElements = arrayPieces(classes).'

关于将分组数组拆分为indeces的单元格:

>> array  = [1 1 1 2 3 3]
>> arrayinds = accumarray(array',1:numel(array),[],@(x){x'})' %' transpose for rows
arrayinds = 
    [1x3 double]    [4]    [1x2 double]
>> arrayinds{:}
ans =
     1     2     3
ans =
     4
ans =
     5     6

答案 1 :(得分:1)

我不知道如何在没有for循环的情况下完成此操作,但您可以使用sortdifffind的组合来组织和分区等价类标识符。这将为您提供一个主要为矢量化的解决方案,其中M代码级for循环为O(n),其中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代码的实现可能会很好。

答案 2 :(得分:1)

关于第二个问题:

array  = [1 1 1 2 3 3]; %// example data
  1. 使用diff查找每次运行相等值的结束,并从中构建组:

    ind = [0 find(diff([array NaN])~=0)];
    groups = arrayfun(@(n) ind(n)+1:ind(n+1), 1:numel(ind)-1, 'uni', 0);
    
  2. 使用unique的相同方法:

    [~, ind] = unique(array);
    ind = [0 ind];
    groups = arrayfun(@(n) ind(n)+1:ind(n+1), 1:numel(ind)-1, 'uni', 0);
    
  3. 但是,我还没有测试复杂性是否为O(n)。