如何找到列表S的所有分区为k个子集(可以为空)?

时间:2018-12-26 21:05:44

标签: python python-3.x math partition

我有一个唯一元素的列表,比方说[1,2],我想将其拆分为k = 2个子列表。现在,我想拥有所有可能的子列表:

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

我想拆分为1 <= k <= n个子列表,所以对于k = 1它将是:

[ [1, 2] ]

如何使用Python 3做到这一点?

更新:我的目标是获取N个唯一编号列表的所有可能分区,其中每个分区都有k个子列表。我想展示比上例更好的例子,希望我不会错过任何东西。因此,对于列表[1、2、3]和k = 2,我想要下一个列表:

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

更新2:到目前为止,我已经结合了两个建议,几乎没有修改就得到了下一个代码:

def sorted_k_partitions(seq, k):
    """Returns a list of all unique k-partitions of `seq`.

    Each partition is a list of parts, and each part is a tuple.

    The parts in each individual partition will be sorted in shortlex
    order (i.e., by length first, then lexicographically).

    The overall list of partitions will then be sorted by the length
    of their first part, the length of their second part, ...,
    the length of their last part, and then lexicographically.
    """
    n = len(seq)
    groups = []  # a list of lists, currently empty

    def generate_partitions(i):
        if i >= n:
            yield list(map(tuple, groups))
        else:
            if n - i > k - len(groups):
                for group in groups:
                    group.append(seq[i])
                    yield from generate_partitions(i + 1)
                    group.pop()

            if len(groups) < k:
                groups.append([seq[i]])
                yield from generate_partitions(i + 1)
                groups.pop()

    result = generate_partitions(0)

    # Sort the parts in each partition in shortlex order
    result = [sorted(ps, key = lambda p: (len(p), p)) for ps in result]
    # Sort partitions by the length of each part, then lexicographically.
    result = sorted(result, key = lambda ps: (*map(len, ps), ps))

    return result

使用此功能,我可以下一步:

import itertools as it
k=2
S = [1, 2, 3]
for i in (range(k)):
    for groups in sorted_k_partitions(S, k-i):
        for perm in it.permutations(groups+[tuple() for j in range(i)]):
            print(perm)

输出为:

((1,), (2, 3))
((2, 3), (1,))
((2,), (1, 3))
((1, 3), (2,))
((3,), (1, 2))
((1, 2), (3,))
((1, 2, 3), ())
((), (1, 2, 3))

我不确定,这段代码是否可以为我提供正确的解决方案,也许还有其他方法?

1 个答案:

答案 0 :(得分:1)

这是替代解决方案:

def partition_k(l, k):
    n = len(l)
    if k > n:
        raise ValueError("k = {0} should be no more than n = {1}".format(k, n))

    if n == 0:
        yield []
        return

    pos = [0] * n
    while True:
        # generate output for the value
        out = [[] for _ in range(k)]
        for i in range(n):
            out[pos[i]].append(l[i])
        yield out

        #increment the value
        pos[0] += 1
        for i in range(n):
            # should we carry from this digit to the next one?
            if pos[i] == k:
                # overflow of the whole value?
                if i == n - 1:
                    return
                pos[i] = 0
                pos[i + 1] += 1
            else:
                break

n为列表的长度,k为分区的数量。该代码背后的思想是,在base-n系统中,输出的每一行都可以表示为许多k位数字。每个“数字”显示值在哪个存储桶中到达相应位置。例如line

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

可以被编码为[1,0,0,2],这意味着

  • 1进入了第一个存储桶
  • 2进入存储桶#0
  • 3进入存储桶#0
  • 4进入了第2个桶

显然,每个这样的n数字基数-k数字都表示列表的有效分区,并且每个分区都由某个数字表示。因此,要生成所有分区,我们只需要遍历所有此类数字并生成相应的分区即可。如果您使用数字列表来表示数字(在代码中为pos),则更容易做到。