在Python中设置分区

时间:2013-10-14 20:05:47

标签: python arrays combinatorics

我有一个[1,2,3]

数组

我想使用数组的所有元素进行所有可能的组合:

结果:

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

4 个答案:

答案 0 :(得分:41)

由于这个好问题已经复活,这里有一个新答案。

问题以递归方式解决:如果您已经有 n-1 元素的分区,那么如何使用它来分区 n 元素?将 n 元素放在其中一个现有子集中,或将其添加为新的单例子集。这就是全部;没有itertools,没有集合,没有重复的输出,以及只有 n partition()的调用:

def partition(collection):
    if len(collection) == 1:
        yield [ collection ]
        return

    first = collection[0]
    for smaller in partition(collection[1:]):
        # insert `first` in each of the subpartition's subsets
        for n, subset in enumerate(smaller):
            yield smaller[:n] + [[ first ] + subset]  + smaller[n+1:]
        # put `first` in its own subset 
        yield [ [ first ] ] + smaller


something = list(range(1,5))

for n, p in enumerate(partition(something), 1):
    print(n, sorted(p))

输出:

1 [[1, 2, 3, 4]]
2 [[1], [2, 3, 4]]
3 [[1, 2], [3, 4]]
4 [[1, 3, 4], [2]]
5 [[1], [2], [3, 4]]
6 [[1, 2, 3], [4]]
7 [[1, 4], [2, 3]]
8 [[1], [2, 3], [4]]
9 [[1, 3], [2, 4]]
10 [[1, 2, 4], [3]]
11 [[1], [2, 4], [3]]
12 [[1, 2], [3], [4]]
13 [[1, 3], [2], [4]]
14 [[1, 4], [2], [3]]
15 [[1], [2], [3], [4]]

答案 1 :(得分:10)

与我建议的评论不同,我无法快速找到基于itertools的相对快速的解决方案!编辑:这不再是真的,我有一个相当短的(但缓慢且不可读)的解决方案,主要使用itertools,请参阅答案的结尾。这就是我所得到的:

我们的想法是找到所有整数组合,这些整数组合加起来就是列表的长度,然后得到包含该长度的切片的列表。

E.g。对于长度为3的列表,组合或分区是(3),(2,1),(1,2)和(1,1,1)。所以我们返回列表的前3项;前2然后下1;第一个1然后是下一个2和第一个1,然后是下一个1,然后是下一个。

