用途
康托展开是一种双射,用于排列和整数之间的映射,可用于排列的哈希
康托展开
公式:
∑
i
=
n
1
p
i
∗
(
i
−
1
)
!
\sum\limits_{i = n}^{1} p_i*(i - 1)!
i=n∑1pi∗(i−1)! 其中
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=n∑1pi∗(i−1)! = 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);
}
}