【问题标题】:Largest intersection of N sets with ability to ignore certain sets (Set compression)N 个集合的最大交集,可以忽略某些集合(集合压缩)
【发布时间】:2015-02-11 02:34:54
【问题描述】:

假设你有 N 组未排序的字符,这些组之间有共同的字符。我想从这些集合中提取尽可能多的字符以使它们更小。但是将字符分解出来有一个限制:字符必须在您从 N 中选择的 M 个集合的交集中。这在某种程度上是一种无损集合压缩算法。下面的示例是有序集合,但这是为了便于阅读。不要假设集合会被订购。

一个简单的例子:

S1 = a b c d
S2 = a b c e f
S3 = a f g

答案是仅将 S1 和 S2 相交并分解出:a b c。这会减少 6 个字符,而其他任何集合的交集组合都会减少。

一个棘手的例子:

S1 = a b c d e f g h i
S2 = j k l m n
S3 = j k l o p q
S4 = j k l
S5 = a b c d

答案是忽略集合 S1 和 S5,取其余集合 S2、S3 和 S4 的交集得到:j k l。

a b c d 不正确的原因是,当你把这些字符从集合中分解出来时,剩下 19 个字符,而当你把 j k 和 l 分解出来时,只剩下 18 个字符。

有没有比指数时间更快地解决这类问题的算法?似乎您必须测试集合的幂集中每个集合的交集({},{S1},{S2},{S3},{S1,S2},{S1,S3},{S2 , S3}, {S1, S2, S3}) - 如果只有 3 个集合,则计算 8 个交点。

附:这不是一个紧迫的问题,但我认为这是我遇到的一个有趣的问题。

【问题讨论】:

  • 嗯,我不太清楚什么条件取交叉口,对于你的第二个例子,如果我们只做两个交叉口会更好吗?
  • 您不需要将已分解的部分至少保留一次吗?如果这是某种压缩,我想您将{abcd, abcef} 压缩为(abc + {d, ef}) 之类的东西,对吧?这很重要,因为在你的计数中,从 2 个集合中提取 5 个字符比从 3 个中提取 3 个要好,但以我的方式更糟。
  • @VincentvanderWeele 我猜压缩器会跟踪删除的内容,从哪些集合中删除。这将允许通过使用 m+n 额外内存来删除 m*n 元素。
  • @JanneKarila 是正确的。我将存储公共交集和一组索引列表,这些集的索引需要与公共交集组合以获取原始集。
  • @PhamTrung 是的,确实如此,两个交叉路口都会更好。但是为了这个问题,假设我们只被允许与 M 个集合执行一个交集。

标签: algorithm set intersection


【解决方案1】:

如果字母表大小不太大...我会使用动态规划来解决这个问题...运行时间应该是 O(S*2^n), S = # of sets, n = # of字母

将 DP(i, bitmask) 定义为 set-0 到 set-i 中的任何子集可以取消的最大字符数,使用此位掩码

例如,我们现在有 3 个集合和 5 个字母 {a,b,c,d,e}

S0 = {a,d,e}, S1 = {b,c,e}, S2 = {a,c,e}

尝试使用0-1位来屏蔽每一组:

S0 = 11001 = 25, S1 = 10110 = 22, S2 = 10101 = 21

总共有 2^5 个不同的可能掩码,我们将在计算 DP(i, bitmask) 时遍历所有掩码

现在用 DP(0, x) 初始化(即简单地填充 x 的 1 位的#) 并使用以下转换填充 DP(i,x) for i > 0:

DP(i, x) = DP(i-1,x) + { # of 1-bit of x if (Si & x == x); 0 otherwise} Si is the bitmask of the Set i, & is bitwise and operation

答案是所有 x 的 DP(S-1, x) 的最大值

如果有很多可能的解决方案,这种方法可以找到所有可能的解决方案,下面是解决上述示例的 C++ 示例代码:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;

int s[3] = {25,22,21};
int dp[5][1<<5] = {0};

int bits(int x){
    int cnt = 0;
    while(x){ cnt += (x&1); x>>=1;}
    return cnt;
}

int main() {
    for(int i=0; i< (1<<5); i++) if((s[0]&i) == i){ dp[0][i] = bits(i); }

    for(int i=1; i<3;i++)
        for(int j=0; j< (1<<5); j++){
            dp[i][j] = dp[i-1][j];
            if((s[i]&j) == j) {dp[i][j] = max(bits(j), dp[i-1][j]+ bits(j));      }
        }


    int x = -1;
    for(int i=0; i< (1<<5); i++){
        x = max(x, dp[2][i]);
        printf("Maximum cancelled: %d,  current DP: %d, bitmask: %d\n", x, dp[2][i], i);
    }
    return 0;
}

只要DP状态的输出等于取消的最大数量,它的位掩码就是对应的解,你可以很容易地转换回英文字符,即上例中的{c,e}或{a,e}

已编辑: 为了回复下面的评论,我尝试在这里逐个回复:

第一季度。它仍然是指数级的吗?仅从指数到集合的 # 转移到字母的 #?

A1。是的。我有这个想法,因为我认为实际上字母大小不会太大......但理论上是的,它仍然是指数时间

第二季度。这个问题是 NP Complete 吗?

A2。好的,这是有趣的部分,这是我的想法,如果我错了请纠正我,我认为 是的,它是 NP Complete。我的想法是将这个问题建模为图形问题,见下图(我的 mspaint 技能很差)

我们得到了一个二分图,在与您原来的问题相同的意义上,我们现在想要找到 最大完全子图——一般来说,这是一个 Clique,这是一个很好的图——已知 NP 完全问题。

然后我认为,这是一个 二分图! 也许二分图中的 Clique 不是 NP Complete,但感谢 Google,我发现了另一个问题 Complete Bipartite Graph 并专注于第一个属性页面:

给定一个二部图,测试它是否包含一个完整的二部子图 Ki,i 对于参数 i 是一个 NP 完全问题。

总而言之,我认为这是 NP-Complete

第三季度。如何想出这样的DP方案?

A3。结合A1.,许多NPC问题实际上有一个伪多项式解决方案,据我所知,O(x * 2^y) 是一种很常见的形式,例如Hamiltonian Path ,可以在 O(n^2 * 2^n) 中求解。另外,如果您问自己,在考虑此 DP 解决方案时,我对背包问题也有类似的想法……但这与您的问题有点无关……

【讨论】:

  • 这是一个非常聪明的解决方案,但这不是指数时间吗?我猜您将限制因素从集合数移到了字母表中的字符数。无论如何,我想知道是否有一个解决方案可以解决任意数量的字母表中包含任意数量字符的集合,该解决方案超过指数时间。我猜答案是否定的,这个问题可以归结为其他一些 NP 完全问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-24
  • 1970-01-01
相关资源
最近更新 更多