我从here获得了整数分区的代码。但是,分区函数不会返回分区的所有排列(即对于3,它只返回(3),(2,1)和(1,1,1)。所以我们需要调用itertools.permutations每个分区。然后我们需要删除重复项 - 就像permutations([1, 2, 3])[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]; permutations([1, 1, 1])[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]。删除重复项的简单方法是转换每个列表将元组转换为set

然后剩下的就是获取列表中的片段以获取元组中的长度。 例如。 f([1, 2, 3], [0, 0, 1, 2, 1, 0])转到[[0], [0, 1], [2, 1, 0]]

我对此的定义是:

def slice_by_lengths(lengths, the_list):
    for length in lengths:
        new = []
        for i in range(length):
            new.append(the_list.pop(0))
        yield new

现在我们只是结合一切:

def subgrups(my_list):
    partitions = partition(len(my_list))
    permed = []
    for each_partition in partitions:
        permed.append(set(itertools.permutations(each_partition, len(each_partition))))

    for each_tuple in itertools.chain(*permed):
        yield list(slice_by_lengths(each_tuple, deepcopy(my_list)))

>>> for i in subgrups(my_list):
        print(i)

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

此外,您还需要在该计划的顶部执行import itertoolsfrom copy import deepcopy

编辑:您的给定输出不清楚。我假设你想要我给你的功能,但你的输出还包含[[1,3],[2]],其中输出中的元素顺序不同,与你建议的其他输出不同(我冒昧地假设你实际上想要[[1, 2], [3]]而不是[[1, 2], 3])。

也就是说,我认为你的意思是输出是这样的:

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

如果实际上是这样的话:

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

然后,您只需要为原始列表的每个3长度排列调用subgrups,例如对于itertools.permutations(my_list, len(my_list))中的每个排列。

编辑:现在坚持我对基于itertools的简短解决方案的承诺。警告 - 它可能既不可读又慢。

首先我们将slice_by_lengths替换为:

def sbl(lengths, the_list):
    for index, length in enumerate(lengths):
        total_so_far = sum(lengths[:index])
        yield the_list[total_so_far:total_so_far+length]

然后从this回答我们得到整数分区函数:

def partition(number):
    return {(x,) + y for x in range(1, number) for y in partition(number-x)} | {(number,)}

这个函数实际上为我们获取了整数分区的所有排列,所以我们不需要

for each_partition in partitions:
    permed.append(set(itertools.permutations(each_partition, len(each_partition))))

了。但是,它比我们之前的要慢得多,因为它是递归的(我们在Python中实现它)。

然后我们把它放在一起:

def subgrups(my_list):
    for each_tuple in partition(len(my_list)):
        yield list(slice_by_lengths(each_tuple, deepcopy(my_list)))

或者可读性较差,但没有函数定义:

def subgrups(my_list):
    for each_tuple in (lambda p, f=lambda n, g:
                          {(x,) + y for x in range(1, n) for y in g(n-x, g)} | {(n,)}:
                              f(p, f))(len(my_list)):
        yield list(my_list[sum(each_tuple[:index]):sum(each_tuple[:index])+length] for index, length in enumerate(each_tuple))

这是一个函数定义和两行,所以非常接近我最初的陈述(尽管可读性和速度都慢得多)!

(称为subgrups的函数,因为最初要求查找“所有子项目”的问题)

答案 2 :(得分:0)

考虑more_itertools.set_partitions

给出

>>=

代码

平整一系列fmap集分区:

import more_itertools as mit


lst = [1, 2, 3]

输出

k

more_itertools是第三方软件包。通过[part for k in range(1, len(lst) + 1) for part in mit.set_partitions(lst, k)] 安装。

答案 3 :(得分:0)

如果有人想在 JS 中使用它。这确实花了我一些时间来实施。我在 JS 的“价值和参考”方面苦苦挣扎。

算法与上面解释的@alexis 相同。

函数 deepCopy 是克隆一个数组,而不是复制到一个数组。

function deepCopy(val){
    return JSON.parse(JSON.stringify(val));
}

function partitions(arr) {
    var results = [];

    if (arr.length == 0) {
        results.push([[]]);
        return results;
    }

    if (arr.length == 1) {
        results.push(new Array(arr));
        return results;//[[[1]]]
    }

    var last = arr[arr.length - 1];
    var sub = partitions(arr.slice(0, arr.length - 1));//remove the last item

    //partitions(2) => [ [ [ 's1', 's2' ] ], [ [ 's1' ], [ 's2' ] ] ]
    //val => [ [ 's1', 's2' ] ] or [ [ 's1' ], [ 's2' ] ]
    //set => [ 's1', 's2' ] or [ 's1' ], [ 's2' ]
    sub.map((partition) => {
        //val => each partition
        //1) insert the "last" into each set, together with the rest of sets in the same partition makes a new partition
        partition.map((set) => {
            //set=>each set of one particular partition
            set.push(last);
            results.push(deepCopy(partition));
            set.pop();
        });
        //2), insert the "last" as a singlton set into the partition, make it a new partition
        partition.push([last]);
        results.push(deepCopy(partition));
        partition.pop();
    });

    return results;
}

var arr = ["s1", "s2", "s3"];
const results = partitions(arr);
console.log(results);

输出:

[
  [ [ 's1', 's2', 's3' ] ],
  [ [ 's1', 's2' ], [ 's3' ] ],
  [ [ 's1', 's3' ], [ 's2' ] ],
  [ [ 's1' ], [ 's2', 's3' ] ],
  [ [ 's1' ], [ 's2' ], [ 's3' ] ]
]