排列:從n個不同元素中,任取m(m<=n)個元素按照一定的順序排成一列,叫做從n個不同元素中取出m個元素的一個排列;從n個不同元素中取出m(m<=n)個元素的所有排列的個數,叫做從n個不同元素中取出m個元素的排列數,用符號A(n,m)表示。 A(n,m)=n(n-1)(n-2)……(n-m+1)= n!/(n-m)! 此外規定0!=1
組合:從n個不同元素中,任取m(m<=n)個元素並成一組,叫做從n個不同元素中取出m個元素的一個組合;從n個不同元素中取出m(m<=n)個元素的所有組合的個數,叫做從n個不同元素中取出m個元素的組合數。用符號C(n,m) 表示。 C(n,m)=A(n,m)/m!=n!/((n-m)!*m!); C(n,m)=C(n,n-m)。
C語言使用標志位實現
#include <iostream> using namespace std; #define MaxN 10 char used[MaxN]; int p[MaxN]; char s[MaxN]; //從n個元素中選r個進行排列 void permute(int pos,const int n,const int r) { int i; /*如果已是第r個元素了,則可打印r個元素的排列 */ if(pos == r) { for(i=0; i<r; i++) cout<<s[p[i]]; cout<<endl; return; } for (i=0; i<n; i++) { if(!used[i]) { /*如果第i個元素未用過*/ /*使用第i個元素,作上已用標記,目的是使以后該元素不可用*/ used[i] = 1; /*保存當前搜索到的第i個元素*/ p[pos] = i; /*遞歸搜索*/ permute(pos+1,n,r); /*恢復遞歸前的值,目的是使以后改元素可用*/ used[i] = 0; } } } //從n個元素中選r個進行組合 void combine(int pos,int h,const int n,const int r) { int i; /*如果已選了r個元素了,則打印它們*/ if (pos == r) { for(i=0; i<r; i++) cout<<s[p[i]]; cout<<endl; return; } for(i=h; i<=n-r+pos; i++) /*對於所有未用的元素*/ { if (!used[i]) { /*把它放置在組合中*/ p[pos] = i; /*使用該元素*/ used[i] = 1; /*搜索第i+1個元素*/ combine(pos+1,i+1,n,r); /*恢復遞歸前的值*/ used[i] = 0; } } } //產生0~2^r-1的二進制序列 void binary_sequence(int pos,const int r) { int i; if(pos == r) { for(i=0; i<r; i++) cout<<p[i]; cout<<endl; return; } p[pos] = 0; binary_sequence(pos+1,r); p[pos] = 1; binary_sequence(pos+1,r); } //利用上面的二進制序列打印字符串的所有組合 //如"abc"輸出a、b、c、ab、ac、bc、abc。 void all_combine(int pos,const int r) { int i; if(pos == r) { for(i=0; i<r; i++) { if(p[i]==1) cout<<s[i]; } cout<<endl; return; } p[pos] = 0; all_combine(pos+1,r); p[pos] = 1; all_combine(pos+1,r); } //利用r進制序列打印字符串的所有重復組合 //如"abc"輸出aaa、aab、aac、aba、abb、abc、aca、acb、acc...。 void repeative_combine(int pos,const int r) { int i; if(pos == r) { for(i=0; i<r; i++) { cout<<s[p[i]]; } cout<<endl; return; } for(i=0; i<r; ++i) { p[pos] = i; repeative_combine(pos+1,r); } } int main() { strcpy(s,"ABC"); int n = 3; int r = 3; //permute(0,n,r); //combine(0,0,n,r); //binary_sequence(0,r); //cout<<"string: "<<s<<endl; //all_combine(0,r); //repeative_combine(0,r); return 0; }
排列組合算法的遞歸實現:
#include <iostream> using namespace std; template <class Type> void permute(Type a[], int start, int end) { if(start == end) { for(int i = 0; i <= end; ++i) { cout<<a[i]<<" "; } cout<<endl; } else { for(int i = start; i <= end; ++i) { swap(a[i],a[start]); permute(a,start+1,end); swap(a[i],a[start]); } } } template <class Type> void combine(Type a[], bool b[], int start, int end) { if(start > end) { for(int i = 0; i <= end; ++i) { if(b[i]) cout<<a[i]<<" "; } cout<<endl; } else { b[start] = true; combine(a,b,start+1,end); b[start] = false; combine(a,b,start+1,end); } } int main() { int p[3]={1,2,3}; int N = 3; cout<<"permute:"<<endl; permute(p,0,N-1); cout<<"combine:"<<endl; bool b[3]; combine(p,b,0,N-1); return 0; }
排列算法的迭代實現
C++ STL中提供了next_permutation和prev_permutation算法。因為next_permutation和prev_permutation實際上是一樣的,因此只描述next_permutation算法。next_permutation()函數的作用是取下一個排列組合。考慮{a,b,c}的全排列:abc,acb,bac,bca,cab,cba,以“bac”作為參考,那么next_permutation()所得到的下一個排列組合是bca,prev_permutation()所得到的前一個排列組合是“acb”,之於“前一個”和“后一個”,是按字典進行排序的。
next_permutation()算法描述:
- 從str的尾端開始逆着尋找相鄰的元素,*i和*ii,滿足*i<*ii;
- 接着,又從str的尾端開始逆着尋找一元素,*j,滿足*i>*j(*i從步驟一中得到);
- swap(*i,*j);
- 將*ii之后(包括*ii)的所有元素逆轉。
舉個例子,需要找到“01324”的下一個排列,找到*i=2,*ii=4,*j=4,下一個排列即“01342”。再來找到“abfedc”的下一個排列,找到*i=b,*ii=f,*j=c,swap操作過后為“acfedb”,逆轉操作過后為“acbdef”。
//求階乘 int factorial(int n) { if(n == 1) return 1; return n*factorial(n-1); } template <class Type> void print(Type a, int n) { for(int i = 0; i < n; ++i) cout<<a[i]<<" "; cout<<endl; } template <class Type> void perm2(Type a, int n) { int i,ii,j; int cnt = 1; print(a,n); int num = factorial(n); // STL <algorithm> next_permutation()函數的核心算法 while(++cnt <= num) { i = n - 2; ii = n - 1; j = ii; while(a[i] >= a[ii]) --i,--ii; //find *i and *ii while(a[i] >= a[j]) --j; //find *j swap(a[i],a[j]); //STL swap reverse(a+ii,a+n); //STL reverse print(a,n); } }
排列:從n個不同元素中,任取m(m<=n)個元素按照一定的順序排成一列,叫做從n個不同元素中取出m個元素的一個排列;從n個不同元素中取出m(m<=n)個元素的所有排列的個數,叫做從n個不同元素中取出m個元素的排列數,用符號A(n,m)表示。 A(n,m)=n(n-1)(n-2)……(n-m+1)= n!/(n-m)! 此外規定0!=1
組合:從n個不同元素中,任取m(m<=n)個元素並成一組,叫做從n個不同元素中取出m個元素的一個組合;從n個不同元素中取出m(m<=n)個元素的所有組合的個數,叫做從n個不同元素中取出m個元素的組合數。用符號C(n,m) 表示。 C(n,m)=A(n,m)/m!=n!/((n-m)!*m!); C(n,m)=C(n,n-m)。
C語言使用標志位實現
#include <iostream> using namespace std; #define MaxN 10 char used[MaxN]; int p[MaxN]; char s[MaxN]; //從n個元素中選r個進行排列 void permute(int pos,const int n,const int r) { int i; /*如果已是第r個元素了,則可打印r個元素的排列 */ if(pos == r) { for(i=0; i<r; i++) cout<<s[p[i]]; cout<<endl; return; } for (i=0; i<n; i++) { if(!used[i]) { /*如果第i個元素未用過*/ /*使用第i個元素,作上已用標記,目的是使以后該元素不可用*/ used[i] = 1; /*保存當前搜索到的第i個元素*/ p[pos] = i; /*遞歸搜索*/ permute(pos+1,n,r); /*恢復遞歸前的值,目的是使以后改元素可用*/ used[i] = 0; } } } //從n個元素中選r個進行組合 void combine(int pos,int h,const int n,const int r) { int i; /*如果已選了r個元素了,則打印它們*/ if (pos == r) { for(i=0; i<r; i++) cout<<s[p[i]]; cout<<endl; return; } for(i=h; i<=n-r+pos; i++) /*對於所有未用的元素*/ { if (!used[i]) { /*把它放置在組合中*/ p[pos] = i; /*使用該元素*/ used[i] = 1; /*搜索第i+1個元素*/ combine(pos+1,i+1,n,r); /*恢復遞歸前的值*/ used[i] = 0; } } } //產生0~2^r-1的二進制序列 void binary_sequence(int pos,const int r) { int i; if(pos == r) { for(i=0; i<r; i++) cout<<p[i]; cout<<endl; return; } p[pos] = 0; binary_sequence(pos+1,r); p[pos] = 1; binary_sequence(pos+1,r); } //利用上面的二進制序列打印字符串的所有組合 //如"abc"輸出a、b、c、ab、ac、bc、abc。 void all_combine(int pos,const int r) { int i; if(pos == r) { for(i=0; i<r; i++) { if(p[i]==1) cout<<s[i]; } cout<<endl; return; } p[pos] = 0; all_combine(pos+1,r); p[pos] = 1; all_combine(pos+1,r); } //利用r進制序列打印字符串的所有重復組合 //如"abc"輸出aaa、aab、aac、aba、abb、abc、aca、acb、acc...。 void repeative_combine(int pos,const int r) { int i; if(pos == r) { for(i=0; i<r; i++) { cout<<s[p[i]]; } cout<<endl; return; } for(i=0; i<r; ++i) { p[pos] = i; repeative_combine(pos+1,r); } } int main() { strcpy(s,"ABC"); int n = 3; int r = 3; //permute(0,n,r); //combine(0,0,n,r); //binary_sequence(0,r); //cout<<"string: "<<s<<endl; //all_combine(0,r); //repeative_combine(0,r); return 0; }
排列組合算法的遞歸實現:
#include <iostream> using namespace std; template <class Type> void permute(Type a[], int start, int end) { if(start == end) { for(int i = 0; i <= end; ++i) { cout<<a[i]<<" "; } cout<<endl; } else { for(int i = start; i <= end; ++i) { swap(a[i],a[start]); permute(a,start+1,end); swap(a[i],a[start]); } } } template <class Type> void combine(Type a[], bool b[], int start, int end) { if(start > end) { for(int i = 0; i <= end; ++i) { if(b[i]) cout<<a[i]<<" "; } cout<<endl; } else { b[start] = true; combine(a,b,start+1,end); b[start] = false; combine(a,b,start+1,end); } } int main() { int p[3]={1,2,3}; int N = 3; cout<<"permute:"<<endl; permute(p,0,N-1); cout<<"combine:"<<endl; bool b[3]; combine(p,b,0,N-1); return 0; }
排列算法的迭代實現
C++ STL中提供了next_permutation和prev_permutation算法。因為next_permutation和prev_permutation實際上是一樣的,因此只描述next_permutation算法。next_permutation()函數的作用是取下一個排列組合。考慮{a,b,c}的全排列:abc,acb,bac,bca,cab,cba,以“bac”作為參考,那么next_permutation()所得到的下一個排列組合是bca,prev_permutation()所得到的前一個排列組合是“acb”,之於“前一個”和“后一個”,是按字典進行排序的。
next_permutation()算法描述:
- 從str的尾端開始逆着尋找相鄰的元素,*i和*ii,滿足*i<*ii;
- 接着,又從str的尾端開始逆着尋找一元素,*j,滿足*i>*j(*i從步驟一中得到);
- swap(*i,*j);
- 將*ii之后(包括*ii)的所有元素逆轉。
舉個例子,需要找到“01324”的下一個排列,找到*i=2,*ii=4,*j=4,下一個排列即“01342”。再來找到“abfedc”的下一個排列,找到*i=b,*ii=f,*j=c,swap操作過后為“acfedb”,逆轉操作過后為“acbdef”。
//求階乘 int factorial(int n) { if(n == 1) return 1; return n*factorial(n-1); } template <class Type> void print(Type a, int n) { for(int i = 0; i < n; ++i) cout<<a[i]<<" "; cout<<endl; } template <class Type> void perm2(Type a, int n) { int i,ii,j; int cnt = 1; print(a,n); int num = factorial(n); // STL <algorithm> next_permutation()函數的核心算法 while(++cnt <= num) { i = n - 2; ii = n - 1; j = ii; while(a[i] >= a[ii]) --i,--ii; //find *i and *ii while(a[i] >= a[j]) --j; //find *j swap(a[i],a[j]); //STL swap reverse(a+ii,a+n); //STL reverse print(a,n); } }