问题描述
我有一个由 N 个元素组成的集合 S。每个元素由长度为 L 的比特串表示。
例如,当 L=5 时,S 可以由 { 01010,01100,10100,00001 }
如果一对元素没有任何共同点(即如果它们的按位 AND 为零),则它们是兼容的。例如:
- 01010 与 01100 不兼容,因为
01010 & 01100 = 01000
(非零) - 01010 与 10100 兼容,因为
01010 & 01010 = 00000
(零)
如果 S 的所有元素的成对按位 AND 为零,则 S 的子集是兼容的。
例如,子集 { 01010,00001 }
是兼容的,因为以下所有语句都为真
01010 & 10100 = 0
01010 & 00001 = 0
10100 & 00001 = 0
我想找到 S 的所有可能的兼容子集。 鉴于上面的 S 示例,这将是:
{ 01010 }
{ 01100 }
{ 10100 }
{ 00001 }
{ 01010,10100 }
{ 01010,00001 }
{ 01100,00001 }
{ 10100,00001 }
{ 01010,00001 }
实现这一目标的最佳算法是什么?
这个问题可以用图来重述,其中 S 的每个元素都是一个节点,兼容元素通过边连接。在这种情况下,问题就变成了找到所有可能完全连接的节点集(即每对节点之间是否存在直接边)。
解决方法
你可以使用这种方法
定义 N
组如下
在组 L
中,将所有具有位 i
的节点放入其 1
等级
所以问题是从每个组中选择 i
或 one
。
显然每组不能超过 none
个元素
*如果某个节点有超过一位 one
那么它会被计算为重复所以我们需要在最后将它的 1
计数
所以结果计数如下:
1
您应该忽略除法部分中的所有零 (Count(L_1)+1) * (Count(L_2)+1) *... * (Count(L_L)+1) / (countOfOne(element1) * ...* countOfOne(elementN))
元素
我解决了问题,分享解决方案可能有用。
首先我枚举集合中的元素。例如:
S = { 0: 01010,1: 01100,2: 10100,3: 00001 }
现在任何子集都可以由索引集按递增顺序定义。例如
{ 01010,00001 } -> {0,3}
我将 P 定义为一对 (indices,bitmask)
,其中位掩码是通过按位或子集中的所有元素获得的。例如:
{ 01010,00001 } -> ({0,3},01011}
我将解 G 描述为 P 的向量。我用所有仅包含一个元素的子集对其进行初始化,这些子集当然是解的一部分。
G = [ {{0},01010},{{1},01100},{{2},10100},{{3},00001} ]
n
元素的每个兼容子集都必须从 n-1
元素添加单个元素的兼容子集中获得。这表明了伪代码中的算法:
令 firstSol(n)
是一个函数,返回 G 中第一个大小为 n 的解的索引
令 lastSol(n)
是一个函数,返回 G 中最后一个大小为 n 的解的索引
设 N 为 S 的大小
int n = 1;
int s;
do
s = nSolutions(G)
for (i = firstSol(n); i <= lastSol(n); ++i)
{vec,mask} = G[n];
for (j = vec[n-1] + 1; j < N; ++j)
if (mask & S[j] == 0)
append to G: {{vec,j},mask | S[j}}
while (s < nSolutions(G))
一旦没有找到新的解决方案,算法就会停止。