康托展开与逆康托展开
1 康托展开
1.1 定义
康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。
之所以说是空间压缩,是因为当我们进行哈希运算时,如果是一个正常的长度为$n$数列,数的范围在$1 \to n$ 的情况下,这个数列总共存在的情况有$n^n$种,我们将数列映射为整数时,则至少需要$n^n$这个数量级以上的空间
而$n$的全排列很明显只有$n!$种情况,也就是说,会没有必要的浪费很多空间,如何将空间降低呢,我们就需要用到康托展开与逆康托展开
举个例子,当$n == 9$时,普通哈希算法需要的空间为$9 ^ 9 = 387420489$,而康托展开只需要$9! = 3682880$的空间即可
康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。
1.2 康托展开运算
其中$a_i$为$0 \le a_i < i$的非负整数,$1 \le i \le n$
$a_i$ 表示原数的第$i$位在当前未出现的元素中是排在第几个(也可以理解为位置$i$后面的数有多少个是小于第$i$位的数)
1.3 举个例子
在$(1, 2, 3, 4)$的全排列中,我们计算排列 $(2, 4, 1, 3)$是第几小的排列
此时我们可以发现 2 后面只有一个比它小的数
所以$a_4 = 1$,同理$a_3 = 2, a_2 = 0, a_1 = 0$
代入公式则可以计算出此时
所以有10个排列比该排列小,该排列的次序应为11
1.4 实现技巧
统计当前未出现的元素的排的名次时,我们可以采用不同的方式
当$n$很小是,我们可以按顺序暴力遍历所有元素,查看是否出现,统计其名次
当$n$比较大时,我们可以考虑通过线段树来查询
2 逆康托展开
既然康托展开是一个全排列到一个自然数的双射,那么我们一定可以通过它的名次,来确定它的具体排列情况。
假设我们想知道$5$的全排列中的第$34$小的排列
我们知道,有$33$个排列小于它。我们便可以通过上述运算过程的逆过程推出该排列的具体内容
因此$a_5 = 1$即未出现的数字中有一个小于它,即它是$2$
因此$a_4 = 1$即未出现的数字中有一个小于它,即它是$3$
因此$a_3 = 2$即未出现的数字中有两个小于它,即它是$5$
因此$a_2 = 0$即未出现的数字中有零个小于它,即它是$1$
最后一位显然是 $4$
因此该排列为 $(2, 3, 5, 1, 4)$
3 代码实现
1 | int a[12], fac[12]; |
4 应用价值
4.1 快速确定某排列的名次
4.2 知道名次快速确定排列的内容
4.3 压缩哈希空间减低空间复杂度
5 经典例题(八数码)
其中经典例题
hdu1043 Eight 八数码
给定一个现有的九宫格布局,请输出将它移动至初始状态的移动方法的步骤
便是利用康托展开加bfs解决的
1 | /* *********************************************** |