// 非递归 字典序(可变为随机序),元素可以重复也可不重复,部分排列(包含全排列)部分排列分为从一个数组中取部分元素进行全排列,或取部分元素做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++) //复制数组从arrCount到tArray
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;
}