全排列及相关扩展算法(七)——组合数的字典序(另含全章代码整理)

来源:互联网 发布:mac上看电视的软件 编辑:程序博客网 时间:2024/05/19 22:02
1.引入概念:要列出一个集合{1,2,3,4}的所有子集是很容易的,我们可以按照二进制数的顺序,0000,0001,0010,0011,0100,0101,0110,0111......来表示我们要取的元素,其中0表示不取,1表示取,这样就获得了一个顺序。而组合也包含在这个顺序当中。我们看从{1,2,3,4}中选取两个元素的所有组合:
0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111.

蓝色标记的是我们所取的组合。对应的顺序便是{3,4}{2,4}{2,3}{1,4}{1,3}{1,2}我们可以用字典序对这些组合进行排序:{1,2}{1,3}{1,4}{2,3}{2,4}{3,4}.共6种。按照我的习惯,我们先看看我们平常列举组合数所采取的策略。比如说1,2,3,4,5取3个元素的组合。我们先从小的取:1,2,3.再把最后一个数改变:1,2,4;1,2,5.当到达5之后,含有1,2的所有组合已经被罗列完了。改变2为3,1,3,4;1,3,5;此时我们不能再选择2进行罗列,否则会重复。改变3为4,此时2和3都不能放入,1,4,5.改变4为5时,2,3,4都不能放入,不存在组合了。这时改变1为2。之后不能再选择1罗列。所以:对于一个组合C1C2C3C4...Cr(每个代表一个数,一共r个数,代表一个组合,数从[1,n]中选),由于一个组合自身是不讲顺序的,我们可以对组合进行升序排列,使得C1<C2<C3<...Cr。一开始我们取最小的元素的组合,并按照字典序把Cr慢慢递增1,这显然是在组合不断取字典序的下一个组合,没有夹杂在这这个方法能取到的值中间的其它组合。在所有组合数当Cr到达n之后,Cr无法变大了,就要做其它的事。由于强制了升序,C(r-1) < Cr ,所以C(r - 1) 到达n-1后也得去做其它的事。

2.性质:

C(r - m)到达n - m后就不能再递增上去。由k = r - (r - k)得到
C(k)到达n - (r - k) = n - r + k 后就不能再递增上去。

那么,对于C1C2C3C4...Cr,我们从Cr开始向前找,直到找到有Ci < n - r + i,并执行
Ci = Ci + 1。由于所有的组合都已经强制升序,所以Cj= Ci + (j-i),j = i+1,i+2,...r即它后面的每一项都比前一项大1。由于Ci< n -r + i,Cr = Ci + (r - i) <=n(因为Ci比原来大了1),所以构造出来的新的C1C2C3C4...Cr仍是[1,n]中选r个元素的一个组合,而且显然它在原组合的字典序后。
由此,我们构造出了一个排在原组合的字典序后面的组合。接下来证明它是紧邻着原组合的。

3.证明:假设有一个组合B1B2B3B4...Br的前i-1个元素和C1C2C3C4...Cr一样,且在原组合字典序后,在我们构造的字典序前,那么必有新Ci > Bi > 原Ci,这是不可能的,因为新Ci= 原Ci + 1。同样不可能存在前i个元素一样但在新Ci字典序前的组合,因为强制排序后Cj 到Cr 的每个元素都是最小的。同样不可能存在前k个元素一样(k < i)但在新Ci字典序前的组合。

如果找不到有Ci < n - r + i,说明已经到达最后一个:n -r + 1,n - r + 2,...n.由此我们可以直接得到组合数的相对字典序。下一个组合算法代码:

bool Next_Combination(int A[], int n, int r){int i;sort(A, A + r);for (i = r - 1; !(A[i] <n - r + i) && i > 0; i--);if (i == 0)return false;A[i] = A[i] + 1;for (int j = i + 1; j<r; j++)A[j] = A[i] + j - i;return true;}


4.参考文档

