假定你有四个姓名条目——Adam, Barb, Carl, Dave——你想得到所有这四个条目中每次选出两个元素的组合。下面所示的 C# 代码片断将生成这个组合的六个元素:
// naive technique to generate all combinations Console.WriteLine("\nAll elements of 4 names, 2 at a time: "); string[] names = {"Adam", "Barb", "Carl", "Dave"}; for (int i = 0; i < names.Length; ++i) { for (int j = i+1; j < names.Length; ++j) { Console.WriteLine( "{ " + names[i] + ", " + names[j] + " }" ); } }
如果你执行这个代码,(正确的)结果将是:
{ Adam, Barb }, { Adam, Carl }, { Adam, Dave }, { Barb, Carl },
{ Barb, Dave }, { Carl, Dave }.
但是这里有三个问题。首先,如果你想要生成组合的所有元素时,这个方法运行很正常,但是如果你只想得到部分元素或一个特别元素呢?第二,这是一个对于特定问题的特定 解法,它并不具有普遍性。第三,只有当每个子集元素的条目个数 k 很小时,它才能工作得较好。
但是如果 k 非常大时会怎样呢?如果你想一次从 n = 100 个的条目中挑出 50 个,你就不得不编写50个循环或使用递归。
对于生成所有组合元素的一个更好的解决方案是实现一个 Successor(继承)方法,它返回给定元素的下一个按词典排序的元素。如果你将 Successor 和 ApplyTo 方法联合使用,它 将某个数学组合映射到一个字符数组上,你将会具备一个有效的、具有普遍性的方法来生成所有组合元素。
Figure 6 的代码表示了 Successor 方法。Successor 一开始就检查这里是否存在下一个组合元素。举个例子,假设你正在处理每次从 n=5 个条目中取 k=3 个元素。这里 有 10 种组合情况:
{ 0, 1, 2 } { 0, 3, 4 }
{ 0, 1, 3 } { 1, 2, 3 }
{ 0, 1, 4 } { 1, 2, 4 }
{ 0, 2, 3 } { 1, 3, 4 }
{ 0, 2, 4 } { 2, 3, 4 }
注意你可以确定词典序列的最后一个元素,{ 2, 3, 4 },因为这里只有一个元素它有第一个原子2——它等于 n-k 的值,或者换句话说,它有一个 n-k 的值在第0个位置上。这个关系一般来说对于所有的组合都是正确的。同样地,你可以确定词典序列的第一个元素,{ 0, 1, 2 },因为这里只有一个元素的最后一个原子为 2,或者换句话说,这里有 n-k 的值在第 k-1 位置上。而且,一般来说这总是正确的。如果这里没有有效的下一个元素 Successor 方法就返回 null。选择返回 null 将导致抛出一个异常或返回一个错误代码。
文章来源于领测软件测试网 https://www.ltesting.net/