康托展开:给每个排列一个整数编号
全部 n! 个排列按字典序排好后,每个都有一个唯一的名次
。康托展开 (Cantor expansion) 把这个名次和排列直接互算: rank 把排列映成序号,unrank 反向解码——无需像枚举那样一个个走。秘诀是把排列看成阶乘进制 (factorial number system) 下的数:第
i 位的权重是
,该位的数位是「右边还没用、且比当前数小的个数」。
本页单步演示 unrank: 拖动序号 k,看它如何逐位被解码成排列。
为什么权重是阶乘? 排列里第一位每变大一档(换成更大的可用数),就跨过了后面 个排列(因为后面 个位置能自由排出这么多种);第二位每变一档跨过 个……依此类推。把「每位的档数 × 该位权重」加起来,正好是这个排列前面有多少个排列——也就是它的字典序名次。
这是一个双射 (bijection)。 rank 与 unrank 互为逆:对任意排列 rank 再 unrank 回到自身,对任意序号反过来也成立。于是排列可以只用一个整数来存储 / 传输 / 比较——需要「字典序第 k 个排列」时,一步 unrank(k) 即得,不必从最小一路 next 过去。
数位的取值范围是变的。 第 i 位的数位 d 只能落在
——最后一位永远是 0(只剩一个候选)。这正是阶乘进制的特征:各位「逢
进一」,而非十进制的逢十。rank 方向则相反:数位 = 当前数右边比它小的个数,乘以权重再求和。
1 · 反过来:单步看 rank(排列 → 序号)
上面的 unrank 把序号解码成排列;rank 是它的逆——把排列编码回序号。逐位扫描:第 i 位的阶乘进制位 d = 它右边比它小的个数,乘以权重
再累加。改下面的排列单步看每位怎么数、怎么累加。