找K-不同的元素子集的独特组合为一组n个元素 [英] Find unique compositions of k-distinct-element subsets for a set of n elements

查看:211
本文介绍了找K-不同的元素子集的独特组合为一组n个元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个算法问题。如果我错过了在Python中的任何现有功能的帮助,请给留言。

This is an algorithmic problem. If I miss any existing function in Python that helps, please give a shout.

鉴于 N 元素的集合取值,我们可以使用 itertools.combinations(在Python)函数找到所有的独特的 K元素的子集的。让我们把包含所有这些子集取值设定。请注意,每个这样的子集具有 K 不同的元素。

Given a set s of n elements, we can use itertools.combinations() function in Python to find all the unique k-element subsets. Let's call the set containing all these subsets S. Notice that each such subset has k distinct elements.

问题是两步骤。首先,考虑到这些的 K-不同的元素的子集,我想组成(部分)他们这样的(组成只是部分子集的一个超集):

The problem is 2-step. First, given these k-distinct-element subsets, I want to compose (some of) them such that (a composition is simply a superset of some subsets):

  1. 组合物中的任何两个子集之间的交集为空

  1. The intersection between any two subsets within a composition is empty

在一个组合物中所有的子集之间的联盟提供了完全原载取值

The union between all the subsets in a composition gives exactly the original set s

二,我想找到的组合物:

Second, I want to find the compositions that:

  1. 不共享任何一个子集

  1. Do not share any subset

他们的结合使完全取值,即集所有的 K k-元子集

Their union gives exactly the S, i.e. the set of all the k-element subsets

由于这里一个具体的例子,考虑原来设置 S = {A,B,C,D} K = 2 ,我们届时将有以下三种成分/超集:

As a concrete example here, consider the original set s = {a, b, c, d} and k = 2, we then will have the following three compositions/supersets:

{{A,B},{C,D}},{{A,C},{B,D}},{{A,D},{B,C}}

显然,取值规模可能很大, K> = 2 ,所以一个有效的(特别是速度)算法,这里需要的。

Obviously, the size of s can be large and k >= 2, so an efficient (especially the speed) algorithm is needed here.

P.S。我短语在2个步骤的问题,但它可能是,一个有效的算法从一个不同的角度攻击问题

P.S. I phrase the problem in 2 steps, but it may well be that an efficient algorithm attacks the problem from a different angle.

推荐答案

我实现了这是我们用来证明的 Baranyai定理。更多细节在你最喜欢的教材​​涵盖了完整的超图的因素。

I implemented the integral maximum flow construction that's used to prove Baranyai's theorem. More details in your favorite textbook covering factors of a complete hypergraph.

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))

这篇关于找K-不同的元素子集的独特组合为一组n个元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