康托展开公式
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}中第几个大的数。
这样考虑:
- 第一位是3,小于3的数有1、2 。所以有2*2!个。
- 再看小于第二位,小于2的数只有一个就是1 ,所以有1*1!=1
- 所以小于32 的{1,2,3}排列数有22!+11!=5个。所以321是第6个大的数。
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)); } }