【问题标题】:An efficient code to determine if a set is a subset of another set确定一个集合是否是另一个集合的子集的有效代码
【发布时间】:2010-10-12 21:00:27
【问题描述】:

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

示例: 设置 A = [1 2 3 4] 设置 B = [4 3] 设置 C = [3 4 1] 设置 D = [4 3 2 1]

输出应该是:Set A

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

所以有两个基本规则: 1.删​​除一个集合,如果它是另一个集合的子集 2. 如果一个集合的元素与前一个集合的元素相同,则删除一个集合

我的 Matlab 代码在这方面效率不高 - 它主要由嵌套循环组成。

非常欢迎提出建议!

补充说明:问题在于,如果有大量集合,就会有非常大量的成对比较。

【问题讨论】:

  • 你知道集合的元素吗?它们只是整数吗?它们仅在一定范围内吗?如果是这样,请考虑设置一个一维布尔(位)数组,每个元素对应于 A 中的一个整数。因此,一次通过 A 以形成该数组,然后通过 B 以检查 B 的每个元素是否为在 A 中(只需查看该标志在数组中是否为真)。如果这些是已知范围内的整数,那么为了提高效率,你应该得到 O(n) 范围内的东西。如果它们不是整数或未知范围 - 散列方案可以以类似的方式工作。
  • 是的,它们只能是整数。集合的最大大小也是已知的。
  • 您是否确实验证了您在成对比较花费太多时间时遇到问题?您可以通过根据大小对集合进行排序来减少比较次数,因为如果 B 小于或与 A 大小相同,则 B 只能是 A 的子集。
  • 你打算用集合 { [1,2], [3,4] } 做什么?即,您打算返回空集(“没有集是所有集的超集”)还是打算返回 {[1,2],[3,4]}(“所有集都是这些集的子集”套”)?

标签: matlab set wolfram-mathematica


【解决方案1】:

您可能想看看 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 集合比较。

【讨论】:

  • 编写一个逻辑测试来检查一个集合是否是一个子集并不是真正的问题。我要避免的是在集合之间进行大量的成对比较。例如,如果我将我的集合保存在一个矩阵中,我想对整个矩阵应用一个操作以提取具有正确集合的行。
  • @Edward:我已经更新了我的答案,提供了一个完整的解决方案供您尝试。希望它比您以前尝试的更有效。 ;)
【解决方案2】:

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

但是,要回答您确实提出的关于集合包含的问题,您可以使用集合差异(等效地补充另一个集合)。在 Mathematica 中,这样的事情:

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

表示 setD 是 setA 的子集。

【讨论】:

  • 我在上面添加了一些说明。输出应该是集合 A,因为它包含集合 B 和 C,而集合 D 是简单的重新排列的集合 A。此外,问题不在于设计逻辑测试来检查集合是否是子集,而是避免有大量成对比较。因此,我的问题更多是关于解决问题的有效方法。
【解决方案3】:

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

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

topSet[a_List] := Module[{pool, maximum, lenBig, i, lenI}, 池 = DeleteDuplicates[Flatten[a]]; 最大 = {};长度大 = 0; For[i = 1, i lenBig, lenBig = lenI;最大 = a[[i]]]; ]; 如果[lenBig == 长度[池],最大,{}] ]

例如:

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} 顶集[{{1, 2}, {3, 4}}] {}

作为一个大测试:

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

-- 编辑 - 转义一个小于隐藏几行实现的内容。还有……

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

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

【讨论】:

    【解决方案4】:

    Mathematica中,我建议为此使用Alternatives

    例如,如果我们有一个集合 {1, 2, 3, 4},并且我们希望测试集合 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}}
    

    【讨论】:

      猜你喜欢
      • 2021-04-05
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多