如果我有一组值(我称之为x),以及x的多个子集:
计算所有可能的联合组合的最佳方法是什么,这些组合的联合等于x,但没有一个彼此相交。
一个例子可能是:
如果x是数字1到100的集合,我有四个子集:
然后可能的组合是:
答案 0 :(得分:10)
您所描述的内容称为Exact cover问题。一般解决方案是Knuth的Algorithm X,Dancing Links算法是具体的实现。
答案 1 :(得分:1)
给定x的元素的顺序(如果需要,可以使用一个,对于有限或可数集合,这总是可行的):
让“目前为止选择的集合”为空。考虑x的最小元素。查找包含x且与目前为止所选的任何集合不相交的所有集合。对于每个这样的集合依次递归,将所选择的集合添加到“到目前为止选择的集合”,并查看x的最小元素而不是任何选择的集合。如果你到达没有x元素的点,那么你已经找到了解决方案。如果您到达的点没有包含您正在寻找的元素的未选择集,并且与您已经选择的任何集相交,那么您找不到解决方案,所以回溯。< / p>
这使用堆栈与非交叉子集的数量成比例,因此请注意这一点。它也会占用大量时间 - 如果在您的示例中,子集都是连续的范围,则可以提高效率。
答案 2 :(得分:1)
这是一种糟糕的方式(递归,做了很多冗余的工作)。但至少它的实际代码可能是“高效”解决方案的一半。
def unique_sets(sets, target):
if not sets and not target:
yield []
for i, s in enumerate(sets):
intersect = s.intersection(target) and not s.difference(target)
sets_without_s = sets[:i] + sets[i+1:]
if intersect:
for us in unique_sets(sets_without_s, target.difference(s)):
yield us + [s]
else:
for us in unique_sets(sets_without_s, target):
yield us
class named_set(set):
def __init__(self, items, name):
set.__init__(self, items)
self.name = name
def __repr__(self):
return self.name
a = named_set(range(0, 50), name='a')
b = named_set(range(50, 100), name='b')
c = named_set(range(50, 75), name='c')
d = named_set(range(75, 100), name='d')
for s in unique_sets([a,b,c,d], set(range(0, 100))):
print s
答案 3 :(得分:0)
一种方式(可能不是最好的方式)是:
答案 4 :(得分:0)
实际算法似乎在很大程度上取决于子集的选择,产品操作和等同操作。对于加法(+),您似乎可以找到一个summation来满足您的需求(1到100的总和类似于您的a + b示例)。如果你能做到这一点,你的算法显然是O(1)。
如果你有一个更强硬的产品或等同运算符(让我们说两个术语的乘积意味着对字符串求和并找到SHA-1哈希),你可能会陷入嵌套循环,这将是O(n ^ x )其中x是术语/变量的数量。
答案 5 :(得分:0)
根据您必须使用的子集,使用更天真的算法可能更有利。您不必比较整个子集,而只需要比较上限和下限。
如果你在谈论随机子集,而不是一个范围,那么尼克约翰逊的建议可能是最好的选择。