}
for (int i=0; i
{swap(pSet[0], pSet[i]);
permutationImp(pSet+1, n-1, m-1, M);
swap(pSet[0], pSet[i]);
}
}
非常简洁明了,这真让人沮丧,相比于Permutation的ToNext的种种烦杂,它无疑极具杀伤力。同样都是在搞排列,为何递归函数可以如此的简单呢?只因编译器帮递归干了很多事情,而ToNext中,我们必须事事亲历亲为。其实这也是递归与非递归的一大差别,递归中以一点点效率和灵活来换取代码的简洁易懂,而非递归则能以各种各样的方法来获取种种灵活与效率,编程就是这样,无非是在做各种各样的妥协。此外,Permutation的输出好看一点,更具规律,严格按照我们的要求来输出,这也能稍稍地自我安慰一下吧。
依此编号的顺序算法,好比,C(4,3),其编号为012,013,023,123,不难设计出一个Combination,而且其ToNext要比部分排列的ToNext简单得多,只要注意到组合数中小的元素必然在前面,例如只存在012的组合,不存在120、102、021等组合数,因为它们都与012为同一个组合数,参考部分排列的代码,很容易就可写出Combination的代码。24点算法涉及的组合只为C(N,2),虽然C(N,2)为C(N,M)的特例,但基于时间与空间等效率的考虑,我更倾向于重新设计一个Combination2专门用来对付C(N,2)。其实算法的设计,不过是时间与空间的交换、复杂与简单的交换、通用与专用的交换,此三项的权衡而已。下面是Combination2的代码,非常简单,我也不想说太多。
class Combination2
{
public:
Combination2(int count)
{
assert(count >= 2);
m_nCount = (char)count;
m_Indexs[0] = 0;
m_Indexs[1] = 1;
}
public:
char operator[](int n)const
{
assert(0<=n && n<2);
return m_Indexs[n];
}
bool ToNext();
private:
char m_Indexs[2];
char m_nCount;
};

bool Combination2::ToNext()
{
if (m_Indexs[0] == m_nCount-1 && m_Indexs[1] == m_nCount-2)
return false;
if (m_Indexs[1] < m_nCount-1)
{
m_Indexs[1]++;
if (m_Indexs[1] == m_Indexs[0])
m_Indexs[1]++;
}
else
{
m_Indexs[0]++;
m_Indexs[1] = 0;
}
return true;
}
至此,终于圆满解决了排列与组合的问题,但其实都没多大的作用,对于不重复集合的排列与组合,只要用公式一乘,就出来结果了。研究可重复集合的排列与组合,可能还有点作用,其设计与代码编写,也很有意思,我就不再罗嗦了。