https://wenku.baidu.com/view/8c79a2facc17552706220880.html



PS:全章代码整理:

//https://wenku.baidu.com/view/8c79a2facc17552706220880.html#include<stdio.h>#include<string.h>#include<stdlib.h>#include<algorithm>#include<iostream>#include<windows.h>#include<math.h>#include <time.h>  #include <functional>using namespace std;int Count = 0;void Swap(int &a, int &b){a == b ? 0 : a ^= b ^= a ^= b;}void Print(int A[],int n){for (int i = 0; i < n; i++){printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');}}void Permutation(int A[], int m, int n){if (m == n){//Print(A, n);Count++;}else{for (int i = m; i < n; i++){Swap(A[m],A[i]);Permutation(A, m + 1, n);    Swap(A[m],A[i]);}}}void Reverse(int A[],int a,int b){while (a < b){Swap(A[a], A[b]);a++;b--;}}bool next_permutation(int A[], int n){int i = n - 2;while ((A[i + 1] <= A[i])&&i>=0)i--;if (i<0){Reverse(A,0,n-1);return false;}else{int j = i+1;while ((A[j] > A[i])&&j<n)j++;Swap(A[j-1], A[i]);Reverse(A ,i+1 , n-1);return true;}}int factorial(int x) { return x > 1 ? x*factorial(x - 1) : 1; }int* get_permutation_medium(int A[], int n){int* temp = new int[n-1];for (int i = 0; i < n-1; i++){temp[i] = 0;for (int j = i + 1; j <= n - 1; j++){if (A[j] < A[i]){temp[i]++;}}}return temp;}int get_permutation_rank(int medium[],int n){int rank = 0;for (int i = 0; i < n - 1;i++){rank += medium[i]* factorial(n - 1 - i);}return rank;}int* get_permutation(int medium[], int n){int* temp = new int[n + 1];int* permutation = new int[n + 1];for (int i = 0; i <= n; i++){permutation[i]=temp[i] = i == n ? 1 : medium[i] + 1;}for (int i = 0; i <= n ; i++){for (int j = i - 1; j >= 0; j--){if (temp[j] <= temp[i]){temp[i]++;permutation[i]++;}else{break;}}sort(temp,temp + i+1, greater<int>());}return permutation;}int* get_permutation_error(int medium[], int n){int* temp = new int[n + 1];for (int i = 0; i <= n; i++){temp[i] = i == n ? 1 : medium[i] + 1;}for (int i = 0; i <= n; i++){for (int j = i - 1; j >= 0; j--){if ((medium[j] <= (i == n ? 1 : medium[i])) || (temp[j] <= temp[i])){temp[i]++;}}}return temp;}int* get_permutation_medium_plus(int A[], int n){int* temp = new int[n];for (int i = 0; i < n; i++){temp[n-A[i]] = 0;for (int j = i + 1; j <= n - 1; j++){if (A[j] < A[i]){temp[n-A[i]]++;}}}return temp;}int get_permutation_rank_plus(int medium[], int n){int rank = 0;for (int i = 0; i < n-1; i++){rank += medium[i];rank *= n - i -1;}return rank;}int* get_permutation_plus(int medium[], int n){int* temp = new int[n];memset(temp, 0, n * sizeof(int));for (int i = 0; i < n; i++){int empty = -1,j=n;//防止末尾已经被占的情况故提前一位while (empty < medium[i] && j >= 0){j--;if (temp[j] <= 0){empty++;}}temp[j] = n - i;}return temp;}bool Movable(int A[], bool direct[], int n) //direct参数用于接收每个元素移动方向的数组。{int max = 1;//初始化最大可移动数为1,因为规定1是最小的数,可以自己设定。int pos = -1;//初始化最大可移动数的位置为-1./*下面先找到最大可移动数位置*/for (int i = 0; i<n; i++){if (A[i] < max)continue;if ((i < n - 1 && A[i] > A[i + 1] && direct[i]) || (i> 0 && A[i] >A[i - 1] && !direct[i])){max = A[i];pos = i;}}/*下面对它进行移动*/if (pos == -1)return false;if (direct[pos]){swap(A[pos], A[pos + 1]);swap(direct[pos], direct[pos + 1]);}else{swap(A[pos], A[pos - 1]);swap(direct[pos], direct[pos - 1]);}/*最后调整所有比最大可移动数大的数的方向*/for (int i = 0; i<n; i++){if (A[i] > max)direct[i] = !direct[i];}return true;}void Full_Array(int A[], int n){bool* direct = new bool[n]; //产生一个记录每个元素移动方向的数组sort(A, A + n); //将原序列变成一个升序for (int i = 0; i<n; i++)    direct[i] = false;//初始化移动方向为false,表示从右向左。do{//Print(A, n);Count++;if (A[n - 1] == n)for (int i = n - 1; i>0; i--){swap(A[i], A[i - 1]);swap(direct[i], direct[i - 1]); //Print(A, n);Count++;}elsefor (int i = 0; i < n-1; i++){swap(A[i], A[i + 1]);swap(direct[i], direct[i + 1]);//Print(A, n);Count++;}} while (Movable(A, direct, n));delete[]direct;}bool Next_Combination(int A[], int n, int r){int i;sort(A, A + r);for (i = r - 1; !(A[i] <n - r + i) && i > 0; i--);if (i == 0)return false;A[i] = A[i] + 1;for (int j = i + 1; j<r; j++)A[j] = A[i] + j - i;return true;}int main(){//int A[] = { 7,6,8,3,4,5,1,2 };//int A[] = { 3,6,5,1,2,4,7 };//int A[] = { 7,6,5,4,3,2,1 };//int A[] = { 1,2,3,4};int A[] = { 1,2,3,4,5,6,7,8,9,10,11,12};int n = sizeof(A) / sizeof(A[0]);//STL模版函数全排列:time_t t1 = time(NULL);    while (next_permutation(A, n))Count++;printf("%d\n", Count); Count = 0;//邻位对换法全排列:time_t t2 = time(NULL);Full_Array(A, n);printf("%d\n", Count); Count = 0;//基础回溯递归全排列:time_t t3 = time(NULL);Permutation(A, 0, n);printf("%d\n", Count); Count = 0;time_t t4 = time(NULL);cout << "STL模版函数全排列: " << t2 - t1 << "s " << endl;cout << "邻位对换法全排列: " << t3 - t2 << "s " << endl;cout << "基础回溯递归全排列: " << t4 - t3 << "s " << endl;//printf("%d\n", Count);//递增进位制数法中序数求排位//int *medium = get_permutation_medium_plus(A, n);//Print(medium, n);//int rank =get_permutation_rank_plus(medium, n);//printf("%d\n",rank);//int *B = get_permutation_plus(medium, n);//Print(B, n);//原始中序数求排位//int *medium2 = get_permutation_medium(A, n);//Print(medium2, n - 1);//int *B2 = get_permutation(medium2, n - 1);//Print(B2,n);//int rank2 = get_permutation_rank(medium2, n);//printf("%d\n", rank2);//用STL模版函数循环求排位/*while (next_permutation(A, n)||!Count){printf("原排列:  ");Print(A, n);printf("中介数:  ");int *medium = get_permutation_medium_plus(A, n);Print(medium, n);int rank = get_permutation_rank_plus(medium, n);printf("新序号:  %d\n", rank);printf("---------------\n");Count++;}*///printf("%d\n", factorial(n)-Count-1);//Permutation(A, 0, n);//printf("%d\n", Count);//sort(A, A+n );//Print(A,n);//prev_permutation(A, A + n);/*Print(A, n);while (next_permutation(A,  n)){Count++;Print(A,n);}printf("%d\n", Count);Print(A, n);*/system("pause");return 0;}




阅读全文
1 0
原创粉丝点击