排列组合的求法&next_permutation

来源:互联网 发布:2016奥运女排数据 编辑:程序博客网 时间:2024/04/30 11:38

即数学上的排列组合,这里提供了两种方式。

第一种是用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 4int a[N+1] = {0,1};//必须保证a[1]为1int 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;}


next_permutation


算法:

在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*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;}




0 0
原创粉丝点击