生成带有限制条件的所有集合划分。

huangapple go评论61阅读模式
英文:

Generate all set partitions with restrictions

问题

给定 N 个大小大于 0 的数组。生成具有限制条件的所有集合分区。

限制条件定义为禁止组合来自同一数组的元素。

示例:

// 给定数组
A = [A1, A2, A3]
B = [B1, B2]
C = [C1, C2]

允许的分区:

{[A1, B1], [B2], [A2, C1], [A3, C2]}

禁止的分区:

{[A1, B1, B2], [A2, C1], [A3, C2]} // 不能将来自同一数组的 B1、B2 分组
英文:

Given N arrays with sizes > 0. Generate all set partitions with restrictions.

Restrictions are defined as prohibiting combinations of elements from the same array.

Example:

// Given arrays
A = [A1, A2, A3]
B = [B1, B2]
C = [C1, C2]

Allowed partition:

{[A1,B1],[B2],[A2,C1],[A3,C2]}

Prohibited partition:

{[A1, B1, B2],[A2,C1],[A3,C2]} // B1, B2 from same array can't be grouped

答案1

得分: 1

# [(('A2',), ('A3',), ('A1',), ('B2',), ('C1',), ('B1',), ('C2',)),
#  (('A2', 'B2'), ('A3',), ('A1',), ('C1',), ('B1',), ('C2',)),
#  (('A2', 'C1'), ('A3',), ('A1',), ('B2',), ('B1',), ('C2',)),
#  (('A2', 'B1'), ('A3',), ('A1',), ('B2',), ('C1',), ('C2',)),
#  (('A2', 'C2'), ('A3',), ('A1',), ('B2',), ('C1',), ('B1',)),
#  (('A3', 'B2'), ('A2',), ('A1',), ('C1',), ('B1',), ('C2',)),
#  (('A3', 'C1'), ('A2',), ('A1',), ('B2',), ('B1',), ('C2',)),
# ...
#  (('A2', 'C1'), ('A3', 'C2'), ('A1', 'B1'), ('B2',)),  # <----- 你提供的特定示例
#  (('A2', 'B1'), ('A3', 'B2'), ('A1', 'C1'), ('C2',)),
#  (('A2', 'B1'), ('A3', 'B2'), ('A1', 'C2'), ('C1',)),
#  (('A2', 'B1'), ('A3', 'C1'), ('A1', 'B2'), ('C2',)),
#  (('A2', 'B1'), ('A3', 'C1'), ('A1', 'C2'), ('B2',)),
# ... 
#  (('A2', 'B1', 'C1'), ('A1', 'B2', 'C2'), ('A3',)),
#  (('A3', 'B2', 'C2'), ('A1', 'B1', 'C1'), ('A2',)),
#  (('A3', 'B1', 'C1'), ('A1', 'B2', 'C2'), ('A2',)),
#  (('A2',), ('A3',), ('A1',), ('B1', 'C2'), ('B2', 'C1')),
#  (('A2', 'B1', 'C2'), ('A3',), ('A1',), ('B2', 'C1')),
#  (('A2', 'B2', 'C1'), ('A3',), ('A1',), ('B1', 'C2')),
# ...
#  (('A3', 'B1', 'C2'), ('A1', 'B2', 'C1'), ('A2',))]

英文:

Combining the combinatorial tools from python's itertools module:

from itertools import chain, combinations, permutations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def partitions2(A, B):
    A, B = map(set, (A, B))
    for A_paired in powerset(A):
        A_alone = A.difference(A_paired)
        for B_paired in permutations(B, len(A_paired)):
            B_alone = B.difference(B_paired)
            yield tuple(chain(zip(A_paired, B_paired), zip(A_alone), zip(B_alone)))

print( list(partitions2('ab', (0,1))) )
# [(('b',), ('a',), (0,), (1,)),
#  (('b', 0), ('a',), (1,)),
#  (('b', 1), ('a',), (0,)),
#  (('a', 0), ('b',), (1,)),
#  (('a', 1), ('b',), (0,)),
#  (('b', 0), ('a', 1)),
#  (('b', 1), ('a', 0))]

