【问题标题】:How to find all partitions of a list S into k subsets (can be empty)?如何将列表 S 的所有分区查找为 k 个子集(可以为空)?
【发布时间】:2018-12-27 07:08:12
【问题描述】:

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

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

我想拆分为 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))

我还不确定,这段代码是否给了我正确的解决方案,也许还有其他方法?

【问题讨论】:

  • @wim 抱歉,我在考虑更复杂的示例,已更新
  • Thisthis 可能有用。
  • 你不能把一个列表放在一个集合中——它不是可散列的......所以不会以任何方式或形式发生——投票结束,因为不清楚你在问什么
  • @busybear 不是我想要的,你可以看到我有空子集
  • @PatrickArtner 可以提供帮助,现在我正在尝试为 [1, 2] 和 k=2 创建与我展示的相同的列表列表

标签: python python-3.x math partition


【解决方案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-k 系统中的多个n 数字。每个“数字”显示对应位置的值在哪个桶中。例如行

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

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

  • 1 去桶#1
  • 2 去桶#0
  • 3 去桶#0
  • 4 去桶#2

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

【讨论】:

  • 我喜欢你的想法,你觉得你的代码怎样才能更快?我刚刚运行了你和我的代码,对于k=3, S = range(14) 输入,我分别得到了 44.2 和 9.3 秒
  • @user3057645,nk 的护林员是什么?速度真的是个问题吗? itertools 的重要优势之一是它们实际上是用 C 实现的,因此速度很快。另一方面,如果您对这些列表进行任何重要的处理,我认为处理将花费比我的代码更多的时间来生成它们。 nk 的分区总数增长得非常快,因为从我的算法中可以清楚地看出,总数是 k^n(或 Python 中的 k**n
  • @user3057645,我还注意到您的代码包含一个错误:您多次生成某些项目。特别是如果groups 包含多个空桶(对于n=k=3,例如[(0,1,2),(),()]),您将生成非唯一排列(所以[(0,1,2),(),()][(),(0,1,2),()] 中的所有3 个和[(),(),(0,1,2)] 将被复制,更大的n 将有更多的过度项目)。发生这种情况是因为空列表实际上彼此相等,不应交换,但您的代码不知道这一事实。
  • 在某些情况下 S = range(400), k = 5,我需要这个可能的分区列表来找到可以为我提供更好解决更大问题的分区,即基本并行机-调度:\
  • @user3057645,我认为你没有收到我的错误报告。我声称对于S = range(3)k = 3 的情况,您将获得 30 条记录而不是预期的 27 条,因为您将拥有每条记录 [(0,1,2),(),()][(),(0,1,2),()][(),(),(0,1,2)]两次 在输出中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 2015-06-21
  • 1970-01-01
  • 2014-05-25
  • 1970-01-01
相关资源
最近更新 更多