康托展開及其逆運算
一、定義
X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
ai為整數,並且0<=ai<i(1<=i<=n)
簡單點說就是,判斷這個數在其各個數字全排列中從小到大排第幾位。
比如 132,在1、2、3的全排列中排第2位。
二、作用
維基:n位(0~n-1)全排列後,其康托展開唯一且最大約為n!,因此可以由更小的空間來儲存這些排列。由公式可將X逆推出對應的全排列。
它可以應用於哈希表中空間壓縮,
而且在搜索某些類型題時,將VIS數組量壓縮。
三、康托展開求法
比如2143 這個數,求其展開:
從頭判斷,至尾結束,
① 比 2(第一位數)小的數有多少個->1個就是1,1*3!
② 比 1(第二位數)小的數有多少個->0個0*2!
③ 比 4(第三位數)小的數有多少個->3個就是1,2,3,但是1,2之前已經出現,所以是 1*1!
將所有乘積相加=7
比該數小的數有7個,所以該數排第8的位置。
1234 1243 1324 1342 1423 1432
2134 2143 2314 2341 2413 2431
3124 3142 3214 3241 3412 3421
4123 4132 4213 4231 4312 4321
四、代碼
int fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的階乘為fac[i] // 康托展開-> 表示數字a是 a的全排列中從小到大排,排第幾 // n表示1~n個數 a數組表示數字。 int kangtuo(int n,char a[]) { int i,j,t,sum; sum=0; for( i=0; i<n ;++i) { t=0; for(j=i+1;j<n;++j) if( a[i]>a[j] ) ++t; sum+=t*fac[n-i-1]; } return sum+1; }
五、康托展開的逆:
康托展開是一個全排列到自然數的雙射,可以作為哈希函數。
所以當然也可以求逆運算了。
逆運算的方法:
假設求4位數中第19個位置的數字。
① 19減去1 → 18
② 18 對3!作除法 → 得3余0
③ 0對2!作除法 → 得0余0
④ 0對1!作除法 → 得0余0
據上面的可知:
我們第一位數(最左面的數),比第一位數小的數有3個,顯然 第一位數為→ 4
比第二位數小的數字有0個,所以 第二位數為→1
比第三位數小的數字有0個,因為1已經用過,所以第三位數為→2
第四位數剩下 3
該數字為 4123 (正解)
用代碼實現上述步驟為:
int fac[] = {1,1,2,6,24,120,720,5040,40320}; //康托展開的逆運算,{1...n}的全排列,中的第k個數為s[] void reverse_kangtuo(int n,int k,char s[]) { int i, j, t, vst[8]={0}; --k; for (i=0; i<n; i++) { t = k/fac[n-i-1]; for (j=1; j<=n; j++) if (!vst[j]) { if (t == 0) break; --t; } s[i] = ‘0‘+j; vst[j] = 1; k %= fac[n-i-1]; } }
康托展開及其逆運算