康托展开&逆康托展开

用途

康托展开是一种双射,用于排列和整数之间的映射,可用于排列的哈希

康托展开

公式:
∑ i = n 1 p i ∗ ( i − 1 ) ! \sum\limits_{i = n}^{1} p_i*(i - 1)! i=n1pi(i1)! 其中 p i p_i pi为第 i i i个数构成的逆序的个数,n为排列数的个数
例:

排列:2134
∑ i = n 1 p i ∗ ( i − 1 ) ! \sum\limits_{i = n}^{1} p_i*(i - 1)! i=n1pi(i1)! = 3! * 1 + 2! * 0 + 1! * 0 + 0! * 0 = 6
p 4 = 1 p_4 = 1 p4=1因为在2后面比2小的数只有一个,其它的同理

康托展开实质就是给排列进行了排序

int getHash(){
    int res = 0;
    for(int i = 0; i < n; i++){
        int t = 0;
        for(int j = i + 1; j < n; j++)
            if(a[i] > a[j])t++;
        res += t * f[n - i - 1];
    }
    return res;
}
逆康托展开

排列34152的康托展开是61那么:
以下部分引用了百度百科的部分内容这里
用 61 / 4! = 2余13,说明,说明比首位小的数有2个,所以首位为3。
用 13 / 3! = 2余1,说明,说明在第二位之后小于第二位的数有2个,所以第二位为4。
用 1 / 2! = 0余1,说明,说明在第三位之后没有小于第三位的数,所以第三位为1。
用 1 / 1! = 1余0,说明,说明在第四位之后小于第四位的数有1个,所以第四位为5。
最后一位自然就是剩下的数2。
通过以上分析,所求排列组合为 34152。

void reHash(int x, int n){
    vector<int> v;
    vector<int> a;
    for(int i = 1;i <= n;i++)
        v.push_back(i);
    for(int i = n;i >= 1;i--)
    {
        int r = x % f[i-1];
        int t = x / f[i-1];
        x = r;
        sort(v.begin(),v.end());
        a.push_back(v[t]);
        v.erase(v.begin()+t);
    }
}
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页