zoukankan      html  css  js  c++  java
  • 康托展开及其逆运算

    转载 https://blog.csdn.net/zhongkeli/article/details/6966805
    https://blog.csdn.net/lttree/article/details/24798653

    康托展开的公式是 X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0! 其中,ai为当前未出现的元素中是排在第几个(从0开始)。
      
      这个公式可能看着让人头大,最好举个例子来说明一下。
      例如,有一个数组 s = [“A”, “B”, “C”, “D”],它的一个排列 s1 = [“D”, “B”, “A”, “C”],现在要把 s1 映射成 X。n 指的是数组的长度,也就是4,所以
    X(s1) = a4*3! + a3*2! + a2*1! + a1*0!
    关键问题是 a4、a3、a2 和 a1 等于啥?
    a4 = “D” 这个元素在子数组 [“D”, “B”, “A”, “C”] 中是第几大的元素。”A”是第0大的元素,”B”是第1大的元素,”C” 是第2大的元素,”D”是第3大的元素,所以 a4 = 3。
    a3 = “B” 这个元素在子数组 [“B”, “A”, “C”] 中是第几大的元素。”A”是第0大的元素,”B”是第1大的元素,”C” 是第2大的元素,所以 a3 = 1。
    a2 = “A” 这个元素在子数组 [“A”, “C”] 中是第几大的元素。”A”是第0大的元素,”C”是第1大的元素,所以 a2 = 0。
    a1 = “C” 这个元素在子数组 [“C”] 中是第几大的元素。”C” 是第0大的元素,所以 a1 = 0。(因为子数组只有1个元素,所以a1总是为0)
    所以,X(s1) = 3*3! + 1*2! + 0*1! + 0*0! = 20

    A B C | 0
    A C B | 1
    B A C | 2
    B C A | 3
    C A B | 4
    C B A | 5

    康托展开是什么?

    定义:

    X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!

    ai为整数,并且0<=ai

    康托展开有啥用呢?

    维基:n位(0~n-1)全排列后,其康托展开唯一且最大约为n!,因此可以由更小的空间来储存这些排列。由公式可将X逆推出对应的全排列。

    它可以应用于哈希表中空间压缩,

    而且在搜索某些类型题时,将VIS数组量压缩。比如:八数码、魔板。。

    康托展开求法:

    比如2143 这个数,求其展开:

    从头判断,至尾结束,

    ① 比 2(第一位数)小的数有多少个->1个就是1,1*3!

    ② 比 1(第二位数)小的数有多少个->0个0*2!

    ③ 比 4(第三位数)小的数有多少个->3个就是1,2,3,但是1,2之前已经出现,所以是 1*1!

    将所有乘积相加=7

    比该数小的数有7个,所以该数排第8的位置。

    1234 1243 1324 1342 1423 1432
    2134 2143 2314 2341 2413 2431
    3124 3142 3214 3241 3412 3421
    4123 4132 4213 4231 4312 4321

    int  fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的阶乘为fac[i]
    // 康托展开-> 表示数字a是 a的全排列中从小到大排,排第几
    // n表示1~n个数  a数组表示数字。
    int kangtuo(int n,char a[])
    {
        int i,j,t,sum;
        sum=0;
        for( i=0; i<n ;++i)
        {
            t=0;
            for(j=i+1;j<n;++j)
                if( a[i]>a[j] )
                    ++t;
            sum+=t*fac[n-i-1];
        }
        return sum+1;
    }

    康托展开的逆:

    康托展开是一个全排列到自然数的双射,可以作为哈希函数。

    所以当然也可以求逆运算了。

    逆运算的方法:

    假设求4位数中第19个位置的数字。

    ① 19减去1 → 18

    ② 18 对3!作除法 → 得3余0

    ③ 0对2!作除法 → 得0余0

    ④ 0对1!作除法 → 得0余0

    据上面的可知:

    我们第一位数(最左面的数),比第一位数小的数有3个,显然 第一位数为→ 4

    比第二位数小的数字有0个,所以 第二位数为→1

    比第三位数小的数字有0个,因为1已经用过,所以第三位数为→2

    第四位数剩下 3

    该数字为 4123 (正解)

    用代码实现上述步骤为:

    int  fac[] = {1,1,2,6,24,120,720,5040,40320};
    //康托展开的逆运算,{1...n}的全排列,中的第k个数为s[]
    void reverse_kangtuo(int n,int k,char s[])
    {
        int i, j, t, vst[8]={0};
        --k;
        for (i=0; i<n; i++)
        {
            t = k/fac[n-i-1];
            for (j=1; j<=n; j++)
                if (!vst[j])
                {
                    if (t == 0) break;
                    --t;
                }
            s[i] = '0'+j;
            vst[j] = 1;
            k %= fac[n-i-1];
        }
    }
  • 相关阅读:
    VIM技巧, .vimrc文件
    vSphere Client克隆虚拟机
    vSphere、 ESXi、Vcenter、vSphere Client关系
    消耗系统内存
    zabbix2.4汉化
    普通用户执行ansible权限被拒绝
    putty秘钥转换成xhell支持的格式
    zbb20170922 mysql 字符集设置 比较 utf8_general_ci、utf8_unicode_ci和utf8_bin的区别
    zbb20170920 页面调用qq
    zbb20170919 tomcat 8 启动异常 Could not publish server configuration for Tomcat v8.0 Server at localhost. Multiple Contexts have a path of "A".
  • 原文地址:https://www.cnblogs.com/bryce1010/p/9386986.html
Copyright © 2011-2022 走看看