def partitions(*iterables):
    if len(iterables) == 0:
        yield ((),)
    elif len(iterables) == 1:
        yield tuple(zip(iterables[0]))
    elif len(iterables) == 2:
        yield from partitions2(*iterables)
    else:
        A, *BCD = iterables
        A = set(A)
        for P in map(set,partitions(*BCD)):
            for A_paired in powerset(A):
                A_alone = A.difference(A_paired)
                for P_paired in permutations(P, len(A_paired)):
                    P_alone = P.difference(P_paired)
                    yield tuple(chain(((a, *p) for a,p in zip(A_paired, P_paired)), zip(A_alone), P_alone))

print( list(partitions('x', 'a', (0,1))) )
# [(('x',), (0,), (1,), ('a',)),
#  (('x', 0), (1,), ('a',)),
#  (('x', 1), (0,), ('a',)),
#  (('x', 'a'), (0,), (1,)),
#  (('x',), (1,), ('a', 0)),
#  (('x', 1), ('a', 0)),
#  (('x', 'a', 0), (1,)),
#  (('x',), (0,), ('a', 1)),
#  (('x', 0), ('a', 1)),
#  (('x', 'a', 1), (0,))]

The number of partitions explodes quite fast. Here is the specific example you asked for:


print( list(partitions(('A1', 'A2', 'A3'), ('B1', 'B2'), ('C1', 'C2'))) )
# [(('A2',), ('A3',), ('A1',), ('B2',), ('C1',), ('B1',), ('C2',)),
#  (('A2', 'B2'), ('A3',), ('A1',), ('C1',), ('B1',), ('C2',)),
#  (('A2', 'C1'), ('A3',), ('A1',), ('B2',), ('B1',), ('C2',)),
#  (('A2', 'B1'), ('A3',), ('A1',), ('B2',), ('C1',), ('C2',)),
#  (('A2', 'C2'), ('A3',), ('A1',), ('B2',), ('C1',), ('B1',)),
#  (('A3', 'B2'), ('A2',), ('A1',), ('C1',), ('B1',), ('C2',)),
#  (('A3', 'C1'), ('A2',), ('A1',), ('B2',), ('B1',), ('C2',)),
# ...
#  (('A2', 'C1'), ('A3', 'C2'), ('A1', 'B1'), ('B2',)),  # <----- THE EXAMPLE YOU GAVE
#  (('A2', 'B1'), ('A3', 'B2'), ('A1', 'C1'), ('C2',)),
#  (('A2', 'B1'), ('A3', 'B2'), ('A1', 'C2'), ('C1',)),
#  (('A2', 'B1'), ('A3', 'C1'), ('A1', 'B2'), ('C2',)),
#  (('A2', 'B1'), ('A3', 'C1'), ('A1', 'C2'), ('B2',)),
# ... 
#  (('A2', 'B1', 'C1'), ('A1', 'B2', 'C2'), ('A3',)),
#  (('A3', 'B2', 'C2'), ('A1', 'B1', 'C1'), ('A2',)),
#  (('A3', 'B1', 'C1'), ('A1', 'B2', 'C2'), ('A2',)),
#  (('A2',), ('A3',), ('A1',), ('B1', 'C2'), ('B2', 'C1')),
#  (('A2', 'B1', 'C2'), ('A3',), ('A1',), ('B2', 'C1')),
#  (('A2', 'B2', 'C1'), ('A3',), ('A1',), ('B1', 'C2')),
# ...
#  (('A3', 'B1', 'C2'), ('A1', 'B2', 'C1'), ('A2',)),
#  (('A3', 'B2', 'C1'), ('A1', 'B1', 'C2'), ('A2',))]

答案2

得分: 1

我们可以获得不同数组的幂集,以获取所有可能的数组组合,然后取这些数组的笛卡尔积,形成实际的结果集。

