康托展开

康托展开公式:
把一个整数X展开成如下形式:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!
其中,a为整数,并且0<=a[i]<i(1<=i<=n)
应用:
{1,2,3,4,...,n}表示1,2,3,...,n的排列
如 {1,2,3} 按从小到大排列一共6个。分别是是s1{1, 2, 3} s2{1, 3, 2} s3{2, 1,,3} s4{2, 3, 1} s5{3, 1, 2} s6{3, 2, 1} 。
康托展开就是要把10进制数与与其中的排列一一对应起来。
那么s3{2, 1, 3}的康托展开式是什么呢。
因为{1,2,3}有三个数字,所有n = 3,则s2的可以用康托展开式表示为:
X(s3) = a3 * (2)! + a2 * (1)! + a1 * (0)!;
只要求出a3,a2,a1即可,就是s2中对应数字在排列{1,2,3}中是第几个小的数,也就是出数字本身比它数还小的个数。
2在{2, 1, 3}比它小的只有数字1,所以a3 = 1,
1在{1, 3}比它小的数字没有,所以a2 = 0,(2已经计算),
3在{3}比它小的数字也没有,所以a1 = 0。(1,2,已经计算过)
所以:X(s3) = 1 * (2)! + 0 * (1)! + 0 * (0)! = 2;
依次可得:
X(s1) = 0 * (2)! + 0 * (1)! + 0 * (0)! = 0;
X(s2) = 0 * (2)! + 1 * (1)! + 0 * (0)! = 1;
X(s3) = 1 * (2)! + 0 * (1)! + 0 * (0)! = 2;
X(s4) = 1 * (2)! + 1 * (1)! + 0 * (0)! = 3;
X(s5) = 2 * (2)! + 0 * (1)! + 0 * (0)! = 4;
X(s6) = 2 * (2)! + 1 * (1)! + 0 * (0)! = 5;
 
知道了X康托展开通过辗转相除法就可以求出对应排列,
比如:X(s5) = 4。
则a3 = 4 / (2)! = 2, 余数为0,
   a2 = 0 / (1)! = 0, 余数为0,
 a1 = 0 / (0)! = 0, 余数为0。
通过康托展开就可以得到全排列。
康托展开代码:
 1 #define ll long long
 2 ll fac[]={1,1,2,6,24,120,720,5040,40320,362880};//阶乘表
 3 ll cantor(int s[], int n)
 4 {
 5     int i, j, t;
 6     ll result = 0;
 7     for (i = 0; i < n - 1; i++)//最后一个可以不用计算 
 8     {
 9         t = 0;//比s[i]小的个数 
10         for (j = i + 1; j < n; j++)
11             if (s[i] > s[j])
12                 t++;
13         result += t * fac[n - i -1];
14     }
15     return result;
16 }

康托逆展开代码:

1 ll cantorInverse(ll result, int s[], int n)
2 {
3     int i;
4     for (i = n - 1; i >= 0; i--)
5     {
6         s[i]  = result / fac[i];
7         result %= fac[i];  
8     }
9 }
原文地址:https://www.cnblogs.com/jecyhw/p/3525483.html