A list of elements is given. I want to have all the possibilities to divide this list into any number of partitions so that each partition has at least x elements. The order of the partitions in the list and the order of the elements in the partition do not matter. E.g.: List = [1,2,3,4] get_partitions(list,2) should return:
[[1,2,3,4],
[[1,2],[3,4]],
[[1,3],[2,4]],
[[1,4],[2,3]]]
List = [1,2,3,4] get_partitions(list,1) should return:
[[1,2,3,4],
[[1,2],[3,4]],
[[1,3],[2,4]],
[[1,4],[2,3]],
[[1],[2],[3,4],
...]
I have started to implement this recursively in Python, but I create too many redundant cases. For runtime reasons, I would like to reduce these cases in advance and not delete them afterwards with frozensets, for example.
from itertools import combinations
import numpy as np
def get_partitions(liste,min,max=None):
if max is None:
# Default setting
max = len(liste)
if len(liste) == min :
# Termination Criterium
yield [liste]
else:
for r in range(np.min([len(liste),max]),min-1,-1):
# max for avoiding cases like: [[1,2,3,4],[2,6]] and [[2,6],[1,2,3,4]]
for perm in combinations(liste,r):
rest = [i for i in liste if i not in perm]
if len(rest) >= min:
for recurse in get_partitions(rest,min,r):
yield [list(perm)] + list(recurse)
if len(rest) == 0:
# r == len(liste)
yield [list(perm)]
This leads to:
[[[1, 2, 3, 4]],
[[1, 2], [3, 4]],
[[1, 3], [2, 4]],
[[1, 4], [2, 3]],
[[2, 3], [1, 4]],
[[2, 4], [1, 3]],
[[3, 4], [1, 2]]]
Thanks in advance for any help.
Trying to use @mozway 's answer and extending it to a recursive version lead me to:
def get_partitions(iterable, minl=2):
s = set(iterable)
for r in range(minl, len(s)//2+1):
if len(s)//2 != r:
for c in combinations(s, r):
for recurse in get_partitions(list(s.difference(c)), minl):
yield [list(c),*recurse]
else:
for c in islice(combinations(s, r), comb(len(s),r)//2):
for recurse in get_partitions(list(s.difference(c)), minl):
yield [list(c),*recurse]
yield [list(s)]
For the example list = [1,2,3,4], x=1 it is reducing the number of possibilities from 47 (my initial try) to 19. Still, there are plenty of redundant cases.
[[[1], [2], [3], [4]], <----
[[1], [2], [3, 4]],
[[1], [2, 3, 4]],
[[2], [1], [3], [4]], <----
[[2], [1], [3, 4]],
[[2], [1, 3, 4]],
[[3], [1], [2], [4]], <----
[[3], [1], [2, 4]],
[[3], [1, 2, 4]],
[[4], [1], [2], [3]], <----
[[4], [1], [2, 3]],
[[4], [1, 2, 3]],
[[1, 2], [3], [4]],
[[1, 2], [3, 4]],
[[1, 3], [2], [4]],
[[1, 3], [2, 4]],
[[1, 4], [2], [3]],
[[1, 4], [2, 3]],
[[1, 2, 3, 4]]]
Here is one long-ish solution. No rejection is used in generating partitions, so in that sense this may be somewhat efficient. Still, there are lots of things to optimize.
Example:
Here is an outline of how this works:
splitfunction that takes a listlstand an integernand return all ways to split the list into two groups, one of sizenand another of sizelen(lst) - n.lstintongroups each of sizek. Of course, this is only possible whenlen(lst) = n * k. This is implemented inget_partitions_same_sizefunction. The idea is to always include the first element oflstin the first group and recurse.len(lst). I copied code from this thread.poflen(lst), we need to find all ways to partitionlstaccording top.len(lst) == 7andp = 3 + 2 + 2. In this case, we can choose any three elements for the first group, any remaining two for the second group, and there is no choice to be made for the final third group.pcorresponds top_scheme = [(3, 1), (2, 2)]. The functionget_partitions_helpertakes in a listlstand a "partition scheme"p_scheme, and returns all corresponding partitions without double-counting. This is whereget_partitions_same_sizefrom step two is used.get_partitions: we loop over integer partitions oflen(lst)and return all possible list partitions corresponding to each possible integer partition.This is a fun problem and comments on bugs and optimizations are very welcome.