全排列实现(转载)

字典序排列
把升序的排列(当然,也可以实现为降序)作为当前排列开始,然后依次计算当前排列的下一个字典序排列。

对当前排列从后向前扫描,找到一对为升序的相邻元素,记为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 54

int 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值