康托展开和逆康托展开

康托展开

康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是在n个不同元素的全排列中, 比当前排列组合小的个数,那么也可以表示当前排列组合在n个不同元素的全排列中的名次(当前的名次 = 比当前排列组合小的个数 + 1)。

在(1,2,3,4,5)5个数的排列组合中,计算 34152的康托展开值。
根据公式:
X = 2 * 4! + 2 * 3! + 0 * 2! + 1 * 1! + 0 * 0! = 2 * 24 + 2 * 6 + 1 = 61
所以比 34152 小的组合有61个,即34152是第62个排列数

def getPermutation(n:int,num:list[int]) -> int :
    fac = [1]
    for i in range(1, n + 1):
        fac.append(fac[-1] * i)
    ans = 0
    for i in range(n):
        samller = 0
        for j in range(i + 1,n):
            if num[j] < num[i]:
                samller += 1
        ans += fac[n - i - 1] * samller
    return ans + 1

tips: 这里主要为了讲解康托展开的思路,实现的算法复杂度为O(n^2),实际当n很大时,内层循环计算在当前位之后小于当前位的个数可以用 线段树来处理计算,而不用每次都遍历,这样复杂度可以降为O(nlogn)。

逆康托展开

即对于上述例子,在(1,2,3,4,5)给出61可以算出起排列组合为 34152。
因为34152是第62个排列数,减一才是真正的逆康托展开

## 逆康托展开
    def getPermutation(self, n: int, k: int) -> str:
        fac = [1]
        for i in range(1,n + 1):
            fac.append(fac[-1] * i)
        num = [i for i in range(1,n + 1)]
        ans = list()
        k -= 1
        for i in range(n,0,-1):
            pre = k % fac[i - 1]
            x = k // fac[i - 1]
            k = pre
            ans.append(str(num[x]))
            num.pop(x)
        return "".join(ans)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值