【问题标题】:Algorithm for solving set problem解决集合问题的算法
【发布时间】:2009-09-20 22:02:06
【问题描述】:

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

找出所有可能的子集组合的最佳方法是什么,这些子集的并集等于 x,但它们都不相交。

一个例子可能是:

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

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

那么可能的组合是:

  • a + b
  • a + c + d

【问题讨论】:

  • 我很怀疑。是作业吗?
  • spender:这不是家庭作业,我保证!它实际上是我需要在现实世界的程序中解决的问题的一个非常笼统的版本。
  • rodrigoap:谢谢,愚蠢的错误 - 现在已修复。
  • 顺便说一句,我对我这么快得到答案的数量和质量感到惊讶!
  • 我必须在我的硕士论文的索引算法中解决一个类似的问题。我可以向你保证,除非教授真的是虐待狂,否则这几乎不会是本科阶段的家庭作业!

标签: algorithm language-agnostic math set


【解决方案1】:

您所描述的称为Exact cover 问题。通用的解决方案是 Knuth 的Algorithm XDancing Links 算法是一个具体的实现。

【讨论】:

    【解决方案2】:

    给定 x 的元素的良好顺序(如有必要,组合一个,这对于有限集或可数集总是可能的):

    让“到目前为止选择的集合”为空。考虑 x 的最小元素。找出所有包含 x 并且不与到目前为止选择的任何集合相交的集合。对于每个这样的集合依次递归,将所选集合添加到“迄今为止选择的集合”中,并查看不在任何所选集合中的 x 的最小元素。如果你到达一个点,其中没有 x 的元素,那么你已经找到了一个解决方案。如果你到达一个点,其中没有包含你正在寻找的元素的未选择集合,并且它不与你已经选择的任何集合相交,那么你没有找到解决方案,所以回溯。

    这使用与非相交子集的数量成正比的堆栈,因此请注意这一点。它还使用大量时间 - 如果像在您的示例中那样,子集都是连续的范围,那么您的效率会高得多。

    【讨论】:

      【解决方案3】:

      这是一个不好的方法(递归,做了很多多余的工作)。但至少它的实际代码可能是“高效”解决方案的一半。

      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
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

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

          如果你有一个更严格的乘积或相等运算符(假设取两个项的乘积意味着对字符串求和并找到 SHA-1 哈希),你可能会被困在嵌套循环中,这将是 O(n^x ) 其中 x 是项/变量的数量。

          【讨论】:

            【解决方案6】:

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

            如果您说的是随机子集,而不是一个范围,那么 Nick Johnson 的建议可能是最好的选择。

            【讨论】:

              猜你喜欢
              • 2011-02-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多