康托展开公式
X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
康托展开. {1...n}的全排列由小到大有序,s[]为第几个数
{1,2,3,4,...,n}的排列总共有n!种,将它们从小到大排序,怎样知道其中一种排列是有序序列中的第几个?
如 {1,2,3} 按从小到大排列一共6个:123 132 213 231 312 321。想知道321是{1,2,3}中第几个大的数。
这样考虑:
22!+11!是康托展开。
康托展开的逆运算. {1...n}的全排列,中的第k个数为s[] {1,2,3,4,5}的全排列已经从小到大排序,要找出第16个数:
1. 首先用16-1得到15
2. 用15去除4! 得到0余15
3. 用15去除3! 得到2余3
4. 用3去除2! 得到1余1
5. 用1去除1! 得到1余0
6. 有0个数比它小的数是1所以第一位是1
7. 有2个数比它小的数是3,但1已经在之前出现过了所以是4
8.有1个数比它小的数是2,但1已经在之前出现过了所以是3
9.有1个数比它小的数是2,但1,3,4都出现过了所以是5
最后一个数只能是2
所以这个数是14352
public class KangTuo {
/**
* 康托展开:X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
* ai为整数,并且0<=ai<i(1<=i<=n)
*/
private int fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的阶乘为fac[i]
/*
* 康托展开. {1...n}的全排列由小到大有序,s[]为第几个数
* {1,2,3,4,...,n}的排列总共有n!种,将它们从小到大排序,怎样知道其中一种排列是有序序列中的第几个?
*
* 如 {1,2,3} 按从小到大排列一共6个:123 132 213 231 312 321。想知道321是{1,2,3}中第几个大的数。
* 这样考虑:第一位是3,小于3的数有1、2 。所以有2*2!个。再看小于第二位,小于2的数只有一个就是1 ,所以有1*1!=1 所以小于32
*
* 的{1,2,3}排列数有2*2!+1*1!=5个。所以321是第6个大的数。2*2!+1*1!是康托展开。
*/
public int KangTuo(int n, int s[])
{
int sum = 0;
for(int i = 0; i < n; i++){
int t = 0;
for(int j = i+1; j < n;j++){
if(s[i] > s[j]){
t++;
}
}
sum += t *fac[n-i-1];
}
return sum+1;
}
/*
* 康托展开的逆运算. {1...n}的全排列,中的第k个数为s[] {1,2,3,4,5}的全排列已经从小到大排序,要找出第16个数:
*
* 1. 首先用16-1得到15
*
* 2. 用15去除4! 得到0余15
*
* 3. 用15去除3! 得到2余3
*
* 4. 用3去除2! 得到1余1
*
* 5. 用1去除1! 得到1余0
*
* 有0个数比它小的数是1
*
* 所以第一位是1
*
* 有2个数比它小的数是3,但1已经在之前出现过了所以是4
*
* 有1个数比它小的数是2,但1已经在之前出现过了所以是3
*
* 有1个数比它小的数是2,但1,3,4都出现过了所以是5
*
* 最后一个数只能是2
*
* 所以这个数是14352
*/
public void invKT(int n, int k, int s[]) {
int i, j, t;
int[] vst = new int[8];
k--;
for (i = 0; i < n; i++) {
t = k / fac[n - i - 1];
for (j = 1; j <= n; j++)
if (0 == vst[j]) {
if (t == 0)
break;
t--;
}
s[i] = j;
vst[j] = 1;
k %= fac[n - i - 1];
}
}
/**
* @param args
*/
public static void main(String[] args) {
// int[] s = new int[]{1,2,4};
int[] s = new int[]{3,2,1};
System.out.println(new KangTuo().KangTuo(s.length, s));
}
}