关于排列枚举算法

来源:互联网 发布:帝国cms 模型 编辑:程序博客网 时间:2024/05/19 03:27

// 非递归   字典序(可变为随机序),元素可以重复也可不重复,部分排列(包含全排列)部分排列分为从一个数组中取部分元素进行全排列,或取部分元素做A(m,n)排列,m和n是不等的。

//欢迎提出不同意见。  QQ 1423173783     邮箱:fanzhiyuan110@sohu.com          参考资料:http://tieba.baidu.com/f?kz=12248706

#include<cstdlib>
#include<iostream>
#include<fstream>
#include<time.h>
#include<iomanip>
#define N 10                             //种子的元素个数  或原始排列的个数
#define NUM  5                         //从原始排列中选取NUM个元素进行操作
using namespace std;
ofstream out;
int count=0;                       //统计排列的个数
int a[N] ,c[NUM],d[NUM],sub=0;             //算法是字典序的a数组用来保存下标
char b[N];                                 //种子
void Swap(int* lhs, int* rhs) ;     
void Reverse(int* beg, int* end) ;
void print1();
int Cmp(const void* lhs, const void* rhs) ;
void P(int* beg, int* mid, int* end);
void select(int *a,int beg,int end,int interm);
void Swap(int* lhs, int* rhs)
{
    int tmp = *lhs;
    *lhs = *rhs;
    *rhs = tmp;
}
void Reverse(int* beg, int* end)
{
    while(beg < end)
    Swap(beg++, --end);
}
void print1()                                 //打印时会输出整个排列,自然会反映出代码做的部分排列或全排列                       
{
 for(int i=0;i<=NUM-1;i++)
 {
  a[d[i]]=c[i];
 }
 for(int i=0;i<=N-1;i++)
  out<<setw(4)<<b[a[i]]<<" ";
 out<<endl;
 count++;
}
int Cmp(const void* lhs, const void* rhs)
{
    return *(const int*)rhs - *(const int*)lhs;
}
void select(int *a,int beg,int end,int interm)  //从a数组中选取NUM个b数组元素的下标进行全排列,beg是a数组的起始地址,end是终点地址
{
 if(interm==0)                  //随机取数组a的NUM个下标(beg——end两端可包含)放到d数组中
 {                              //..................元素...........................c......
         int i;
   srand(time(0));          //这里没有对d数组排序,是因为本代码的侧重点是枚举所有排列具体问题要具体分析,可以根据实际情况对d数组进行排列  下面三种情况类似
   while(sub<NUM)
   {
    i=rand()%(end-beg+1)+beg;
    if(a[i]==0)  continue;
    else
    {
     c[sub]=a[i];
     d[sub]=i;
     sub++;
     a[i]=0;
    }
   }
         qsort(c,NUM,sizeof(int),Cmp);
   Reverse(c,c+NUM);
  
 }
 else if(interm==1000)        //取数组a的NUM个下标放到d数组中,取数组a的NUM个元素放到c数组中,输入的是下标(注意:b数组没交换输入的是b数组的下标,b数组交换了输入的则是a数组的下标因为随机交换是无法预测的但实质上还是b数组的下标)
 {
  for(int i=0;i<=NUM-1;i++)//输入下标时可随意输入就是不能相同且不要越界,下标不同b数组元素可能相同,具体怎样输入要具体问题具体分析,如题目要求选取b数组中不同的NUM个元素进行排列,那么在输入下标时就要注意有些下标不同元素相同的情况
  { cin>>d[i]; c[i]=a[d[i]];}
     qsort(c,NUM,sizeof(int),Cmp);
  Reverse(c,c+NUM);
 }
 else
 {
  for(int i=0;i<=NUM-1;i++)             //取数组a的NUM个下标放到d数组中,beg,beg+interm,beg+2*interm,beg+3*interm...
  { d[i]=beg+interm*i;  c[i]=a[d[i]] ; }//取数组a的NUM个元素放到c数组中
     qsort(c,NUM,sizeof(int),Cmp);         //beg和interm和NUM选取时注意不要使数组越界 
  Reverse(c,c+NUM);
 }
}
void P(int* beg, int* mid, int* end)   //核心函数,对从小到大的下标进行排列
{                 
    bool partial = end != mid  ? true : false;
    int* initPos = partial ? mid + 1 : end ;
    int* next = initPos;
    if(beg == end || next == beg)
    return;
    if(partial)
    qsort(mid+1, end - mid, sizeof(int), Cmp);
    print1();
    while(true)
      {
         int* prev = next;
         if(*--next < *prev)
          {
             int* rmbt = end;
             while(*rmbt <= *next) rmbt--;
             Swap(next, rmbt);
             if(prev <= mid)
               {
                   Reverse(prev, end+1);
                   if(partial)
                   qsort(mid+1, end - mid, sizeof(int), Cmp);
               }
             print1();
             next = initPos;
          }
        if(next == beg)
          {
            Reverse(beg, end+1);
            break;
          }
     }
}
int main()
{
    for(int i=0;i<=N-1;i++)
   {
   a[i]=i;
   b[i]=i+65;
   }
 a[2]=a[1];b[2]=b[1];
 b[4]=b[3];a[4]=a[3];
 b[6]=b[5];a[6]=a[5];
   srand(time(0));
  /* for(int i=0;i<=N-1;i++)               //这里交换顺序在密码学中应该会应用到,但在工程领域有时是不喜欢做交换的
   {
   int t=rand()%N;
   char tmp;  int tmp1;
   tmp=b[i];  b[i]=b[t]; b[t]=tmp;
   tmp1=a[i]; a[i]=a[t]; a[t]=tmp1;
   }*/
  out.open("d:\\c++\\数独12\\question\\question474.txt");
  for(int i=0;i<=N-1;i++)                 //可有可无
   out<<b[i]<<" ";
  out<<endl;
  select(a,1,9,1);
  P(c, c+4, c+4);
  printf("%d\n",count);
  system("pause");
}
运行结果

