列表的所有可能细分

时间:2018-07-06 22:03:50

标签: python permutation itertools discrete-mathematics

我刚刚编写了一个小的递归程序来生成列表的所有可能的细分:

def subdivisions(ls):
    yield [ls]
    if len(ls) > 1:
        for i in range(1, len(ls)):
            for lhs in subdivisions(ls[:i]):
                yield lhs + [ls[i:]]

>>> for x in subdivisions('abcd'): print x
... 
['abcd']
['a', 'bcd']
['ab', 'cd']
['a', 'b', 'cd']
['abc', 'd']
['a', 'bc', 'd']
['ab', 'c', 'd']
['a', 'b', 'c', 'd']

我蛮力强迫这样做,花了我很长时间才弄清楚。我想知道这叫什么,因为我肯定有个名字。

总的来说,我想知道如何从数学的角度学习这些知识,以及是否有很好的众所周知的编程库来涵盖这样的有用算法(我知道https://docs.python.org/3/library/itertools.html


[编辑]将此问题标记为重复的问题-get all possible partitions of a set -得到不同的答案。

它正在寻找{ {{1,2,3},{}} , {{1},{2,3}} , {{1,2},{3}} , {{1,3},{2}}, {{1},{2},{3}}} 而对我来说(用术语)的正确答案是{ {{1,2,3}} , {{1},{2,3}} , {{1,2},{3}} , {{1},{2},{3}}}

另外,提出这个问题的目的是弄清楚这是什么术语。我称它为“细分”;这个答案就是所谓的“分区”。我正在寻找一种可以列举所有这些模式的好资源,这样人们就不会再发明轮子了。

3 个答案:

答案 0 :(得分:4)

让我对这个问题进行一些数学解释。

想象一下:您有列表abcd。如果在其中放置一些分隔符(例如a|bc|d),则会将其划分为子列表。所有可能的分隔符均为a|b|c|d(其计数为N-1,其中N是列表的大小)。我们称它们为123

然后,列表{1, 2, 3}的所有combinations都会生成列表的所有细分。它们将有2**3 = 8:每个元素可以组合也可以不组合。 (所有这些组合称为powerset)。

这可以帮助您列出所有细分而无需递归:您只需将二进制数从0b000迭代到0b111(包括range(0, 2**(N-1))):

from itertools import zip_longest, chain

def yield_possible_splits(string):
    N = len(string)
    for i in range(2 ** (N-1)):
        spaces_bitmask = bin(i).replace('0b', '').rjust(N, '0')
        spaces = [' ' if bit == '1' else '' for bit in spaces_bitmask]
        yield ''.join(chain(*zip_longest(spaces, string, fillvalue='')))

使用itertools.product而不是二进制操作的等效变体:

from itertools import zip_longest, chain, product

def yield_possible_splits(string):
    N = len(string)
    for spaces in product(['', ' '], repeat=N-1):
        yield ''.join(chain(*zip_longest(string, spaces, fillvalue='')))

用法:

print(list(yield_possible_splits('abcd')))
# ['abcd', 'abc d', 'ab cd', 'ab c d', 'a bcd', 'a bc d', 'a b cd', 'a b c d']

答案 1 :(得分:3)

查找列表的所有partitions等同于查找要对列表进行切片的所有索引集。

通过示例,给定列表l = [1, 2, 3, 4],我们可以用索引[[1, 2], [3], [4]]的列表表示分区[2, 3]。特别是,这样的索引列表和分区列表之间存在一一对应的关系。

这意味着,给定列表l,我们可以找到range(1, len(l))的{​​{3}}并找到每个对应的分区。

代码

此解决方案使用powerset中的powerset函数。使用生成器比使用递归更有效。

from itertools import chain, combinations

def powerset(iterable):
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def partitions(lst):
    for indices in powerset(range(1, len(lst))):
        partition = []
        i = 0
        for j in indices:
            partition.append(lst[i:j])
            i = j
        partition.append(lst[i:])

        yield partition

示例

print(*partitions([1, 2, 3]))
# [[1, 2, 3]] [[1], [2, 3]] [[1, 2], [3]] [[1], [2], [3]]

答案 2 :(得分:0)

我的解决方案:

from itertools import chain, product
def p(l):
    return {(l,)} | {tuple(chain(*s)) for i in range(1, len(l)) for s in product(p(l[:i]), p(l[i:]))}

p('abcd')返回:

{('a', 'bcd'), ('abcd',), ('abc', 'd'), ('ab', 'c', 'd'), ('ab', 'cd'), ('a', 'b', 'cd'), ('a', 'bc', 'd'), ('a', 'b', 'c', 'd')}
相关问题