Generating Partitions of a Set
Dividing a set into distinct subsets, known as partitions, is a common mathematical operation. This article delves into an efficient method for partitioning a set, ensuring no duplicates arise due to the irrelevance of order.
Recursive Approach
Our solution utilizes a recursive strategy, beginning with the simplest scenario: partitioning into exactly two parts. By representing each element as a bit (0 for the first part and 1 for the second), we avoid duplicate results by consistently placing the first element in the first part.
Next, we delve into the recursive function that addresses more complex partitions. The function operates on the original set, finding all two-part partitions. Each partition's second part is recursively partitioned into two parts, yielding three-part partitions. This process continues until the entire set is partitioned.
Implementation
Below is a C# implementation of the partitioning algorithm:
using System; using System.Collections.Generic; using System.Linq; namespace PartitionTest { public static class Partitioning { public static IEnumerable<T[][]> GetAllPartitions<T>(T[] elements) { return GetAllPartitions(new T[][]{}, elements); } private static IEnumerable<T[][]> GetAllPartitions<T>( T[][] fixedParts, T[] suffixElements) { // A trivial partition consists of the fixed parts // followed by all suffix elements as one block yield return fixedParts.Concat(new[] { suffixElements }).ToArray(); // Get all two-group-partitions of the suffix elements // and sub-divide them recursively var suffixPartitions = GetTuplePartitions(suffixElements); foreach (Tuple<T[], T[]> suffixPartition in suffixPartitions) { var subPartitions = GetAllPartitions( fixedParts.Concat(new[] { suffixPartition.Item1 }).ToArray(), suffixPartition.Item2); foreach (var subPartition in subPartitions) { yield return subPartition; } } } private static IEnumerable<Tuple<T[], T[]>> GetTuplePartitions<T>( T[] elements) { // No result if less than 2 elements if (elements.Length < 2) yield break; // Generate all 2-part partitions for (int pattern = 1; pattern < 1 << (elements.Length - 1); pattern++) { // Create the two result sets and // assign the first element to the first set List<T>[] resultSets = { new List<T> { elements[0] }, new List<T>() }; // Distribute the remaining elements for (int index = 1; index < elements.Length; index++) { resultSets[(pattern >> (index - 1)) & 1].Add(elements[index]); } yield return Tuple.Create( resultSets[0].ToArray(), resultSets[1].ToArray()); } } } }
Invoking Partitioning.GetAllPartitions(new[] { 1, 2, 3, 4 }) generates the following partitions:
{ {1, 2, 3, 4} }, { {1, 3, 4}, {2} }, { {1, 2, 4}, {3} }, { {1, 4}, {2, 3} }, { {1, 4}, {2}, {3} }, { {1, 2, 3}, {4} }, { {1, 3}, {2, 4} }, { {1, 3}, {2}, {4} }, { {1, 2}, {3, 4} }, { {1, 2}, {3}, {4} }, { {1}, {2, 3, 4} }, { {1}, {2, 4}, {3} }, { {1}, {2, 3}, {4} }, { {1}, {2}, {3, 4} }, { {1}, {2}, {3}, {4} }.
The above is the detailed content of How to Efficiently Generate All Partitions of a Set Using a Recursive Approach in C#?. For more information, please follow other related articles on the PHP Chinese website!