组合的概念不用多解释。计算组合数有特定的公式。
当然某些应用可能要求我们枚举组合中所有的可能情况,这时套公式算数就无能为力了。
如:集合{1,2,3,4}的字典序2-组合为{1,2}{1,3}{1,4}{2,3}{2,4}{3,4},组合数为6。
计算机生成组合各种情况有多种方式,主要的思路是,首先要凑够r-组合,即数量要够,等于r;其次,不能重复,可以依次列举包含第一个元素的r-组合,列举完毕后,列举包含第二个元素,但不包含第一个元素的r-组合,以此类推。
我用递归的方法实现了一个基于上述思路的生成组合算法,求解{1,2....8}的4-组合。
#define ARRAYSIZE 8 #define PICKNUM 4 int gl_i = 0;//统计组合数 //对于输入inputArray[i...ARRAYSIZE-1],取k个元素的所有组合,逆序存入outputArray中 void Recur_Combination(int inputArray[], int i, int k, int outputArray[]); int main(int argc, char* argv[]) { int inputArray[ARRAYSIZE] = {1, 2, 3, 4, 5, 6, 7, 8}; int outputArray[PICKNUM]; //递归方法求组合 Recur_Combination(inputArray, 0, PICKNUM, outputArray); cout << endl << gl_i << endl; return 0; } void Recur_Combination(int inputArray[], int i, int k, int outputArray[]) { if (k == 0) { for (int j = 0; j < PICKNUM; j++) { cout << outputArray[j] << " "; } gl_i++; cout << endl; } else if (i + k - 1 <= ARRAYSIZE - 1) { outputArray[k - 1] = inputArray[i]; //选取inputArray中第i个元素放入组合,则在剩下的inputArray[i+1..ARRAYSIZE-1]中,选取k-1个 Recur_Combination(inputArray, i+1, k-1, outputArray); //若没有选取第i个元素,则在剩下的inputArray[i+1..ARRAYSIZE-1]中,选取k个 Recur_Combination(inputArray, i+1, k, outputArray); } }
上述算法生成组合序列的效率并不是很高。不适合用来生成大规模组合序列。下面介绍一种较高效的生成方法。
设集合为{1,2,....n},需生成该集合的r-组合。核心思想是:对于当前已经得到的r-组合{a1,a2,....ar},找到最大的k,使得ak ////////////////////////////////////////////////////////////////////////// // This program is designed to create r- combination in alphabet order. // The main idea in this program is to find the max intergar k so that // ak < n and ak + 1 doesn't appear in a1a2a3....ar. ////////////////////////////////////////////////////////////////////////// void GetCombination(int iTotal, int iPickOut); bool IsPickOutOver(int *pPickOutArrany, int iPickOut, int iTotal); bool IsNumInPickOutArray(int num, int *pPickOutArrany, int iPickOut); void PrintPickOutArray(int *pPickOutArrany, int iPickOut); int gl_i = 0;//count the number of differnt combinations int main(int argc, char* argv[]) { int i, j; printf("Please enter the number of the elments:/n"); scanf("%d", &i); printf("Please enter the number you want to pick:/n"); scanf("%d", &j); GetCombination(i, j); printf("The number of different combinations is: %d./n", gl_i); return 0; } void GetCombination(int iTotal, int iPickOut) { // this program only create the PickOutArray[1..iPickOut] to store the picked index of the CollectionArray[1...iTotal](CA). // iPickOut <= iTotal. This Program doesn't have CA. Just default set CA = 1..iTotal. // You can get the real elements in the CollectionArray by index. int *pPickOutArrany = (int *)malloc((iPickOut + 1) * sizeof(int *));//0th is not used int i; for (i = 1; i <= iPickOut; i++) { pPickOutArrany[i] = i; } int k;//used to find the max number that a(k-1) doesn't need to change PrintPickOutArray(pPickOutArrany, iPickOut); while (!IsPickOutOver(pPickOutArrany, iPickOut, iTotal)) { for (k = iPickOut; k >= 1; k--) { //pPickOutArrany[k] must less than iTotal. if pPickOutArray[k]=iTotal, k must be equal to iPickOut, //k need to move leftwards. if (pPickOutArrany[k] < iTotal) { //find the max k so that pPickOutArrany[k]+1 is not in pPickOutArray. if (!IsNumInPickOutArray(pPickOutArrany[k]+1, pPickOutArrany, iPickOut)) { //update the pPickOutArrany.pPickOutArrany[1..k-1] does't need to change pPickOutArrany[k] += 1; for (i = k + 1; i <= iTotal; i++) { pPickOutArrany[i] = pPickOutArrany[i-1] + 1; } PrintPickOutArray(pPickOutArrany, iPickOut); break; } } } } } bool IsPickOutOver(int *pPickOutArrany, int iPickOut, int iTotal)//pick out over? { bool b = true; int i; for (i = 1; i <= iPickOut; i++) { if (pPickOutArrany[i] != iTotal - iPickOut + i) { b = false; break; } } return b; } bool IsNumInPickOutArray(int num, int *pPickOutArrany, int iPickOut) { int i; for (i = 1; i <= iPickOut; i++) { if (pPickOutArrany[i] == num) { return true; } } return false; } void PrintPickOutArray(int *pPickOutArrany, int iPickOut) { int i; for (i = 1; i <= iPickOut; i++) { printf("%d ", pPickOutArrany[i]); } printf("/n"); ++gl_i; }