A B B D D F F H I J
   A    B    B    D    D    F    F    H    I    J
   A    B    B    D    F    D    F    H    I    J
   A    B    B    F    D    D    F    H    I    J
   A    B    D    B    D    F    F    H    I    J
   A    B    D    B    F    D    F    H    I    J
   A    B    D    D    B    F    F    H    I    J
   A    B    D    D    F    B    F    H    I    J
   A    B    D    F    B    D    F    H    I    J
   A    B    D    F    D    B    F    H    I    J
   A    B    F    B    D    D    F    H    I    J
   A    B    F    D    B    D    F    H    I    J
   A    B    F    D    D    B    F    H    I    J
   A    D    B    B    D    F    F    H    I    J
   A    D    B    B    F    D    F    H    I    J
   A    D    B    D    B    F    F    H    I    J
   A    D    B    D    F    B    F    H    I    J
   A    D    B    F    B    D    F    H    I    J
   A    D    B    F    D    B    F    H    I    J
   A    D    D    B    B    F    F    H    I    J
   A    D    D    B    F    B    F    H    I    J
   A    D    D    F    B    B    F    H    I    J
   A    D    F    B    B    D    F    H    I    J
   A    D    F    B    D    B    F    H    I    J
   A    D    F    D    B    B    F    H    I    J
   A    F    B    B    D    D    F    H    I    J
   A    F    B    D    B    D    F    H    I    J
   A    F    B    D    D    B    F    H    I    J
   A    F    D    B    B    D    F    H    I    J
   A    F    D    B    D    B    F    H    I    J
   A    F    D    D    B    B    F    H    I    J


 

//递归  部分排列(包含全排列)这里不能做A(M,N) M!=N,本质上接近字典序(可随机生成一个种子), 元素可以重复(也可不重复)

#include<stdio.h>
#include<time.h>
#include<cstdlib>
#include<windows.h>
#include<fstream>
#include<iostream>
#define N  5
using namespace std;
int count1=0;                                         //统计排列的个数
ofstream out;
void Perm(int list[], int k, int m)                     //对list[k]——list[m]进行全排列(可以有重复元素)
{                                                       //这里可以改动成对k到m的奇数(偶数引申到给出一个公式即一种顺序)下标进行排列  
     int i;
     int tmp,t;
     if (k == m)
  {
        for (i = 0; i <= N-1; i++)                     //这里用N-1就要输出所有数,排列的和没参加排列的,当然可以改成只输出参加排列的
   out<<list[i]<<" ";
  out<<endl;
  count1++;
     }
    else
        for (i=k; i <= m; i++)
  {
     for(t=k;t<=i-1;t++)
      if(list[i]==list[t])
       break;
     if(t==i)
     {
      tmp=list[k]; list[k]=list[i]; list[i]=tmp;
      Perm (list, k+1, m);
      tmp=list[k]; list[k]=list[i]; list[i]=tmp;
     }
  }
}
void main()
{
 int d[N]={1,2,2,2,3};
 srand(time(NULL));
 for(int i=0;i<=N-1;i++)                                  //生成一个原始排列(任意)
 {
  int tmp,t=rand()%5;
  tmp=d[i]; d[i]=d[t];d[t]=tmp;
 }
 out.open("d:\\c++\\数独12\\question\\question474.txt");
 long dwStart=GetTickCount();
 Perm(d, 0, 4);
 printf("%d毫秒\n",GetTickCount()-dwStart);
 printf("%d个排列\n",count1);
 system("pause");
}

 下面这个代码是对上面的代码的改进,可以做A(M,N) M!=N, 字典序(但要知道以啥序为标准),可以对任意字符进行排列,代码没有演示有重复元素的情况

