[英]Find all unordered partitions of a list with a minimum number of elements
給出了一個元素列表。 我想有所有的可能性把這個列表分成任意數量的分區,這樣每個分區至少有 x 個元素。 列表中分區的順序和分區中元素的順序無關緊要。 例如:List = [1,2,3,4] get_partitions(list,2) 應該返回:
[[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) 應該返回:
[[1,2,3,4],
[[1,2],[3,4]],
[[1,3],[2,4]],
[[1,4],[2,3]],
[[1],[2],[3,4],
...]
我已經開始在Python中遞歸實現這個,但是我創建了太多冗余案例。 出於運行時的原因,我想提前減少這些情況,而不是事后使用 frozensets 刪除它們,例如。
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)]
這將導致:
[[[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]]]
在此先感謝您的幫助。
嘗試使用@mozway 的答案並將其擴展為遞歸版本導致我:
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)]
對於示例 list = [1,2,3,4], x=1 ,它將可能性的數量從 47(我最初的嘗試)減少到 19。不過,仍然有很多多余的情況。
[[[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]]]
這是一個很長的解決方案。 在生成分區時沒有使用拒絕,所以從這個意義上說,這可能有點有效。 不過,還有很多東西需要優化。
例子:
list(get_partitions(range(3), 1))
# [[[0, 1, 2]], [[0], [1, 2]], [[1], [0, 2]], [[2], [0, 1]], [[0], [1], [2]]]
以下是其工作原理的概述:
split
function 是很有用的,它接受一個列表lst
和一個 integer n
並返回將列表分成兩組的所有方法,一組大小為n
,另一組大小為len(lst) - n
。lst
分成n
組,每組大小為k
。 當然,這只有在len(lst) = n * k
時才有可能。 這是在get_partitions_same_size
function中實現的,思路是總是把lst
的第一個元素包含在第一組中,然后遞歸。len(lst)
的所有integer 個分區。 我從這個線程復制了代碼。len(lst)
的每個 integer 分區p
,我們需要找到所有根據p
分區lst
的方法。
len(lst) == 7
和p = 3 + 2 + 2
。 在這種情況下,我們可以為第一組選擇任意三個元素,為第二組選擇剩余的任意兩個元素,最后的第三組沒有選擇。p
對應於p_scheme = [(3, 1), (2, 2)]
。 function get_partitions_helper
接受一個列表lst
和一個“分區方案” p_scheme
,並返回所有相應的分區而不重復計算。 這是使用第二步中的get_partitions_same_size
的地方。get_partitions
中:我們遍歷len(lst)
的 integer 個分區,並返回對應於每個可能的 integer 分區的所有可能列表分區。這是一個有趣的問題,非常歡迎對錯誤和優化提出意見。
from itertools import combinations
from collections import Counter
# from this thread:
# https://stackoverflow.com/questions/10035752/elegant-python-code-for-integer-partitioning
def partitions(n, I=1):
yield (n,)
for i in range(I, n//2 + 1):
for p in partitions(n-i, i):
yield (i,) + p
def split(lst, n):
'''
return all ways to split lst into two groups,
with n and len(lst) - n elements respectively
'''
assert len(lst) >= n
# handle special case of len(lst) == 2 * n
if len(lst) == 2 * n:
for first, second in split(lst[1:], n-1):
yield [lst[0], *first], second
else:
for comb in combinations(range(len(lst)), n):
comb = set(comb)
first = [x for i, x in enumerate(lst) if i in comb]
second = [x for i, x in enumerate(lst) if i not in comb]
yield first, second
def get_partitions_same_size(lst, n, k):
# print(lst, n, k)
'return all ways to partition lst into n parts each of size k up to order'
if len(lst) != n * k:
print(lst, n, k)
assert len(lst) == n * k
if n == 0 and len(lst) == 0:
yield []
# case when group size is one
elif k == 1:
yield [[x] for x in lst]
# otherwise, without loss, the first element of lst goes into the first group
else:
for first, rest in split(lst[1:], k-1):
for rec_call in get_partitions_same_size(rest, n-1, k):
yield [[lst[0], *first], *rec_call]
def get_partitions_helper(lst, p_scheme):
"""
return all ways to partition lst into groups according to a partition scheme p_scheme
p_scheme describes an integer partition of len(lst)
for example, if len(lst) == 5, then possible integer partitions are:
[(5,), (1, 4), (1, 1, 3), (1, 1, 1, 2), (1, 1, 1, 1, 1), (1, 2, 2), (2, 3)]
for each, we count the number of groups of a given size
the corresponding partition schemes are:
[[(5, 1)],
[(1, 1), (4, 1)],
[(1, 2), (3, 1)],
[(1, 3), (2, 1)],
[(1, 5)],
[(1, 1), (2, 2)],
[(2, 1), (3, 1)]]
"""
if not lst and not p_scheme:
yield []
return
assert len(lst) == sum(a * b for a, b in p_scheme)
group_size, group_count = p_scheme[0]
num_elts = group_size * group_count
for first, second in split(lst, num_elts):
for firsts in get_partitions_same_size(first, group_count, group_size):
for seconds in get_partitions_helper(second, p_scheme[1:]):
yield [*firsts, *seconds]
def get_partitions(lst, min_):
"""
get all partitions of lst into groups s.t. each group has at least min_ elements
up to order (of groups and elements within a group)
"""
for partition in partitions(len(lst), min_):
p_scheme = list(Counter(partition).items())
yield from get_partitions_helper(lst, p_scheme)
這看起來像是帶有補碼的部分冪集。
您不需要定義最大值,設置最小值后它就固定了。
此外,包括完整集是一種特殊情況(從技術上講,完整集是集合 + 空集,因此它違反了最小條件)
要限制組合的數量,只需在總長度的前半部分停止即可。 如果您有一個偶數輸入並且正在選擇一半分區,則只計算一半的組合(使用itertools.islice
):
你可以使用:
from itertools import combinations
from math import comb
def get_partitions(iterable, minl=2):
s = set(iterable)
return [list(s)]+\
[[list(c), list(s.difference(c))]
for r in range(minl, len(s)//2+1)
for c in ( combinations(s, r) if len(s)//2 != r else
islice(combinations(s, r), comb(len(s),r)//2))
]
out = get_partitions([1,2,3,4])
output:
[[1, 2, 3, 4],
[[1, 2], [3, 4]],
[[1, 3], [2, 4]],
[[1, 4], [2, 3]]]
其他例子:
>>> get_partitions([1,2,3,4,5,6], 1)
[[1, 2, 3, 4, 5, 6],
[[1], [2, 3, 4, 5, 6]],
[[2], [1, 3, 4, 5, 6]],
[[3], [1, 2, 4, 5, 6]],
[[4], [1, 2, 3, 5, 6]],
[[5], [1, 2, 3, 4, 6]],
[[6], [1, 2, 3, 4, 5]],
[[1, 2], [3, 4, 5, 6]],
[[1, 3], [2, 4, 5, 6]],
[[1, 4], [2, 3, 5, 6]],
[[1, 5], [2, 3, 4, 6]],
[[1, 6], [2, 3, 4, 5]],
[[2, 3], [1, 4, 5, 6]],
[[2, 4], [1, 3, 5, 6]],
[[2, 5], [1, 3, 4, 6]],
[[2, 6], [1, 3, 4, 5]],
[[3, 4], [1, 2, 5, 6]],
[[3, 5], [1, 2, 4, 6]],
[[3, 6], [1, 2, 4, 5]],
[[4, 5], [1, 2, 3, 6]],
[[4, 6], [1, 2, 3, 5]],
[[5, 6], [1, 2, 3, 4]],
[[1, 2, 3], [4, 5, 6]],
[[1, 2, 4], [3, 5, 6]],
[[1, 2, 5], [3, 4, 6]],
[[1, 2, 6], [3, 4, 5]],
[[1, 3, 4], [2, 5, 6]],
[[1, 3, 5], [2, 4, 6]],
[[1, 3, 6], [2, 4, 5]],
[[1, 4, 5], [2, 3, 6]],
[[1, 4, 6], [2, 3, 5]],
[[1, 5, 6], [2, 3, 4]]]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.