对于一组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;章节的一部分)。我应该找到一个算法来生成给定集合的所有分区。我整天都在努力解决这个问题,但我无法找到解决方案。你能解释一下这个算法是如何工作的吗?另外,你能给我一个算法的伪代码草图吗?
答案 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。或者换句话说,在序列的任何前缀中,都不会跳过整数。
现在,有一种按字典顺序枚举约束序列的标准算法,包括以下内容:
有关搜索可递增元素的若干规定,此算法最差O( n ),当元素为O时,通常为O( 1 )递增通常接近序列的末尾。 (例如,使用此算法枚举置换是O(1)以找到下一个置换,只要您可以在O(1)中找到&#34; next元素&#34;。
为了将此算法应用于分区案例,我们观察到以下内容:
在观察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