【问题标题】:Set partitions in Python在 Python 中设置分区
【发布时间】:2013-10-22 11:58:40
【问题描述】:

我有一个 [1,2,3] 数组

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

结果:

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

【问题讨论】:

  • @thefourtheye 可能不仅仅是combinations,但我很确定解决方案将是import itertools,然后是两行左右的代码。
  • 您希望[1,2,3,4] 得到什么结果?
  • 你实际上是在寻找set partitions
  • 我同意;这个问题不是重复的(无论如何建议的问题;可能还有另一个问题。)itertools.combinations 不会产生设置分区。

标签: python arrays combinatorics


【解决方案1】:

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

我们的想法是,我们找到加起来等于列表长度的所有整数组合,然后得到具有该长度切片的列表。

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

我从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】:

    既然这个好问题已经复活,这里有一个新的答案。

    问题递归解决:如果你已经有一个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]]
    

    【讨论】:

    • 我想这个解决方案比我想象的更明显:-)(在碰到这个问题的新问题中发布了相同的内容)
    • 该死!如果我看到它,我可以节省自己的时间来解决这个问题……但也错过了乐趣。 (但是这个问题被编辑了,我没有看到另一个问题的链接。)
    • 我同意,这很有趣。和一个很好的锻炼。新问题被暂时标记为与此问题重复,这就是此问题引起注意然后进行编辑的方式。
    • 它是如何以 not 为复制品的呢?它们完全相同。
    • @étale-cohomology,我自己想出了。我确定我不是第一个这样做的人。
    【解决方案3】:

    考虑more_itertools.set_partitions

    给定

    import more_itertools as mit
    
    
    lst = [1, 2, 3]
    

    代码

    展平一系列k设置分区:

    [part for k in range(1, len(lst) + 1) for part in mit.set_partitions(lst, k)]
    

    输出

     [((1, 2, 3),),
      ((1,), (2, 3)),
      ((2,), (1, 3)),
      ((3,), (1, 2)),
      ((1,), (2,), (3,))]
    

    more_itertools 是第三方包。通过> pip install more_itertools安装。

    【讨论】:

      【解决方案4】:

      如果有人想在 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' ] ]
      ]
      

      【讨论】:

        猜你喜欢
        • 2015-06-25
        • 2021-11-01
        • 2010-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多