set partitions
combinatorics
discrete mathematics
mathematics tutorial
problem solving

How to find all partitions of a set

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

A partition of a set splits elements into non-empty, disjoint blocks whose union is the original set. Generating all partitions is useful in combinatorics, clustering search, and exhaustive reasoning tasks. The challenge is that partition counts grow very quickly, so implementation must be correct and output-aware.

What A Set Partition Means

For a set S, a partition is a collection of subsets where:

  • no subset is empty,
  • subsets do not overlap,
  • every element in S appears in exactly one subset.

For example, partitions of 1, 2, 3 are:

  • '1, 2, 3'
  • '1, 2 with 3'
  • '1, 3 with 2'
  • '2, 3 with 1'
  • '1 with 2 with 3'

There are 5 total, matching Bell number B3.

Recursive Construction Strategy

A reliable recursive strategy processes one element at a time.

For new element x:

  1. insert x into each existing block of every smaller partition,
  2. also create a new singleton block containing only x.

This guarantees complete enumeration without logical duplicates when list copying is handled correctly.

Python Generator Implementation

python
1from typing import Any, Iterable, List
2
3
4def partitions(items: List[Any]):
5    if not items:
6        yield []
7        return
8
9    first = items[0]
10    for smaller in partitions(items[1:]):
11        # Put first into each existing block
12        for i in range(len(smaller)):
13            candidate = [block[:] for block in smaller]
14            candidate[i].append(first)
15            yield candidate
16
17        # Put first into a new block
18        yield [[first]] + [block[:] for block in smaller]
19
20
21if __name__ == "__main__":
22    data = [1, 2, 3]
23    all_parts = list(partitions(data))
24    for p in all_parts:
25        print(p)
26    print("count:", len(all_parts))

This code is easy to adapt for constraint-based filtering.

Why Copying Matters

Without copying lists at each branch, recursive paths share mutable structures and corrupt each other.

If you see duplicate or missing partitions, shared mutation is usually the cause. Clone blocks before append operations.

Add Constraints To Reduce Search Space

Real applications rarely need all partitions. Add filters early.

python
1def partitions_with_k_blocks(items: List[Any], k: int):
2    for p in partitions(items):
3        if len(p) == k:
4            yield p
5
6print(list(partitions_with_k_blocks([1, 2, 3, 4], 2))[:5])

Constraint pruning keeps runtime manageable for slightly larger sets.

Validate Using Bell Numbers

A simple correctness check compares count to known Bell numbers for small n.

python
1known_bell = [1, 1, 2, 5, 15, 52, 203]
2for n in range(0, 6):
3    vals = list(range(1, n + 1))
4    cnt = sum(1 for _ in partitions(vals))
5    print(n, cnt, known_bell[n])

This is useful as a unit test and regression guard.

Complexity Reality

Enumeration complexity is dominated by output size itself. Bell numbers grow rapidly:

  • 'B5 = 52'
  • 'B6 = 203'
  • 'B7 = 877'
  • 'B8 = 4140'

Full enumeration becomes expensive quickly in time and memory. Use generators and streaming consumers where possible.

Practical Engineering Advice

  • Keep output lazy with generators.
  • Add domain constraints early.
  • Normalize block ordering only when needed for display.
  • Avoid materializing all results unless required.
  • Profile memory, not only CPU.

For large-scale partition-like problems, heuristic or approximate approaches are often more practical than exact enumeration.

Common Pitfalls

  • Mutating shared block lists across recursive branches.
  • Assuming runtime is polynomial and underestimating Bell growth.
  • Materializing all partitions when only count or subset is needed.
  • Forgetting to test against known small Bell values.
  • Confusing combinations with partitions and implementing wrong algorithm.

Summary

  • Set partition generation is a classic recursive branching problem.
  • Insert-each-or-new-block recursion gives complete enumeration.
  • Careful copying is required to avoid mutation bugs.
  • Bell number growth makes exhaustive generation expensive quickly.
  • Use lazy generation and constraints to keep solutions practical.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.