康拓展开
康拓展开是一个全排列到自然数的双射,对于hash表的空间压缩很有用,同时在搜索当中也会有一些使用,比如八数码问题。 其实它的操作过程并不复杂。
公式:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+a[1]*0!
其中a[i]表示的是第i位数字后面的数字比第i个数字小的个数。
累加的和就是表示这个全排列前面有多少个全排列,所以这个全排列的位次就是X+1;
以34125为例我们模拟一下整个过程。
第一位是3,所以其后面比3小的个数为2,分别为1,2;a[1]=2;
第二位是4,所以其后面比4小的个数为2,分别为1,2;a[2]=2;
第三位是1,没有比他小的, a[3]=0;
第四位是2,也没有比他小的, a[4]=0;
第五位其后面没有数字所以,a[5]=0;
所以X=2X4!+2X3!+0+0+0=60; 这个全排列前面有60个全排列。
证明这是第61个全排列。
static const int FAC[]={1,1,2,6,24,120,720,5040,40320,362880};
ll cantor(int *a,int n){
ll x=0;
for(int i=0;i<n;++i){
int small=0; //统计在这一位数字后面比这个数字还小的个数
for(int j=i+1;j<n;++j){
if(a[j]<a[i])
small++;
}
x+=FAC[n-i-1]*small; //累加求全排列名次。
}
return x;
}
逆康拓展开
因为康拓展开是一个全排列与自然数相对应的双射所以可以根据全排列求其位次,也可以通过位次求其相应的全排列;
整个实现过程也不难。我们求得了第61个全排列是34125;
所以我们根据这个61反推回去看是否可以得到34125;
第一步:60/4!得2余12,所以后面的数字比第一数字小的数字有两个 ,所以第一个数是3;
第二步:12/3!得2余0,所以后面的数字比第二位数字小的数字有两个,所以第二位是4;
第三步:0/2!得0余0,所以后面的数字比第三个数字小的数字没有,所以第三位只能为1;
与第三步同理可得到,第四位,第五位分别为2和5;
void decantor(int n,int x){
vector<int>vv; //存放当前可选数
vector<int>ve; //所求的排列组合
for(int i=1;i<=n;++i){
vv.push_back(i);
}
for(int i=0;i<n;++i){
ll r,s;
r=x%FAC[n-i-1];
s=x/FAC[n-i-1];
x=r;
sort(vv.begin(),vv.end()); //对可选数排序
ve.push_back(vv[s]); //选择好就放进数组
vv.erase(vv.begin()+s); //删除已经选过的数
}
vector<int>::iterator v;
for(v=ve.begin();v!=ve.end();v++){
printf("%d",*v);
}
cout<<endl;
}
整体代码
//康拓展开求一个全排列是第几个,逆康拓展开是知道是第几个求这个全排列
//以 34125为例;
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
static const int FAC[]={1,1,2,6,24,120,720,5040,40320,362880};
ll cantor(int *a,int n){
ll x=0;
for(int i=0;i<n;++i){
int small=0; //统计在这一位数字后面比这个数字还小的个数
for(int j=i+1;j<n;++j){
if(a[j]<a[i])
small++;
}
x+=FAC[n-i-1]*small; //累加求全排列名次。
}
return x;
}
void decantor(int n,int x){
vector<int>vv; //存放当前可选数
vector<int>ve; //所求的排列组合
for(int i=1;i<=n;++i){
vv.push_back(i);
}
for(int i=0;i<n;++i){
ll r,s;
r=x%FAC[n-i-1];
s=x/FAC[n-i-1];
x=r;
sort(vv.begin(),vv.end()); //对可选数排序
ve.push_back(vv[s]); //选择好就放进数组
vv.erase(vv.begin()+s); //删除已经选过的数
}
vector<int>::iterator v;
for(v=ve.begin();v!=ve.end();v++){
printf("%d",*v);
}
cout<<endl;
}
int main()
{
int a[5]={3,4,1,2,5};
ll can=cantor(a,5)+1; //因为所求结果为比这个全排列小的全排列所以加1表示这个全排列的位次
cout<<can<<endl;
decantor(5,can-1);
}