全排列的编码与解码——康托展开

康托展开是一个全排列到一个自然数的映射,可以快速求出一个全排列在所有全排列中字典序排第几

康托展开公式

$\Large X=a_n*(n-1)!+a_{n-1}*(n-2)!+……+a_1*0!$

其中$a_i$表示全排列中后i个元素,第n-i+1个元素排第几(然后要减一)

证明

$a_i*(i-1)!$表示所有全排列中前n-i个元素与当前全排列相同,且第n-i+1个元素比当前全排列小的所有全排列都比当前全排列小

这样所有数相加就代表比当前全排列小的全排列的个数

代码实现

const int fac[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320};//阶乘,不够用可以再加 
int cantor(int a[],int k){//康托展开 
    int ans=0,tmp;
    for(int i=0;i<k;i++){
        tmp=0;//记录有几个比它小的数 
        for(int j=i+1;j<k;j++){
            if(a[j]<a[i])tmp++;
        }
        ans+=tmp*fac[k-i-1];
    }
    return ans;
}
void uncantor(int a[],int k,int num){//逆康托展开
    int b[10];//b表示剩下的数,并且按升序排列 
    for(int i=0;i<k;i++)b[i]=i+1;
    b[k]=0;
    for(int i=0,x;i<k;i++){
        x=num/fac[k-i-1],num%=fac[k-i-1],a[i]=b[x];//x表示当前数是剩下的数中的第几个 
        for(int j=x;b[j];j++){
            b[j]=b[j+1];
        }
    }
}

 

posted @ 2018-01-15 08:49  Bennettz  阅读(376)  评论(0编辑  收藏  举报