我發現了一個簡單的遞歸解決方案。
首先,我們來解決一個更簡單的問題:如何找到由兩部分組成的所有分區。對於一個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());
}
}
}
}
我不能說,我也碰到過這個問題,但和一些搜索也沒有提供足夠的答案,+1。乍一看,代碼看起來也不錯(絕對比我接觸到的任何東西都更接近這個意圖),來自我的+1。 –