解决集合问题的算法

时间:2009-09-20 22:02:06

标签: algorithm language-agnostic math set

如果我有一组值(我称之为x),以及x的多个子集:

计算所有可能的联合组合的最佳方法是什么,这些组合的联合等于x,但没有一个彼此相交。

一个例子可能是:

如果x是数字1到100的集合,我有四个子集:

  • a = 0-49
  • b = 50-100
  • c = 50-75
  • d = 76-100

然后可能的组合是:

  • a + b
  • a + c + d

6 个答案:

答案 0 :(得分:10)

您所描述的内容称为Exact cover问题。一般解决方案是Knuth的Algorithm XDancing 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)

一种方式(可能不是最好的方式)是:

  1. 创建一组重叠的所有子集对。
  2. 对于原始子集的每个组合,如果组合包含步骤1中列出的一对或多对,则说“假”,否则如果子集的并集等于x则说“是”(例如,如果总数为子集中的元素是x)

答案 4 :(得分:0)

实际算法似乎在很大程度上取决于子集的选择,产品操作和等同操作。对于加法(+),您似乎可以找到一个summation来满足您的需求(1到100的总和类似于您的a + b示例)。如果你能做到这一点,你的算法显然是O(1)。

如果你有一个更强硬的产品或等同运算符(让我们说两个术语的乘积意味着对字符串求和并找到SHA-1哈希),你可能会陷入嵌套循环,这将是O(n ^ x )其中x是术语/变量的数量。

答案 5 :(得分:0)

根据您必须使用的子集,使用更天真的算法可能更有利。您不必比较整个子集,而只需要比较上限和下限。

如果你在谈论随机子集,而不是一个范围,那么尼克约翰逊的建议可能是最好的选择。