我正在寻找一种有效的方法来确定一个集合是否是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代码在执行此操作时效率不高 - 它主要由嵌套循环组成。
建议非常欢迎!
补充说明:问题是,对于大量的集合,将会有大量的成对比较。
答案 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}}