可以转换从每组中选择不同数量的项目的问题
通过重复
设置所需的次数,即 A 选择 3 ,B 选择 2 ,C 选择 2 变为
从每个 A、A、A、B、B、C、C 中选择 1。
这就是寻找不同代表系统 (sdr) 的问题。
可以通过以下方法找到一个集合的集合是否至少有一个sdr。
设集合为 S = { A_0 , A_1 , A_3 , ... , A_n }
并且 X 是所有 A_i 的并集,并将其元素称为 x_0 , x_1 , x_2 ,...
考虑(二分)图,其中每个 A_i 的节点称为 N(A_i),每个 x_i 的节点称为 N(x_i),如果 x_j 在 A_i 中,则边连接 N(A_i) 和 N(x_j)
寻找 sdr 的问题与寻找覆盖每个 N(A_i) 的该图的匹配的问题相同。这样的匹配是最大匹配,所以如果至少存在一个
Hopcroft Karp algorithm 会找到它。因此,要查看一组集合是否在其图上运行 hopcroft karp 的 sdr,然后检查每个 N(A_i) 的匹配覆盖
我们如何检查输出集是否有效? (例如示例中的 {1,2,3,4,9,13,15})
有效输出集的元素与输入集 A_i 至少一一对应
即让输出集为 W = { y_0 , y_1, ... , y_n } 那么至少有一种方法可以为每个 y_j 选择一个不同的 A_i 使得 y_j 在 A_i
如果我们构造一个新的集合集合 SN = { B_0 , B_1, B_3, ... , B_N } 其中 B_i 是包含 y_i 的集合 A_j 的集合
找到这个对应关系只是为 SN 找到一个 sdr。所以要检查输出集是否有效,检查是否有这个新集合集合的 sdr。
同样,如果我们对 W 的一个子集进行此构造,那么如果有一个用于 W 的 sdr,就会有一个 sdr
使用模块 networkx 来实现 hopcroft karp 以下检查集合的集合是否具有 sdr
import networkx as nx
def hasSDR(s):
x=set.union(*s)
setnames = ["set"+str(i+1) for i in range(len(s)) ]
B = nx.Graph()
B.add_nodes_from( setnames , bipartite=0)
B.add_nodes_from( x , bipartite=1)
B.add_edges_from( [( setnames[i] , j ) for i in range(len(s)) for j in s[i] ] )
hk = nx.bipartite.hopcroft_karp_matching(B,top_nodes=setnames)
return all( i in hk for i in setnames )
然后我们可以通过
找到一个输出集是否有效(假设它的大小正确)
def possibleSDRSet( s , w ):
sn = [ { j for j in range(len(s)) if i in s[j] } for i in w ]
return hasSDR( sn )
如果给这个函数 w 的大小小于集合中集合的数量,它只会在 w 是没有有效输出集合的子集时返回 false
遍历 X 的所有长度为 n 的子集,然后测试它们是否是我们可以做的有效输出集(使用生成器)(这是问题中的想法 2)
def allsets( s , req = set() ):
if len(req) == len(s):
if possibleSDRSet( s , req ): yield req
return
x = set.union(*s) - req
if len(x) == 0: return
xr = x.pop()
sr = [i - {xr} for i in s]
for i in allsets( sr , req ): yield i
for i in allsets( s , req | {xr} ): yield i
这将集合集合和所需元素集合作为参数,然后选择不在所需集合中的元素,
然后它将搜索拆分为没有该元素的集合和具有该元素的集合;第一个方法是创建一个新的集合集合,并从集合中的所有集合中删除元素
第二个是将元素添加到所需的集合中
然而,这些分支中的许多可能不需要考虑,当我们从集合中的所有集合中删除一个元素时,可能不再有 sdr,因此我们可以检查
如果仍然有一个带有 hasSDR 的 sdr,并且只有在存在的情况下继续在该分支中。
当我们将一个元素添加到所需列表时, possibleSDRSet 可能能够排除该分支中存在任何 SDR,因此我们也可以检查这一点,给出
def _findSDRSetsRec( s , req ):
if len(req) == len(s):
yield req
return
x = set.union(*s) - req
xr = x.pop()
sr = [ i - {xr} for i in s ]
if hasSDR( sr ):
for i in _findSDRSetsRec( sr , req ): yield i
reqp = req | {xr}
if possibleSDRSet( s , reqp ):
for i in _findSDRSetsRec( s , reqp ): yield i
def findSDRSets(s):
if not hasSDR(s): return
for i in _findSDRSetsRec( s , set() ): yield i
这些函数中的第一个假设 s 和 req 已通过两个测试进行检查,因此需要一个包装器来检查是否存在 sdr。
做例子:
s = ({1,3,5,7,9,11,13,15},)*3 +({4,7,8,9,10,12},)*2 + ({1,2,5,6,7,14},)*2
for i in findSDRSets(s): print(i)
如果只有少量有效输出集,使用此方法应该会比您列出的那些提供更快的速度,但是对于给定的示例,allsets 方法看起来更快