对于以下字符a,b,c,d
,我想找到以下组合。
序列始终排序。我想知道如何找到这些组合?
a b c d ab ac ad bc bd cd abc abd acd bcd abcd
答案 0 :(得分:4)
你想要的是每一个组合。通常在获得组合时,您将获得特定大小的所有组合,n。我们首先创建该方法以从序列中获取大小为n的组合:
public static IEnumerable<IEnumerable<T>> Combinations<T>(
this IEnumerable<T> source, int n)
{
if (n == 0)
yield return Enumerable.Empty<T>();
int count = 1;
foreach (T item in source)
{
foreach (var innerSequence in source.Skip(count).Combinations(n - 1))
{
yield return new T[] { item }.Concat(innerSequence);
}
count++;
}
}
一旦你有了这个简单的问题就是将n的所有n的组合从1加到序列的大小:
public static IEnumerable<IEnumerable<T>> AllCombinations<T>(this IList<T> source)
{
IEnumerable<IEnumerable<T>> output = Enumerable.Empty<IEnumerable<T>>();
for (int i = 0; i < source.Count; i++)
{
output = output.Concat(source.Combinations(i));
}
return output;
}
使用它的一些示例代码:
var list = new List<string> { "a", "b", "c", "d" };
foreach (var sequence in list.AllCombinations())
{
Console.WriteLine(string.Join(" ", sequence));
}
值得注意的是,对于除最微小的输入序列之外的所有操作,此操作都非常昂贵。它并不是最有效的,但即使你确实剔除了最后一点性能,你也无法计算超过15-20的序列组合,这取决于你愿意等待的时间长短。你的电脑有多好。
答案 1 :(得分:0)
您可以使用Combinatorics库为您计算(documentation),但正如Servy所说,数据的长度是需要多长时间的主要因素。
答案 2 :(得分:0)
它们不是真正的子集,因为没有什么可以阻止输入序列包含重复项,但以下扩展方法应该适用于一般情况:
public static IEnumerable<IEnumerable<T>> Subsets<T>(this IEnumerable<T> source)
{
List<T[]> yielded = new List<T[]> { new T[0] };
foreach(T t in source)
{
List<T[]> newlyYielded = new List<T[]>();
foreach(var y in yielded)
{
var newSubset = y.Concat(new[] {t}).ToArray();
newlyYielded.Add(newSubset);
yield return newSubset;
}
yielded.AddRange(newlyYielded);
}
}
基本上从空序列开始,它会添加空序列,并附加第一个项目。然后,对于这两个序列中的每一个,它将附加下一个项目的序列添加。那么对于这四个序列中的每一个......
这必须保留每个序列的副本,因此会占用大量内存。
要从字符串中获取字符串,可以将其称为
"abcd".Subsets().Select(chars => new string(chars.ToArray()))
如果你不会有很多角色,你可以利用你可以直接计算第n个子集的事实:
public static int SubsetCount(this string s)
{
return 2 << s.Length;
}
public static string NthSubset(this string s, int n)
{
var b = New StringBuilder();
int i = 0;
while (n > 0)
{
if ((n&1)==1) b.Append(s[i]);
i++;
n >>= 1;
}
return b.ToString();
}
答案 3 :(得分:0)
我编写了一个类来处理使用二项式系数的常用函数,这是您的问题所处的问题类型。我没有看过@Bobson建议的Cominatorics库,但我相信我的课程可能更快更高效。它执行以下任务:
以任意N选择K到文件的格式输出所有K索引。 K-index可以用更具描述性的字符串或字母代替。
将K索引转换为已排序二项系数表中条目的正确索引。这种技术比依赖迭代的旧发布技术快得多。它通过使用Pascal三角形中固有的数学属性来实现。我的论文谈到了这一点。我相信我是第一个发现和发布这种技术的人,但我可能错了。
将已排序的二项系数表中的索引转换为相应的K索引。我相信它可能比您找到的链接更快。
使用Mark Dominus方法计算二项式系数,这样就不太可能溢出并使用更大的数字。
该类是用.NET C#编写的,它提供了一种通过使用通用列表来管理与问题相关的对象(如果有)的方法。此类的构造函数采用名为InitTable的bool值,当为true时,将创建一个通用列表来保存要管理的对象。如果此值为false,则不会创建表。不需要创建表来执行上述4种方法。提供访问者方法来访问该表。
有一个关联的测试类,它显示了如何使用该类及其方法。它已经过2个案例的广泛测试,并且没有已知的错误。
要阅读此课程并下载代码,请参阅Tablizing The Binomial Coeffieicent。
您的问题的解决方案涉及为每个N选择K案例生成K索引。因此,在上面的示例中,N(A,B,C,D)有4种可能性,代码(在C#中)看起来像这样:
int TotalNumberOfValuesInSet = 4;
int N = TotalNumberOfValuesInSet;
// Loop thru all the possible groups of combinations.
for (int K = N - 1; K < N; K++)
{
// Create the bin coeff object required to get all
// the combos for this N choose K combination.
BinCoeff<int> BC = new BinCoeff<int>(N, K, false);
int NumCombos = BinCoeff<int>.GetBinCoeff(N, K);
int[] KIndexes = new int[K];
// Loop thru all the combinations for this N choose K case.
for (int Combo = 0; Combo < NumCombos; Combo++)
{
// Get the k-indexes for this combination, which in this case
// are the indexes to each letter in the set starting with index zero.
BC.GetKIndexes(Loop, KIndexes);
// Do whatever processing that needs to be done with the indicies in KIndexes.
...
}
// Handle the final combination which in this case is ABCD since since K < N.
...
}
答案 4 :(得分:0)
上面servy的代码非常优雅,但它不会生成与源代码长度相同的组合。
for (int i = 0; i < source.Count; i++) should be
for (int i = 0; i <= source.Count; i++).
以下是vb.net变体,它不能使用yield。
<Extension()>
Public Function Combinations(Of T)(source As IEnumerable(Of T), n As Integer) As IEnumerable(Of IEnumerable(Of T))
Dim lstResults As New List(Of IEnumerable(Of T))
If n = 0 Then
lstResults.Add(Enumerable.Empty(Of T))
Else
Dim count As Integer = 1
For Each item As T In source
For Each innerSequence In source.Skip(count).Combinations(n - 1)
lstResults.Add(New T() {item}.Concat(innerSequence))
Next
count += 1
Next
End If
Return lstResults
End Function
<Extension()>
Public Function AllCombinations(Of T)(source As IList(Of T)) As IEnumerable(Of IEnumerable(Of T))
Dim output As IEnumerable(Of IEnumerable(Of T)) = Enumerable.Empty(Of IEnumerable(Of T))()
For i As Integer = 0 To source.Count
output = output.Concat(source.Combinations(i))
Next
Return output
End Function