#include<stdio.h>
#include<time.h>
#include<cstdlib>
#include<windows.h>
#include<fstream>
#include<iostream>
#define N  8
using namespace std;
int count1=0;                                         //统计排列的个数
ofstream out;
int d[N];
char b[N];
void move1(int list[],int a,int b)
{
 int tmp=*(list+b);
 for(int i=b-1;i>=a;i--)
  *(list+i+1)=*(list+i);
 *(list+a)=tmp;
}
void move2(int list[],int a,int b)
{
 int tmp=*(list+a);
 for(int i=a+1;i<=b;i++)
  *(list+i-1)=*(list+i);
 *(list+b)=tmp;
}
void Perm(int list[], int s, int n,int e)                     //对list[k]——list[m]进行全排列(可以有重复元素)
{                                                       //这里可以改动成对k到m的奇数(偶数引申到给出一个公式即一种顺序)下标进行排列  
     int i,t;
     if (s == n)
  {
        for(i=s;i<=e;i++)
  {
     for(t=n;t<=i-1;t++)
      if(list[i]==list[t])
       break;
     if(t==i)
     {
      move1(list,n,i);
         for (int j = 0; j <= N-1; j++)                     //这里用N-1就要输出所有数,排列的和没参加排列的,当然可以改成只输出参加排列的
       out<<b[list[j]]<<" "; 
         out<<"  "<<count1<<endl;
         count1++;
               move2(list,n,i);
     }
  }
     }
    else
        for (i=s; i <= e; i++)
  {
     for(t=s;t<=i-1;t++)
      if(list[i]==list[t])
       break;
     if(t==i)
     {
      move1(list,s,i);
      Perm(list,s+1,n,e);
      move2(list,s,i);
     }
  }
}
void main()
{
 for(int i=0;i<=N-1;i++)
 {
  d[i]=i;
  b[i]=i+48;
 }
 srand(time(NULL));
 for(int i=0;i<=N-1;i++)                                  //生成一个原始排列(任意)
 {
  int tmp,t=rand()%5;
  char tmp1;
  tmp=d[i]; d[i]=d[t];d[t]=tmp;
  tmp1=b[i];b[i]=b[t];b[t]=tmp1;
 }
 out.open("d:\\c++\\数独12\\question\\question474.txt");
 long dwStart=GetTickCount();
 Perm(d, 0,3, 7);
 printf("%d毫秒\n",GetTickCount()-dwStart);
 printf("%d个排列\n",count1);
 system("pause");
}

递归,字典序,重复元素,全排列(可改为重复排列) 

#include <stdlib.h>

#include <stdio.h>

#include <memory.h>

#include<cstdlib>

#include<iostream>

using namespace std;

int count=0;

//生成有重复元素的全排列

//作者:liangbch@263.net, 2008-11-14

 

//#define _MY_DEBUG

 

#ifdef _MY_DEBUG

#define MAX_LEN  8

#else

#define MAX_LEN  256  //最多可以生成256个数的全排列

#endif

 

typedef struct _pair

{

    int v;    //value

    int c;    //count

}PAIR;

 

void printPermutation( int data[],int len)

// len: data 数组的长度

{

    int i;

    for (i=0;i<len;i++)

    {

        if (i==0)

            printf("%d",data[i]);

        else

            printf(",%d",data[i]);

    }

    printf("n");

}

 

void permutation( int newData[],

                 PAIR arrCount[],

                 int len,int level)

// len:countArray 数组元素的个数

// level: newData 数组已被填充的元素的个数

{

    int i,j;

    bool bFind=false;

    PAIR tArray[MAX_LEN];

    for (i=0;i<len;i++)

    {

        for (j=0;j<len;j++)    //复制数组从arrCounttArray

            tArray[j]=arrCount[j];

 

        if ( tArray[i].c>0)

        {

            bFind=true;

            newData[level]= tArray[i].v;

            tArray[i].c--;

#ifdef _MY_DEBUG

            printf("#tArray[%d].c=%dn",i,tArray[i].c);

            printf("#new item= data[%d]=%dn",level,tArray[i].v);

#endif

             permutation( newData,tArray,len,level+1);

        }

    }

    if (!bFind)

    {

       k printPermutation(newData,level);

                   count++;

    }

}

 

//输出data各个元素的一个全排列

//数组data中的各个元素必须以非递减排序,数组中元素可以重复

//假如有n个元素,则这个算法的空间复杂度为n*n,递归深度为n

void printAllPermutation(int data[],int n)

{

    PAIR countArray1[MAX_LEN];

    int newData[MAX_LEN];

    int i,j;

   

    //根据data初始化countArray1

    countArray1[0].v=data[0];

    countArray1[0].c=1;

    for (j=0,i=1;i<n;i++)

    {

        int k;

                   for( k=0;k<=i-1;k++)

                            if(data[i]== countArray1[k].v)

                            {countArray1[k].c++; break;}

                   //if ( data[i]== countArray1[j].v)

        //{

          //  countArray1[j].c++;

        //}

        if(k==i)

        {

            j++;

            countArray1[j].v=data[i];

            countArray1[j].c=1;

        }

    }

    permutation( newData,countArray1,j+1,0);

}

 

int main(int argc, char* argv[])

{

    //int a[]={2,3,5};

    int a[]={1,1,2,2,3,3,4,4,5,5};

    //int a[]={2,2,3,3,5};

    printAllPermutation(a,sizeof(a)/sizeof(int));

         cout<<count<<endl;

         system("pause");

    return 0;

}

原创粉丝点击