n个集合之间的最大交集

13

我有x个集合,每个集合中有y个元素(未排序的整数)。我想找到这些集合中任意一对交集的最大大小。

例如:

*5个集合,大小为3

集合1:12 3

集合2:42 3

集合3:5 6 7

集合4:5 8 9

集合5:5 10 11

最大交集是集合1和集合2,大小为2;答案是2。

因此,可以使用 HashSets 在 O(x^2*y) 的时间复杂度内完成,只需查找所有组合并计算它们的交集大小即可。但我想更快地解决问题。我认为有特定的算法或数据结构可以帮助。你能给我一些想法吗?

更新:x和y约为10^3,元素为整数。而且没有相同的集合。


如果set 1: 1 3 2set 2: 4 2 3,即集合内元素的顺序不重要,那么1和2会相交吗? - igon
是的,顺序无关紧要。 - rusted
元素的值是否有限制?集合的数量呢?这方面有限制吗? - Ivaylo Strandjev
元素是整数,x和y大约为1000。如果您有使用小元素的想法,那也很有用。 - rusted
只是澄清一下,如果你有3个集合,其中集合1和集合2之间的交集大小为2,集合2和集合3之间的交集大小为5,你要寻找的答案是5,而不是7,正确吗? - Joel
是的,我正在寻找最大交集,而不是所有交集的总和。 - rusted
3个回答

4

我能想到的一种优化方法是记住第一个集合与其余集合之间的交集大小,并使用这些数据来减少一些情况。

如何使用它:

如果您有长度为 n 的集合 ABC,则可以利用上述方法。

intersection(A,B) = p
intersection(A,C) = q

那么

intersection(B,C) <= n - abs(p - q)

针对您的情况中的集合:

S0 = { 1 2 3 }
S1 = { 4 2 3 }
S2 = { 5 6 7 }

您需要计算 intersection(S0,S1) = 2 并记住结果:

[ i(0,1)=2 ]

接着,intersection(S0,S2) = 0,所以

[ i(0,1)=2; i(0,2)=0 ]

当你比较第一个元素后,计算intersection(S1,S2)

(S1[0]=4 != S2[0]=5)

你可以说intersection(S1,S2) <= 2,这是目前为止最好的结果。

进一步的改进是记住更精确的交集结果,但仍然不计算所有结果。

我不确定这是否是最佳选择。也许存在完全不同的方法来解决这个问题。


4

以下是一些伪代码:

function max_intersection(vector<vector<int>> sets):
    hashmap<int, vector<set_id>> val_map;
    foreach set_id:set in sets:
        foreach val in set:
            val_map[val].push_back(set_id);
    max_count = 0
    vector<int> counts = vector<int>(size = sets.size() * sets.size(), init_value = 0);
    foreach val:set_ids in val_map:
        foreach id_1:set_id_1 in set_ids:
            foreach id_2:set_id_2 in set_ids where id_2 > id_1:
                count = ++counts[set_id_1 * sets.size() + set_id_2];
                if (count > max_count):
                    max_count = count;
    return max_count;

因此,如果X是集合的数量,Y是每个集合中元素的数量:

  1. 将值插入val_map的时间复杂度为O(X*Y)
  2. 创建counts并将每个元素初始化为零的时间复杂度为O(X^2)
  3. 如果没有交集(每个值恰好出现一次),则最后一个循环的运行时间为O(X*Y)。然而,在另一个极端情况下,如果存在大量的交集(所有集合都相等),那么最后一个循环的运行时间为O(X^2*Y)

因此,取决于交集的数量,时间复杂度在O(X*Y + X^2)O(X^2*Y)之间。


1
算法的复杂度为O(k^2 *y)。其中,k是包含具体数字的集合的平均数量。 - Alexander Kuznetsov

2

我想不出一种能够改进 O(x*x*y) 的解决方案,但我可以建议一种避免散列的方法。代替期望复杂度为O(x*x*y),我们可以以10^6的额外内存代价换取复杂度O(x*x*y)。根据您所提供的限制条件,您不会有超过10^6个不同的数字。因此,我的想法是这样的-对所有数字进行排序,然后去重。将从1到10^6(或唯一数字数目)的唯一编号分配给每个数字(使用它们在排序和去重数组中的顺序)。之后,代替每对键值对的哈希映射,使用大小为10^6的位集合。这样,您将具有一定的复杂度O(x*x*y)(因为我提出的预计算的复杂度为O(x * y *(log(x) + log (y)))。


1
既然您已经对所有数字进行了排序和去重,那么您也可以丢弃所有仅出现一次的数字——因为它们不能在两个不同的集合中!这不会改变复杂度,但非常便宜,而且可能会大大减少常数因子(取决于输入分布)。 - j_random_hacker
1
是的,我考虑过这一点,但我的建议着重于最坏情况,而不是平均情况。 - Ivaylo Strandjev
你的解决方案的复杂度是O(x^2),但实际上它是O(x^2*10^6),对吗? - rusted
1
@rusted 不是的。如果正确实现,它是O(x^2*y + 10^6)。 - Ivaylo Strandjev
@IvayloStrandjev 但是为什么?你有大约x^2对,每一对都使用大小为10^6的位集进行“比较”。所以x^2对,每个对之间有10^6次比较=>总共是10^6 * x^2。也许我没有理解你的想法? - rusted
1
你需要迭代第一个集合中的所有数字,并将位设置为1 - 复杂度为O(y),然后你需要迭代另一个集合中的所有数字,检查相应的元素是否设置为1。又是O(y)。最后再次迭代第一个集合,将元素设置回0。总体复杂度为O(y)。 - Ivaylo Strandjev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接