• 康托展开 / 逆康托展开


      先搬一下(戳)维基百科的康托展开(戳):

      康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

      由于是双射    所以可以求n的全排列里第k大的排列(逆康托展开)

      (伪)计算原理: 从某个元素找后面比这个元素小的数的个数,再乘以这个位置每一个数字能有的组合方法数(排列 / 阶乘),得出只考虑从这一位开始到末尾比当前小的排列数,然后加起来就是康托展开求的数(追求难懂的巅峰...........看不懂就看看维基.........

        公式:    

            ai 是整数 且 0 <= a< i ,  1 <= i <= n 

            ai 的意义:参悟栗子吧

        栗子:

        例如,3 5 7 4 1 2 9 6 8 展开为 98884。
            因为X=2*8!+3*7!+4*6!+2*5!+0*4!+0*3!+2*2!+0*1!+0*0!=98884.
        解释:
            排列的第一位是3,比3小的数有两个,以这样的数开始的排列有8!个,因此第一项为2*8!
            排列的第二位是5,比5小的数有1、234,由于3已经出现,因此共有3个比5小的数,这样的排列有7!个,因此第二项为3*7!
            以此类推,直至0*0!

      再贴一下(戳)NOCOW关于康托展开的一页(戳),有代码,看得懂就差不多了

      逆康托展开并不是康托展开完全逆过来:

        1.减去1,得到比该排列小的排列的数量

        2.从高位算起:取摸对应位的阶乘,得到在这一位后面比这一位小的数的个数(所以要注意前面比这一位的数小的要去除)

        

        栗子:

        如n=5,x=96时:
            首先用96-1得到95,说明x之前有95个排列.(将此数本身减去!)
            用95去除4! 得到3余23,说明有3个数比第1位小,所以第一位是4.
            用23去除3! 得到3余5,说明有3个数比第2位小,所以是4,但是4已出现    过,因此是5.
            用5去除2!得到2余1,类似地,这一位是3.
            用1去除1!得到1余0,这一位是2.
            最后一位只能是1.
            所以这个数是45321.

      

      再贴上忘了什么时候写的代码:

     

     1 typedef long long ll;
     2 const ll factorial[11] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800};
     3 int num[101];
     4 ll Cantorex(int n){
     5     for(int i = 0; i < n; ++i) cin >> num[i];
     6     ll sum = 0;
     7     for(int i = 0; i < n; ++i){
     8         int t = 0;
     9         for(int j = i + 1; j < n; ++j) if(num[j] < num[i]) ++t;
    10         sum += t * factorial[n-i-1];
    11     }
    12     return sum;
    13 }
    14 ll uCantorex(ll sum, int n){
    15     memset(num, 0, sizeof(num));
    16     sum -= 1;
    17     int x = 0;
    18     ll ans = 0;
    19     for(int i = n - 1; i > 0; --i){
    20         ll k = sum / factorial[i] + 1;
    21         sum %= factorial[i];
    22         while(num[k]) ++k;
    23         num[k] = 1;
    24         ans = ans * 10 + k;
    25     }
    26     for(int i = 1; i <= n; ++i)
    27         if(!num[i]) ans = ans * 10 + i;
    28     return ans;
    29 }

      Done!

  • 相关阅读:
    【并行计算-CUDA开发】CUDA shared memory bank 冲突
    【并行计算-CUDA开发】CUDA shared memory bank 冲突
    【并行计算-CUDA开发】CUDA bank conflict in shared memory
    【并行计算-CUDA开发】CUDA bank conflict in shared memory
    【并行计算-CUDA开发】从熟悉到精通 英伟达显卡选购指南
    【并行计算-CUDA开发】从熟悉到精通 英伟达显卡选购指南
    【C/C++开发】【VS开发】win32位与x64位下各类型长度对比
    【C/C++开发】【VS开发】win32位与x64位下各类型长度对比
    【并行计算-CUDA开发】浅谈GPU并行计算新趋势
    【并行计算-CUDA开发】浅谈GPU并行计算新趋势
  • 原文地址:https://www.cnblogs.com/book-book/p/5348851.html
Copyright © 2020-2023  润新知