康托和逆康托展开(转)

1.康托展开的解释

康托展开就是一种特殊的哈希函数

  把一个整数X展开成如下形式:

  X=a[n]*n!+a[n-1]*(n-1)!+...+a[2]*2!+a[1]*1!

  其中,a为整数,并且0<=a<i,i=1,2,..,n

  {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。

  代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。

  他们间的对应关系可由康托展开来找到。

  如我想知道321是{1,2,3}中第几个大的数可以这样考虑 :

  第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个
。所以321是第6个大的数。 2*2!+1*1!是康托展开。

  再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以
有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。

  康托展开的代码(C语言):
  

//参数int s[]为待展开之数的各位数字,如需展开2134,则s[4]={2,1,3,4}.
int fac[]= {1,1,2,6,24,120,720,5040,40320,362880}; //...
long cantor(int s[],int n){
    int i,j,temp,num;
    num=0;
    for(i=1;i<n;i++){//n为位数
        temp=0;
        for(int j=i+1;j<=n;j++){
            if(s[j]<s[i])temp++; 
        }
        num+=fac[n-i]*temp;
    }
    return num+1;
}

康托展开的逆运算
  例 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕

  (1)找出第96个数

  首先用96-1得到95

  用95去除4! 得到3余23

  用23去除3! 得到3余5

  用5去除2!得到2余1

  用1去除1!得到1余0有3个数比它小的数是4

  所以第一位是4

  有3个数比它小的数是4但4已经在之前出现过了所以是5(因为4在之前出现过了所以实际比5小的数是3个)

  有2个数比它小的数是3

  有1个数比它小的数是2

  最后一个数只能是1

  所以这个数是45321

  (2)找出第16个数

  首先用16-1得到15

  用15去除4!得到0余15

  用15去除3!得到2余3

  用3去除2!得到1余1

  用1去除1!得到1余0

  有0个数比它小的数是1

  有2个数比它小的数是3 但由于1已经在之前出现过了所以是4(因为1在之前出现过了所以实际比4小的数是2)

  有1个数比它小的数是2 但由于1已经在之前出现过了所以是3(因为1在之前出现过了所以实际比3小的数是1)

  有1个数比它小得数是2 但由于1,3,4已经在之前出现过了所以是5(因为1,3,4在之前出现过了所以实际比5小的数是1)

  最后一个数只能是2

  所以这个数是14352

时间: 2024-09-30 16:55:33

康托和逆康托展开(转)的相关文章

康托展开和逆康托展开

问题:给定的全排列,计算出它是第几个排列? 对于全排列,不清楚的可以参考全排列 方法:康托展开 对于一个长度为 n 的排列 num[1..n], 其序列号 X 为 X = a[1]*(n-1)! + a[2]*(n-2)! +...+ a[i]*(n-i)! +...+ a[n-1]*1! + a[n]*0! 其中a[i]表示在num[i+1..n]中比num[i]小的数的数量 写做伪代码为: Cantor(num[]) X = 0 For i = 1 .. n tp = 0 For j = i

nyist 139 我排第几个&amp;&amp;143 第几是谁(康托展开和逆康托展开)

 我排第几个 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 现在有"abcdefghijkl"12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的? 输入 第一行有一个整数n(0<n<=10000); 随后有n行,每行是一个排列: 输出 输出一个整数m,占一行,m表示排列是第几位: 样例输入 3 abcdefghijkl hgebkflacdji gfkedhjblcia 样例输出 1 3027

康托展开 / 逆康托展开

先搬一下(戳)维基百科的康托展开(戳): 康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩. 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的. 由于是双射    所以可以求n的全排列里第k大的排列(逆康托展开) (伪)计算原理: 从某个元素找后面比这个元素小的数的个数,再乘以这个位置每一个数字能有的组合方法数(排列 / 阶乘),得出只考虑从这一位开始到末尾比当前小的排列数,然后加起来就是康托展开求的数(追求难懂的巅峰...........看不懂就看看维

LightOJ1060 nth Permutation(不重复全排列+逆康托展开)

一年多前遇到差不多的题目http://acm.fafu.edu.cn/problem.php?id=1427. 一开始我还用搜索..后来那时意外找到一个不重复全排列的计算公式:M!/(N1!*N2!*...*Nn!), 然后就靠自己YY出解法,搞了好几天,最后向学长要了数据,然后迷迷糊糊调了,终于AC了. 后来才知道当时想的解法类似于逆康托展开,只是逆康托展开是对于没有重复元素全排列而言,不过有没有重复元素都一个样. 而现在做这题很顺,因为思路很清晰了,另外这做法和数论DP的统计部分有相似之处.

nyoj 139——我排第几个|| nyoj 143——第几是谁? 康托展开与逆康托展开

讲解康托展开与逆康托展开.http://wenku.baidu.com/view/55ebccee4afe04a1b071deaf.html #include<bits/stdc++.h> using namespace std; int fac[20]; int fun(){ fac[0]=1; int i; for(i=1;i<=12;i++){ fac[i]=fac[i-1]*i; } } int main(){ int t,i,j,c,sum,num; char str[15];

数据结构——康托展开与逆康托展开

含义 康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩. 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的. 原理 X = s1(n-1)! + s2(n-2)! + s3(n-3)! + …… + sn-1 * 1! + sn * 0! 其中si表示在第i位右边比ai小的数的个数. 我们现在用sl表示第i位左边比ai小的数的个数,sr表示第i位右边比ai小的数的个数,显然可以得到如下等式: ai = sl + sr + 1 故公式中的si可以用上述等式

hdu 1027 Ignatius and the Princess II(正、逆康托)

题意: 给N和M. 输出1,2,...,N的第M大全排列. 思路: 将M逆康托,求出a1,a2,...aN. 看代码. 代码: int const MAXM=10000; int fac[15]; int ans[1005]; int kk; int n,m; vector<int> pq; int main(){ int cn=0; fac[0]=1; while(1){ ++cn; fac[cn]=fac[cn-1]*cn; if(fac[cn]>MAXM){ --cn; break

康拓展开与逆康拓展开

1.康托展开的解释 康托展开就是一种特殊的哈希函数 把一个整数X展开成如下形式: X=a[n]*n!+a[n-1]*(n-1)!+...+a[2]*2!+a[1]*1! 其中,a为整数,并且0<=a<i,i=1,2,..,n {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个.123 132 213 231 312 321 . 代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来. 他们间的对应关系可由康托展开来找到.

康拓展开和逆康拓展开

康拓展开和逆康拓展开 康拓展开模板题 复杂度O(\(n^2\))的会tle(看数据就知道了)(虽然某题解说可以,不知道是不是后期加强了数据 然而我还是写了O(\(n^2\))的 #include <cstdio> typedef long long LL; LL f[1000010]; const LL mod = 998244353; int a[1000010], b[1000010]; int main() { f[0] = 1; for(int i = 1; i < 100000