生成集合的所有分区

时间:2015-06-17 13:37:24

标签: algorithm set combinatorics backtracking

对于一组A = {1, 2, 3, ..., n}形式。它被称为集A的分区,一组k<=n元素,它们遵循以下定理:

a)A的所有分区的并集是A

b)A的2个分区的交集是空集(它们不能共享相同的元素)。

例如。 A = {1, 2,... n}我们有分区: {1, 2, 3} {1, 2} {3} {1, 3} {2} {2, 3} {1} {1} {2} {3}

这些理论概念在我的算法教科书中提出(顺便说一句,本章节是&#34; Backtracking&#34;章节的一部分)。我应该找到一个算法来生成给定集合的所有分区。我整天都在努力解决这个问题,但我无法找到解决方案。你能解释一下这个算法是如何工作的吗?另外,你能给我一个算法的伪代码草图吗?

3 个答案:

答案 0 :(得分:15)

有一个重要的观察(在评论中)一组n元素的分区可以表示为[ p 1 <的形式的整数序列/ sub>,... p n ]其中 p i < / sub>是元素 i 的分区号。要使这样的序列有效,它必须遵守 p 1 为1的规则,并且对于每个 j 其中1&lt; j n ,有一些 i &lt; j 使 p j p i < / EM> 1。或者换句话说,在序列的任何前缀中,都不会跳过整数。

现在,有一种按字典顺序枚举约束序列的标准算法,包括以下内容:

  • 从最小的序列开始。
  • 按字典顺序查找下一个序列:
    • 向后扫描序列并找到最右边的&#34;可递增的&#34;元件。 (可递增元素是一个元素,使得有一些更大的元素可以替换该元素,并且到那时为止的结果子序列是至少一个有效序列的前缀。)
    • 将该元素更改为下一个可行的较大元素(即产生有效前缀的元素,如上所述),然后用尽可能小的值填充其右侧的剩余元素(如果有的话)。
    • 如果没有可递增元素,则枚举已终止。

有关搜索可递增元素的若干规定,此算法最差O( n ),当元素为O时,通常为O( 1 )递增通常接近序列的末尾。 (例如,使用此算法枚举置换是O(1)以找到下一个置换,只要您可以在O(1)中找到&#34; next元素&#34;。

为了将此算法应用于分区案例,我们观察到以下内容:

  1. 最小的序列是[1,... 1]
  2. 元素 p i 可递增,条件是:
    • P <子> I &LT; 名词
    • 有一些 j &lt; i p i = p <子> Ĵ
  3. 可行前缀的最小后缀是[1,... 1]
  4. 在观察2中陈述条件的另一种方式是元素是可递增的,除非它的值是 n ,或者它是序列中具有其值的第一个元素。如果我们也保持序列[ m 1 ,... m n,我们可以在O(1)中做出这个决定 ]其中 m 1 为0, m i < / sub>是 m 的最大值 i -1 p i -1 m 维护起来很简单,它允许我们将增量条件重写为简单的 p i <子> I

    很容易看出 Next Partition 可以在O( n )时间内实现,但是当它发生时它也是分摊时间O的情况(1)。粗略地说,这是因为在大多数情况下,序列的最后一个元素是可递增的。

答案 1 :(得分:2)

如果您的集合不大(或者使用堆栈),您可以尝试递归答案:

原则如下,你有一个功能可以回馈:

rec_func(SET) = List of List of Set

工作如下:

rec_func(SET) =
  if SET = {empty}:
    // if no element, easy to give the answer
    return([[]])
  else:
    // 1. Remove one element from the set : 'a' to this set
    a = SET.pop()
    // 2. Call rec_func :
    list_of_list_of_set = rec_func(SET\'a')  
    response = []
    // 3. For every possibilities given by the function add the element 'a' :
    For every list_of_set in list_of_list_of_set  :
       // Case 1, you add 'a' to list_of_set
       response.push( [{'a'} + list_of_set] )
       // Case 2, for every set, you create a copy where you add 'a'
       for every set in list_of_set:
           response.push( [{set,'a'} + list_of_set\set] )

    // The function return the list of list of set created.        
    return(response)

答案 2 :(得分:-1)

我正在研究一种有效的算法,该算法根据@rici定义的关键字方法生成如前所述的集合的所有分区。以python编写的以下算法可以实现,并且仍然可以进行优化。我去做。如您所知,这个问题是NP完全的!由于优化,可能有一些奇怪的符号,如try / except。但是,n和k变量用于定义via n,集合有多少不同的元素,k是允许集合的不同类的数量。信息:算法生成所有分区UP到不同类的数量,而不仅仅是那些类!!!

def partitions():
        global n
        global k
        codeword = [1 for digitIndex in range(0, n)]
        while True:
                print codeword
                startIndex = n - 1
                while startIndex >= 0:
                        maxValue = max(codeword[0 : startIndex])
                        if codeword[startIndex] > maxValue or maxValue > k or codeword[startIndex] >= k:
                                codeword[startIndex] = 1
                                startIndex -= 1
                        else:
                                codeword[startIndex] += 1
                                break

n = 12
k = 2
try:
        partitions()
except:
        pass