Jose Ricardo Bustos M.
Jose Ricardo Bustos M.

Reputation: 8174

how find all groups of subsets of set A? Set partitions in Python

I want to find an algorithm that given a set A to find all groups of subsets that satisfy the following condition:

x ∪ y ∪ .... z = A, where x, y, ... z ∈ Group

and

∀ x,y ∈ Group: x ⊆ A, y ⊆ A, x ∩ y = ∅ = {}

and

∀ x ∈ Group: x != ∅

Note: I hope to define it well, I'm not good with math symbols

I made the following approach to search groups of two subsets only:

from itertools import product, combinations

def my_combos(A):
  subsets = []
  for i in xrange(1, len(A)):
    subsets.append(list(combinations(A,i)))
  combos = []
  for i in xrange(1, 1+len(subsets)/2):
    combos.extend(list(product(subsets[i-1], subsets[-i])))
  if not len(A) % 2:
    combos.extend(list(combinations(subsets[len(A)/2-1], 2)))
  return [combo for combo in combos if not set(combo[0]) & set(combo[1])]

my_combos({1,2,3,4})

I get the following output, these are all groups composed of two subsets

[
  ((1,), (2, 3, 4)), 
  ((2,), (1, 3, 4)), 
  ((3,), (1, 2, 4)), 
  ((4,), (1, 2, 3)), 
  ((1, 2), (3, 4)), 
  ((1, 3), (2, 4)), 
  ((1, 4), (2, 3))
]

..... but, groups composed of one, three, four subsets ....

Question:

how may i find a general solution?

for example the following expected output:

my_combos({1,2,3,4})

[
  ((1,2,3,4)),
  ((1,2,3),(4,)),
  ((1,2,4),(3,)),
  ((1,3,4),(2,)),
  ((2,3,4),(1,)),
  ((1,2),(3,4)),
  ((1,3),(2,4)),
  ((1,4),(2,3)),
  ((1,2),(3,),(4,)),
  ((1,3),(2,),(4,)),
  ((1,4),(2,),(3,)),
  ((1,),(2,),(3,4)),
  ((1,),(3,),(2,4)),
  ((1,),(4,),(2,3)),
  ((1,),(4,),(2,),(3,))
]

Upvotes: 3

Views: 1571

Answers (1)

Stefan Pochmann
Stefan Pochmann

Reputation: 28606

Solution:

def partitions(A):
    if not A:
        yield []
    else:
        a, *R = A
        for partition in partitions(R):
            yield partition + [[a]]
            for i, subset in enumerate(partition):
                yield partition[:i] + [subset + [a]] + partition[i+1:]

Explanation:

  • The empty set only has the empty partition.
  • For a non-empty set, take out one element and then for each partition of the remaining elements, add that element as its own subset or add it to one of the partition's subsets.
  • Note that a partition is really a set of sets. I only represent it as list of lists because that's faster and because I don't want to use frozensets, which don't print nicely. Tuples are faster and the question asked for them, but I can't stand the commas for single-element tuples.

Test with output:

for partition in partitions({1, 2, 3, 4}):
    print(partition)

[[4], [3], [2], [1]]
[[4, 1], [3], [2]]
[[4], [3, 1], [2]]
[[4], [3], [2, 1]]
[[4, 2], [3], [1]]
[[4, 2, 1], [3]]
[[4, 2], [3, 1]]
[[4], [3, 2], [1]]
[[4, 1], [3, 2]]
[[4], [3, 2, 1]]
[[4, 3], [2], [1]]
[[4, 3, 1], [2]]
[[4, 3], [2, 1]]
[[4, 3, 2], [1]]
[[4, 3, 2, 1]]

Speed test with output (on a relatively weak laptop):

from time import time
print('elements partitions seconds')
for n in range(14):
    t0 = time()
    number = sum(1 for partition in partitions(range(n)))
    print('{:5}{:10}{:11.2f}'.format(n, number, time() - t0))

elements partitions seconds
    0         1       0.00
    1         1       0.00
    2         2       0.00
    3         5       0.00
    4        15       0.00
    5        52       0.00
    6       203       0.00
    7       877       0.00
    8      4140       0.06
    9     21147       0.07
   10    115975       0.36
   11    678570       2.20
   12   4213597      13.56
   13  27644437      87.59

I confirmed those partition numbers with the OEIS page.

Upvotes: 11

Related Questions