Book--康托展开及其逆运算
看了度娘的解释,对Cantor展开式有了一定了解。
和所有算法一样,它最初的想法是不难理解的。
那么康托展开有什么用呢?度娘只给出了一个例子,其实康托展开应该还有其他应用。
例子:列举{1,2,3,4,.....n}中所有数组成排列,如:n=4时,有一个排列 1 4 2 3
Cantor的应用在于判断某个排列在所有排列中是第几大(小)的
{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!是康托展开。
再举个例子: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是第三个大数
int fac ( int x ) { if ( x == 0 ) return 1; else return x * fac( x - 1 ); } int fac[ ] = {1,1,2,6,24,120,720,5040,40320};//可以自己写表也可以选择写fac函数写个表(如上) int Cantor(int n , int s[ ] ) { int sum = 0; for ( int i = 0 ; i < n ; i ++ ) { int count = 0 ; for ( int j = i + 1 ; j < n ; j ++ ) { if ( s[ j ] < s[ i ] ) count ++;//判断后面的数有几个小于自己,精巧之处:无需再开used数组判断是否用过。 } sum += t * fac[ n - i - 1 ];//从第一个数开始判断,阶乘递减 } return sum+1;//sum为比该数小的数的个数,所以返回sum+1. }
康托展开式另一个功能---逆运算也非常有用
例子:还是一个{1,2,3,4....n}的全排列,问:第n个数是几?
举个例子便很明了。14352这个数({1,2,3,4,5})
正运算:0*4! + 2*3! + 1*2! + 1*1! = 15。
15+1 = 16.
所以14352是第16大的数。
逆运算:第16大的数是几?
直接反过来算:
16-1 = 15.
15除以4! = 0 余 15
15除以3! = 2 余 3
3除以2! = 1 余 1
1除以1!= 1 余 0
所以第一位是1
有2个数比它小的数是3,但1已经在之前出现过了所以是4
有1个数比它小的数是2,但1已经在之前出现过了所以是3
有1个数比它小的数是2,但1,3,4都出现过了所以是5
最后一个数只能是2
数为:14352.
void invCantor(int n , int k , int s[ ] ) { int i , j , count , used[8]={0}; k --; for( i = 0 ; i < n ; i ++) { count = k / fac[ n - i - 1 ]; for( j = 1 ; j <= n ; j ++) { if ( !vis[ j ]) { if ( count == 0 ) break; count--; } } s[ i ] = j; vis [ j ] = 1; k %= fac[n - i - 1 ]; } }

浙公网安备 33010602011771号