我有一组不同的值。我正在寻找一种方法来生成此集合的所有分区,即将集合划分为子集的所有可能方法。
例如,集合{1, 2, 3}具有以下分区:

{ {1}, {2}, {3} },
{ {1, 2}, {3} },
{ {1, 3}, {2} },
{ {1}, {2, 3} },
{ {1, 2, 3} }.

因为这些是数学意义上的集合,所以顺序是不相关的。例如,{1, 2}, {3}{3}, {2, 1}相同,不应是单独的结果。
可以在Wikipedia上找到集合分区的完整定义。

最佳答案

我找到了一个简单的递归解决方案。
首先,让我们解决一个更简单的问题:如何找到正好由两个部分组成的所有分区。对于n元素集,我们可以计算从0到(2^n)-1的整数。这将创建每个n位模式,每个位对应一个输入元素。如果位为0,则将元素放在第一部分;如果位为1,则将元素放在第二部分。这留下了一个问题:对于每个分区,我们将得到两个部分交换的重复结果。为了解决这个问题,我们总是将第一个元素放在第一个部分中。然后我们只通过从0到(2^(n-1))-1的计数来分配剩余的n-1元素。
现在我们可以将一个集合分成两部分,我们可以编写一个递归函数来解决剩下的问题函数从原始集合开始,查找所有两部分的分区对于这些分区中的每一个,它递归地找到将第二部分划分为两部分的所有方法,从而生成所有三部分的分区然后,它将这些分区的最后一部分进行划分,以生成所有由四部分组成的分区,依此类推。
下面是C_中的一个实现。打电话

Partitioning.GetAllPartitions(new[] { 1, 2, 3, 4 })

产量
{ {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} }.

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());
            }
        }
    }
}

09-25 20:25
查看更多