全排列实现(转载)

来源:互联网 发布:crc循环冗余校验码算法 编辑:程序博客网 时间:2024/05/24 01:30
字典序排列
把升序的排列(当然,也可以实现为降序)作为当前排列开始,然后依次计算当前排列的下一个字典序排列。

对当前排列从后向前扫描,找到一对为升序的相邻元素,记为i和j(i < j)。如果不存在这样一对为升序的相邻元素,则所有排列均已找到,算法结束;否则,重新对当前排列从后向前扫描,找到第一个大于i的元素k,交换i和k,然后对从j开始到结束的子序列反转,则此时得到的新排列就为下一个字典序排列。这种方式实现得到的所有排列是按字典序有序的,这也是C++ STL算法next_permutation的思想。算法实现如下:

template <typename T>  void CalcAllPermutation(T perm[], int num)  {      if (num < 1)          return;                while (true) {          int i;          for (i = num - 2; i >= 0; --i) {              if (perm[i] < perm[i + 1])                  break;          }                    if (i < 0)              break;  // 已经找到所有排列                int k;          for (k = num - 1; k > i; --k) {              if (perm[k] > perm[i])                  break;          }                    swap(perm[i], perm[k]);          reverse(perm + i + 1, perm + num);               }  }  

3. 组合算法

3.1 全组合

在此介绍二进制转化法,即,将每个组合与一个二进制数对应起来,枚举二进制的同时,枚举每个组合。如字符串:abcde

00000 <– –> null

00001<– –> e

00010 <– –> d

… …

11111 <– –> abcde

3.2 从n中选m个数

(1) 递归

a. 首先从n个数中选取编号最大的数,然后在剩下的n-1个数里面选取m-1个数,直到从n-(m-1)个数中选取1个数为止。

b. 从n个数中选取编号次小的一个数,继续执行1步,直到当前可选编号最大的数为m。

下面是递归方法的实现:


/// 求从数组a[1..n]中任选m个元素的所有组合。 /// a[1..n]表示候选集,n为候选集大小,n>=m>0。 /// b[1..M]用来存储当前组合中的元素(这里存储的是元素下标), /// 常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。 voidcombine( inta[], intn, intm,  intb[], constint M ){  for(inti=n; i>=m; i--)   // 注意这里的循环范围  {    b[m-1] = i - 1;    if(m > 1)      combine(a,i-1,m-1,b,M);    else                    // m == 1, 输出一个组合    {      for(intj=M-1; j>=0; j--)      <SPAN style="WHITE-SPACE: pre"></SPAN>cout << a[b[j]] << " ";      cout << endl;    }  }}

洗牌算法:
错误方法
1。随机产生一个1-n的数x,然后让第x张牌和第1张牌互相调换。
2。随机产生一个1-n的数y,然后让第y张牌和第2张牌互相调换。
3。随机产生一个1-n的数z,然后让第z张牌和第i张牌互相调换。(i=3,4,5...54)
这种算法的复杂度为O(N)。

看好像不错,网上也有很多文章使用这种算法,但是其实这是一种错误的方法,因为方法二的所有可能性为N^N,而洗好的牌一种有N!种可能,又因为N^N % N! !=0,所以每种结果的概率是不相同的。
那么如何修正这个问题呢?第i次洗牌不是产生一个1-n的随机数,而是产生一个i-n的随机数,这样可能性结果的可能性就是N!了。就有可能概率相等了。
证明在<<计算机程序设计艺术>>上。

修正方法:
#include <stdio.h>#include <stdlib.h>#define swap(a,b) {int t=(a);(a)=(b);(b)=(t);}#define N 54int poker[N+1];void shuffle(){    int i,k;    for(i=1;i<=N;i++)//初始化        poker[i]=i;    for(i=1;i<=N;i++){        k = rand()%(N+1-i)+i;         swap(poker[i],poker[k]);   //交换    }   }int main(){    int i;    srand(time(NULL));    shuffle();    for(i=1;i<=N;i++)        printf("%d ", poker[i]);    printf("\n");}



转自:http://blog.csdn.net/v_july_v/article/details/6879101

原创粉丝点击