使用python itertools文档中的powerset实现:

from itertools import chain, combinations, product

def powerset(iterable):
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def solve(*arrays):
    for subset in powerset(arrays):
        yield from product(*subset)

示例:

>>> list(solve(["a1", "a2", "a3"], ["b1", "b2"], ["c1", "c2"]))
[(), ('a1',), ('a2',), ('a3',), ('b1',), ('b2',), ('c1',), ('c2',), ('a1', 'b1'), ('a1', 'b2'), ('a2', 'b1'), ('a2', 'b2'), ('a3', 'b1'), ('a3', 'b2'), ('a1', 'c1'), ('a1', 'c2'), ('a2', 'c1'), ('a2', 'c2'), ('a3', 'c1'), ('a3', 'c2'), ('b1', 'c1'), ('b1', 'c2'), ('b2', 'c1'), ('b2', 'c2'), ('a1', 'b1', 'c1'), ('a1', 'b1', 'c2'), ('a1', 'b2', 'c1'), ('a1', 'b2', 'c2'), ('a2', 'b1', 'c1'), ('a2', 'b1', 'c2'), ('a2', 'b2', 'c1'), ('a2', 'b2', 'c2'), ('a3', 'b1', 'c1'), ('a3', 'b1', 'c2'), ('a3', 'b2', 'c1'), ('a3', 'b2', 'c2')]

当然,如果你愿意,你可以跳过空集()

计数更新

如果你只想要“分区”的计数,那么解决方案会简单得多。当然,你仍然可以使用上面的生成器,将其转换为列表,然后获取其长度,但这将更快:

from math import prod

def count(*arrays):
    return prod(len(array)+1 for array in arrays)

注意,math.prod 需要 Python 3.8+。

对于示例:

>>> count(["a1", "a2", "a3"], ["b1", "b2"], ["c1", "c2"])
36
英文:

We can get the powerset of the different arrays, to get all possible combinations of arrays, and then take the cartesian product of those arrays, to form the actual resulting sets.

Taking the implementation of powerset from the python itertools docs:

from itertools import chain, combinations, product

def powerset(iterable):
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def solve(*arrays):
    for subset in powerset(arrays):
        yield from product(*subset)

Example:

>>> list(solve(["a1", "a2", "a3"], ["b1", "b2"], ["c1", "c2"]))
[(), ('a1',), ('a2',), ('a3',), ('b1',), ('b2',), ('c1',), ('c2',), ('a1', 'b1'), ('a1', 'b2'), ('a2', 'b1'), ('a2', 'b2'), ('a3', 'b1'), ('a3', 'b2'), ('a1', 'c1'), ('a1', 'c2'), ('a2', 'c1'), ('a2', 'c2'), ('a3', 'c1'), ('a3', 'c2'), ('b1', 'c1'), ('b1', 'c2'), ('b2', 'c1'), ('b2', 'c2'), ('a1', 'b1', 'c1'), ('a1', 'b1', 'c2'), ('a1', 'b2', 'c1'), ('a1', 'b2', 'c2'), ('a2', 'b1', 'c1'), ('a2', 'b1', 'c2'), ('a2', 'b2', 'c1'), ('a2', 'b2', 'c2'), ('a3', 'b1', 'c1'), ('a3', 'b1', 'c2'), ('a3', 'b2', 'c1'), ('a3', 'b2', 'c2')]

You can of course skip the empty set () if you like.

Counting Update

If you just want the count of "partitions", then the solution is much easier. You could still of course use the above generator, convert it to a list, and get its len, but this will be much faster:

from math import prod

def count(*arrays):
    return prod(len(array)+1 for array in arrays)

Note that math.prod requires python 3.8+

For the example:

>>> count(["a1", "a2", "a3"], ["b1", "b2"], ["c1", "c2"])
36

huangapple
  • 本文由 发表于 2023年2月24日 02:16:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75548792.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定