【问题标题】:rebuilding sets and intersection in python在python中重建集合和交集
【发布时间】:2020-06-06 15:52:38
【问题描述】:

我有以下问题,一开始我有3套:

A [1,2,3],B [2,3,4,5] and C [2,5,6,7] 

接下来我会考虑两个两个集合的交集以及所有集合的交集

AB [2,3],
AC [2],
BC [2,5] and
ABC [2] (Full intersection)

现在我想要的是在以下条件下对我的集合进行新的重新排序: 1. 保持每组的基数。 2. 保留所有可能的交叉点的基数。

例如我应该得到

A [3,4,7],
B [1,3,7,5] and
C [2,6,5,7]

请注意,A 和 B 的新交集(现在 [3,7])与前一个交集一样有 2 个元素,类似于交集 AC 、BC 和完全交集 ABC,当然还有 A、B 和C 继续分别为 3、4 和 4。 最后,我需要尽可能多地进行重组,这取决于集合的基数和集合的总数。

【问题讨论】:

  • 这是一个算法问题,应该为此进行标记。另外,到目前为止,您尝试过什么?
  • 事实上问题更大,我提出的解决方案就是我描述的那个。找到所有可能的交叉点是我的想法,现在构建新组是我无法实现的部分
  • 为什么不生成一个包含 A、B 和 C 元素的列表,计算所有可能的排列,如果所有检查都通过,将列表分割成初始长度集?
  • 生成所有元素的列表并尝试从中构建集合,但不能保持交集的基数与交集中的元素大于初始值或有时为零

标签: python set intersection rebuild preserve


【解决方案1】:

您可以只生成原始元素集的所有可能排列,并将它们用作从原始元素到新配置的“映射”。例如,2345671 将每个数字映射到下一个数字并环绕;这将创建集合:

A = {2, 3, 4}    # from {1, 2, 3}
B = {3, 4, 5, 6} # from {2, 3, 4, 5}
C = {3, 6, 7, 1} # from {2, 5, 6, 7} 

使用itertools 非常简单:

from itertools import permutations

def all_configurations(*sets):
    elements = list(set.union(*sets))
    for perm in permutations(elements):
        map = {old: new for old, new in zip(elements, perm)}
        new_sets = [{map[k] for k in old_set} for old_set in sets]
        yield new_sets


A = {1, 2, 3}
B = {2, 3, 4, 5}
C = {2, 5, 6, 7} 

confs = all_configurations(A, B, C)
for conf in confs:
    print(conf)
    # Or: Ax, Bx, Cx = conf

请注意我是如何使用yield 语句的,这将一次生成每个新排列,而不是一次创建它们,因此您可以将其用于大量元素而不会占用内存。此外,所编写的函数适用于任意数量的输入集。

当然,这肯定会生成一些重复项(例如,在您的示例中,映射 6 到 7 和 7 到 6 不会改变任何内容)但它肯定也应该生成每个有效选项。一些示例输出:

[{2, 4, 6}, {3, 4, 5, 6}, {1, 3, 6, 7}]
[{4, 5, 7}, {1, 3, 4, 7}, {2, 3, 6, 7}]
[{3, 5, 6}, {1, 3, 5, 7}, {2, 3, 4, 7}]
[{1, 6, 7}, {1, 2, 4, 7}, {2, 3, 5, 7}]

编辑:为了获得固定数量的非重复排列,您可以更改原始代码以返回 tuplefrozensets 而不是集合列表,这样整个事情可以散列,所以你只能得到唯一的。然后,您可以将内容添加到输出集中,直到达到您想要的基数:

from itertools import permutations

def all_configurations(*sets):
    elements = list(set.union(*sets))
    for perm in permutations(elements):
        map = {old: new for old, new in zip(elements, perm)}
        new_sets = tuple(frozenset(map[k] for k in old_set) for old_set in sets)
        yield new_sets

