即数学上的排列组合,这里提供了两种方式。
第一种是用STL头文件algorithm中的next_permutation或prev_permutation函数。但是这样只能求全排列,不能求组合。这种方法网络上很多讲解的,这里不再赘述。
第二种是自己编写的算法,采用了遍历和回溯。
算法描述如下:
结果存于数组a中
判断a[i]是否符合条件,若符合再判断是否已到第N位,若不符合,再判断a[i]加一还是回到第i-1位继续判断
a[i]的初值是a[i-1]值的下一个值
若a[i]的值与a[i-1]相等时,第i位此轮遍历结束
低位逐次加1,此位遍历结束时,其上一位加1,直到第1位加到N程序结束
数组a的a[0]弃置不用,用以表示排列组合元素的数字从1开始,不使用0
#include <stdio.h> #include <stdlib.h> #define arrange//排列,否则是组合 ///求解A(N,M)或C(N,M) #define N 4 #define M 4 int a[N+1] = {0,1};//必须保证a[1]为1 int counter = 0;//计算排列或组合数, 注意数目太大时可能溢出 ///将符合条件的情况输出 void show() { for(int k=1; k<=N; ++k) printf("%d",a[k]); printf(" "); ++counter; } ///判断数组a第i位的值是否符合要求 bool ismatch(int i) { for(int k=1; k<i; ++k) { #ifdef arrange//排列 if(a[k] == a[i]) return false; #else//组合 if(a[k] >= a[i]) return false; #endif } return true; } ///回溯或自加 int backtrack(int &i) { if(a[i] == a[i-1])//若a[i]的值已经经过一圈追上a[i-1] { --i;//i值减1,退回处理前一个元素 if(a[i]==M && i>1) a[i] = 1;//当第i位的值达到M时,第i位的值取1 else if(a[i]==M && i==1)//当第一位的值达到M时结束 return 0;//遍历结束,退出程序 else ++a[i];//第i位的值取下一个,加1 } else if(a[i]==M) a[i]=1; else ++a[i]; return 1; } ///遍历 void ergodic(void) { int i=1; while(1) { if(ismatch(i))//第i位已经满足要求,处理第i+1位 { if(i==N)//i已到达N,获得了一个结果 { show(); if(!backtrack(i)) return; } else//继续处理下一位 { ++i; if(a[i-1] == M) a[i] = 1;//a[i]的初值是a[i-1]值的下一个值 else a[i] = a[i-1] + 1; } } else//第i位不满足要求,需要重新设置:回溯至第i-1位或a[i]加1 if(!backtrack(i)) return; } } int main(void) { ergodic(); printf("\n\n共 %d 种排列/组合.\n",counter); return 0; }
算法:
在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*i,后一个记为*ii,并且满足*i < *ii。然后再从尾端寻找另一个元素*j,如果满足*i < *j,即将第i个元素与第j个元素对调,并将第ii个元素之后(包括ii)的所有元素颠倒排序,即求出下一个序列了。
原型:
template<class BidirectionalIterator> bool next_permutation(BidirectionalIterator _First, BidirectionalIterator _Last); template<class BidirectionalIterator, class BinaryPredicate> bool next_permutation(BidirectionalIterator _First, BidirectionalIterator _Last, BinaryPredicate _Comp);
我以int数组实现这个算法:
template <typename T> void nswap(T *a, T *b) { if(a == b) return; T temp; temp = *a; *a = *b; *b = temp; } //反转序列[first,last) template <typename T> void reverse(T first, T last) { --last; while(last - first > 0) nswap(first++, last--); } //区间为[first,last) bool next_permutation(int *first, int *last) { if(last - first < 2) return false; //只有一个元素或为空 int *ii = last-1; while(!(*(ii-1)<*ii)) { --ii; //已是最大序列,反转一次换到最小序列 if(ii == first) { reverse(first, last); return false; } } int *i = ii-1; int *j = last-1; for(; j!=i; --j) { if(*i < *j) { nswap(i, j); break; } } reverse(ii, last); //置换[ii,last) return true; }