用于确定集合是否是另一集合的子集的有效代码

时间:2010-10-12 21:00:27

标签: matlab set wolfram-mathematica

我正在寻找一种有效的方法来确定一个集合是否是Matlab或Mathematica中另一个集合的子集。

实施例: 设A = [1 2 3 4] 设B = [4 3] 设定C = [3 4 1] 设置D = [4 3 2 1]

输出应为:设置A

集合B和C属于集合A,因为A包含它们的所有元素,因此,它们可以被删除(集合中元素的顺序无关紧要)。集合D与集合A具有相同的元素,并且由于集合A在集合D之前,我想简单地保持集合A并删除集合D.

因此有两个基本规则: 1.如果它是另一个集的子集,则删除它 2.如果集合的元素与前面的集合

相同,则删除集合

我的Matlab代码在执行此操作时效率不高 - 它主要由嵌套循环组成。

建议非常欢迎!

补充说明:问题是,对于大量的集合,将会有大量的成对比较。

4 个答案:

答案 0 :(得分:7)

您可能希望在MATLAB中查看内置的set operation functions。如果你不需要,为什么要重新发明轮子? ;)

提示:您可能对ISMEMBER函数特别感兴趣。

修改

这是使用嵌套循环解决此问题的一种方法,但要设置它们以尝试减少潜在迭代次数。首先,我们可以使用Marc注释中的建议来按照元素数量对集合列表进行排序,以便它们从最大到最小排列:

setList = {[1 2 3 4],...  %# All your sets, stored in one cell array
           [4 3],...
           [3 4 1],...
           [4 3 2 1]};
nSets = numel(setList);                       %# Get the number of sets
setSizes = cellfun(@numel,setList);           %# Get the size of each set
[temp,sortIndex] = sort(setSizes,'descend');  %# Get the sort index
setList = setList(sortIndex);                 %# Sort the sets

现在我们可以设置我们的循环,从列表末尾的最小集合开始,首先将它们与列表开头的最大集合进行比较,以增加我们快速找到超集的几率(即我们'重新依赖较大的集合更可能包含更小的集合。当找到超集时,我们从列表中删除子集并打破内部循环:

for outerLoop = nSets:-1:2
  for innerLoop = 1:(outerLoop-1)
    if all(ismember(setList{outerLoop},setList{innerLoop}))
      setList(outerLoop) = [];
      break;
    end
  end
end

运行上面的代码后,setList将从中删除所有集合,这些集合可以是列表中的其他集合的子集或副本。

在最佳情况下(例如问题中的示例数据),内部循环每次第一次迭代后都会中断,仅使用ISMEMBER执行nSets-1集合比较。在最坏的情况下,内部循环永远不会中断,它将执行(nSets-1)*nSets/2集合比较。

答案 1 :(得分:1)

我认为你要问的问题是“给出一组集合,挑选出包含所有其他集合的集合”。有一堆边缘情况,我不知道你想要什么输出(例如A = {1,2}和B = {3,4}),所以你需要澄清很多。

但是,要回答你所做的问题询问,关于集合包含,你可以使用set difference(等效补充wrt另一个集合)。在Mathematica中,有这样的事情:

setA = {1, 2, 3, 4};
setB = {4, 3};
setC = {3, 4, 1};
setD = {4, 3, 2, 1};
Complement[setD, setA] == {}
 True

表示setD是setA的子集。

答案 2 :(得分:1)

假设如果没有set是所有提供的集的超集,则希望返回空集。 (即如果没有集合是所有集合的超集,则返回“没有东西”。)

所以,...,你想要获取所有集合的联合,然后在列表中找到包含那么多元素的第一个集合。这不是太难,跳过将输入重新格式化为内部列表形式... Mathematica:

    topSet[a_List] := Module[{pool, biggest, lenBig, i, lenI},  
        pool = DeleteDuplicates[Flatten[a]];  
        biggest = {}; lenBig = 0;  
        For[i = 1, i <= Length[a], i++,  
            lenI = Length[a[[i]]];  
            If[lenI > lenBig, lenBig = lenI; biggest = a[[i]]];  
            ];  
        If[lenBig == Length[pool], biggest, {}]  
    ]  

例如:

    topSet[{{1,2,3,4},{4,3},{3,4,1},{4,3,2,1}}]  
      {1,2,3,4}  
    topSet[{{4, 3, 2, 1}, {1, 2, 3, 4}, {4, 3}, {3, 4, 1}}]  
      {4,3,2,1}  
    topSet[{{1, 2}, {3, 4}}]  
      {}  

作为一项大型测试:

    <<Combinatorica`  
    Timing[Short[topSet[Table[RandomSubset[Range[10^3]], {10^3}]]]]  
      {14.64, {}}  

即,在14.64秒内分析了一组1000个随机选择的范围[1,1000]的子集(并且不出所料,它们都不是所有这些子集的超集)。

- 编辑 - 逃避了少于隐藏几行实现的内容。还......

运行时分析:设L为列表数,N为所有列表中的元素总数(包括重复项)。池分配采用O(L)进行展平,O(N)进行重复删除。在for循环中,lenI的所有L赋值累积地需要O(N)时间,并且所有L条件最多需要O(L)时间。其余的是O(1)。由于L

正确性证明:超集(如果存在),(1)包含其自身,(2)包含其自身的任何排列,(3)包含任何(其他)集合中存在的每个元素,(4)是长或比集合中的任何其他集合更长。后果:超集(如果存在)是集合中最长的集合,任何其他相等长度的集合都是它的排列,它包含任何集合中包含的每个元素的副本。因此,如果存在与集合集合一样大的集合,则存在超集。

答案 3 :(得分:0)

Mathematica 中,我建议使用Alternatives

例如,如果我们有一组{1, 2, 3, 4},我们希望测试set x是否是我们可以使用的子集MatchQ[x, {(1|2|3|4) ..}]。这种结构的优点是,只要找到不属于的元素,测试就会停止并返回False。

我们可以按如下方式打包此方法:

maximal[sets_] :=
  Module[{f},
    f[x__] := (f[Union @ Alternatives @ x ..] = Sequence[]; {x});
    f @@@ sets
  ]

maximal @ {{1, 2, 3, 4}, {4, 3}, {5, 1}, {3, 4, 1}, {4, 3, 2, 1}}
{{1, 2, 3, 4}, {5, 1}}