这是一个算法问题。如果我错过Python中任何有用的现有功能,请大声呼喊。
给定s
个n
元素,我们可以在Python中使用itertools.combinations()
函数来查找所有唯一的 k元素子集。让我们调用包含所有这些子集S
的集合。请注意,每个此类子集都有k
个不同的元素。
问题是两步。首先,给定这些 k-distinct-element 子集,我想组合(某些)它们(组合只是某些子集的超集):
合成中任何两个子集之间的交集为空
合成中所有子集之间的并集完全给出了原始集合s
其次,我想找到以下的作品:
不要共享任何子集
他们的联合提供了S
,即所有k
- 元素子集的集合
作为一个具体的例子,考虑原始集合s = {a, b, c, d}
和k = 2
,然后我们将有以下三个组合/超集:
{{a, b}, {c, d}}, {{a, c}, {b, d}}, {{a, d}, {b, c}}
显然,s
的大小可能很大且k >= 2
,因此这里需要一个有效的(尤其是速度)算法。
P.S。我用2个步骤来表达问题,但很可能是一个有效的算法从不同的角度解决问题。
答案 0 :(得分:3)
我实施了用于证明Baranyai's theorem的积分最大流量结构。您最喜欢的教科书中的更多细节,涵盖完整超图的因素。
from collections import defaultdict
from fractions import Fraction
from math import factorial
from operator import itemgetter
def binomial(n, k):
return factorial(n) // (factorial(k) * factorial(n - k))
def find_path(graph, s, t):
stack = [s]
predecessor = {s: t}
while stack:
v = stack.pop()
for u in graph[v]:
if u not in predecessor:
stack.append(u)
predecessor[u] = v
assert t in predecessor
path = [t]
while path[-1] != s:
path.append(predecessor[path[-1]])
path.reverse()
return path
def round_flow(flow):
while True:
capacities = []
for (u, v), x in flow.items():
z = x - x.numerator // x.denominator
if z:
capacities.append(((v, u), z))
capacities.append(((u, v), 1 - z))
if not capacities:
break
(t, s), delta = min(capacities, key=itemgetter(1))
graph = defaultdict(list)
for (v, u), z in capacities:
if (v, u) not in [(s, t), (t, s)]:
graph[v].append(u)
path = find_path(graph, s, t)
for i, v in enumerate(path):
u = path[i - 1]
if (u, v) in flow:
flow[(u, v)] += delta
else:
flow[(v, u)] -= delta
def baranyai(n, k):
m, r = divmod(n, k)
assert not r
M = binomial(n - 1, k - 1)
partition = [[()] * m for i in range(M)]
for l in range(n):
flow = defaultdict(Fraction)
for i, A_i in enumerate(partition):
for S in A_i:
flow[(i, S)] += Fraction(k - len(S), n - l)
round_flow(flow)
next_partition = []
for i, A_i in enumerate(partition):
next_A_i = []
for S in A_i:
if flow[(i, S)]:
next_A_i.append(S + (l,))
flow[(i, S)] -= 1
else:
next_A_i.append(S)
next_partition.append(next_A_i)
partition = next_partition
assert len(partition) == M
classes = set()
for A in partition:
assert len(A) == m
assert all(len(S) == k for S in A)
assert len({x for S in A for x in S}) == n
classes.update(map(frozenset, A))
assert len(classes) == binomial(n, k)
return partition
if __name__ == '__main__':
print(baranyai(9, 3))
print(baranyai(20, 2))