简述:康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,

   康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。

康拓展开

X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! ,其中a[i]为当前未出现的元素中是排在第几个(从0开始)

举例:在(1,2,3,4,5)5个数的排列组合中,计算 34152的康托展开值为61

代码:

#include<iostream> 
#include<cstdio>
#include<cstring>
using namespace std;
long int factory[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
long contor(char str[], int n) {
    long result = 0;
    for(int i = 0; i < n; i++) {
        long count = 0; 
        for(int j = i+1; j < n; j++) {
            if(str[j] < str[i]) count++; 
        }
        result += count*factory[n-i-1]; 
    } 
    return result; 
}
int main() {
    char str[100];
    cin >> str;
    cout << contor(str, strlen(str));
    return 0;
} 

逆康托展开

在(1,2,3,4,5)给出61可以算出起排列组合为 34152。由上述的计算过程可以容易的逆推回来,具体过程如下:

  • 用 61 / 4! = 2余13,说明a[5]=2,说明比首位小的数有2个,所以首位为3。
  • 用 13 / 3! = 2余1,说明a[4]=2,说明在第二位之后小于第二位的数有2个,所以第二位为4。
  • 用 1 / 2! = 0余1,说明a[3]=0,说明在第三位之后没有小于第三位的数,所以第三位为1。
  • 用 1 / 1! = 1余0,说明a[2]=1,说明在第二位之后小于第四位的数有1个,所以第四位为5。
  • 最后一位自然就是剩下的数2啦。
  • 通过以上分析,所求排列组合为 34152。

代码:

#include<iostream>  
#include<cstring>  
#include<vector>
#include<algorithm>
using namespace std;  
long int factory[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; 
void decontor(int x, int n)  {
    vector<int> v;
    vector<int> a;
    for(int i = 1; i <= n; i++) v.push_back(i);
    sort(v.begin(), v.end());
    for(int i = n-1; i >= 0; i--) {  
        int r = x % factory[i];
        int t = x / factory[i];
        x = r;
        a.push_back(v[t]);
        v.erase(v.begin()+t);
    }
    for(int i = 0; i < a.size(); i++) cout << a[i];                          
}  
int main()  
{  
      int x, n;
    cin >> x >> n;
    decontor(x, n);   
    return 0;  
}  

 

posted on 2018-04-09 00:11  kindleheart  阅读(127)  评论(0编辑  收藏  举报