1. 程式人生 > >康拓展開和康拓逆展開

康拓展開和康拓逆展開

前言:
今天做一道排列組合的題目,然後發現那道題目要用到康拓展開,於是自學了一下康拓展開和逆展開。
是什麼
她是什麼呢?
定義:X=an*(n-1)!+an-1*(n2)!+…+ai(i1)!+…+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; }

康託逆展開:
因為她可以作為hash 所以有一定的對映關係,所以肯定存在逆展開式,她的作用就是求n位數第m個排列的組合形式。

假設求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}; 
//這裡實際用的時候可以先打表計算出來fac[i]就是i!是幾 
//康託展開的逆運算,{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];  
    }  
}