A盒中的N个元素的置换?

时间:2016-12-15 02:55:06

标签: python combinations permutation enumeration

我正在尝试找到所有可能的方法来在A框中排列N个元素,其中框的顺序和元素的顺序无关紧要,但是在同一个框中哪些元素在一起并不重要。例如,3个盒子和3个元素的预期结果如下:

            box_1       box_2      box_3
case-1      [1,2,3]    []          []
case-2      [1,2]      [3]         []
case-3      [1,3]      [2]         []
case-4      [2,3]      [1]         []
case-5      [1]        [2]         [3]

这是一个类似但更普遍的问题,而不是这里提出的问题:Enumeration of combinations of N balls in A boxes?

我非常感谢对这个问题的贡献,最好是使用python语言。

1 个答案:

答案 0 :(得分:0)

这些被称为set partitions。可以在此处找到递归函数:Set partitions in Python。更有效率的自下而上"递归版本是这样的:

def generate_partitions(a):
    a = list(a)
    n = len(a)
    partition = [] # a list of lists, currently empty

    def assign(i):
        if i >= n:
            yield [list(part) for part in partition]
        else:
            # assign a[i] to an existing part
            for part in partition:
                part.append(a[i])
                yield from assign(i + 1)
                part.pop()

            # assign a[i] to a completely new part
            partition.append([a[i]])
            yield from assign(i + 1)
            partition.pop()

    if n:
        yield from assign(0)
    else:
        yield [[]]


for partition in generate_partitions([1,2,3]):
    print(*partition)

输出:

[1, 2, 3]
[1, 2] [3]
[1, 3] [2]
[1] [2, 3]
[1] [2] [3]

这不会像您的示例中那样生成空框,但是增加生成器这样做很简单。

对于迭代算法,请参阅"有效生成集合分区"作者:Michael Orlov(2002)。请注意,设置分区的数量会快速增长非常,因此即使是迭代算法也需要一些时间来枚举即使是适度大小的集合的所有分区。

计算设置分区的数量而不生成它们,请参阅Bell Numbers(OEIS A000110)。以下是在Python中计算Bell数的一种可能(非常有效)的过程:

def bell(n):
    "-> the n'th Bell number."
    assert n >= 0, n

    # loop will execute at least once
    for b in bell_sequence(n): 
        pass 

    return b

def bell_sequence(n):
    """Yields the Bell numbers b(0),b(1)...b(n).

    This function requires O(n) auxiliary storage.
    """
    assert n >= 0, n
    # compute Bell numbers using the triangle scheme
    yield 1 # b(0)

    row = [1] + (n-1)*[0]

    for i in range(0, n):
        row[i] = row[0]

        for k in reversed(range(i)):
            row[k] += row[k + 1]

        yield row[0] # b(i + 1)