def n_configurations(n, *sets):
    output = set()
    confs = all_configurations(*sets)
    for conf in confs:
        output.add(conf)
        if len(output) >= n:
            break
    return output


A = {1, 2, 3}
B = {2, 3, 4, 5}
C = {2, 5, 6, 7} 

confs = n_configurations(10, A, B, C)
for a, b, c in confs:
    print(a, b, c)

这会产生以下 10 种配置:

(frozenset([1, 2, 3]), frozenset([2, 3, 5, 6]), frozenset([2, 4, 6, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 4, 6]), frozenset([2, 5, 6, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 6, 7]), frozenset([2, 4, 5, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 4, 6]), frozenset([2, 4, 5, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 4, 5]), frozenset([2, 4, 6, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 5, 6]), frozenset([2, 4, 5, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 4, 7]), frozenset([2, 5, 6, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 4, 5]), frozenset([2, 5, 6, 7]))
(frozenset([1, 2, 3]), frozenset([2, 3, 4, 7]), frozenset([2, 4, 5, 6]))
(frozenset([1, 2, 3]), frozenset([2, 3, 5, 7]), frozenset([2, 4, 6, 7]))

【讨论】:

  • 这个提议对我来说很棒!仅在我的真实案例中,我有大约 50 个组,每个组中大约有 100 到 300 个元素。当我应用您的想法时,如果但取决于组的数量和规模,这可能是一个漫长的过程,那么它是否有效,有什么方法可以改善它吗?
  • 我怎样才能修改它的代码,这样它就不会给我所有可能的排列,而是一个固定的数字。在示例中,我们有大约 5040 个新组(您向我展示了 4 个),例如,我希望能够获得 200 个新组或少于总数的任何数量,并且清楚地显示这些组。谢谢!
  • @Herberth 您可以制作一组输出集,并在它拥有任意数量的元素后停止向其中添加内容。请参阅我编辑的答案。
  • 太棒了!直到现在我才意识到,例如在 10 种配置中,您显示的第一组 {1,2,3} 在所有情况下都是相同的(我知道通常每一行都是新配置,我同意)但我想要一些随机性。可以随机选择 N 个排列,因此显示 N 个对应的配置?据我所知,到目前为止,我们正在采用前 n 个排列生成的前 n 个配置。
  • 例如,在展示给我的第一个配置中,每个配置的第一组都有一定的随机性。 [{2, 4, 6}, {3, 4, 5, 6}, {1, 3, 6, 7}] [{4, 5, 7}, {1, 3, 4, 7}, {2 , 3, 6, 7}] [{3, 5, 6}, {1, 3, 5, 7}, {2, 3, 4, 7}] [{1, 6, 7}, {1, 2 , 4, 7}, {2, 3, 5, 7}]
【解决方案2】:

这能让你开始吗?

    import random
    import itertools


    def same_intersect_len(x, y, z):
        return (len(x & y) == len(a & b) and
                len(x & z) == len(a & c) and
                len(z & y) == len(b & c) and
                len(x & y & z) == len(a & b & c))


    a = set(random.sample(range(0, 10), 1))
    b = set(random.sample(range(0, 10), 2))
    c = set(random.sample(range(0, 10), 3))

    elements = list(a) + list(b) + list(c)

    ps = set(itertools.permutations(elements))
    new_a = list()
    new_b = list()
    new_c = list()
    for p in ps:
        candidate_a = {p[len(a) - 1]} - a
        candidate_b = set(p[len(a):len(a) + len(b) - 1]) - b
        candidate_c = set(p[len(a) + len(b):len(a) + len(b) + len(c) - 1]) - c
        if same_intersect_len(
                candidate_a,
                candidate_b,
                candidate_c):
            new_a.append(candidate_a)
            new_b.append(candidate_b)
            new_c.append(candidate_c)

【讨论】:

  • 当然!现在对于一般情况,我的意思是 n 个组,每个包含 m 个元素的组将是下一步
猜你喜欢
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多