1. 程式人生 > >【學習隨筆】Cantor 展開式

【學習隨筆】Cantor 展開式

做到HDOJ1043的時候接觸到了Cantor展開式,因為開始在用BFS+hash 做的,因此在四向搜尋的時候需要判重,需要知道

當前方向的這種九宮情況是否之前被訪問過,但是...九宮的總情況大概有9!個36W多個,因此判重非常的煩,看了網上的

基本都是手寫的,用陣列模擬 hash table ,hash 最重要的就是hash函式來得到序列號,因為九宮不存在重複數,因此每種

情況可以當做一個全排序,因此Cantor來了...通過這下列操作可以得出每一種九宮情況的序列號而不會重複,來作為hash

序列號..很nice的操作!

Cantor展開:

X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!

使用示例來幫助理解利用Cantor展開如何求解本問題。 
假如序列s=[“A”,”B”,”C”,”D”] 
需要求序列s’=[“D”,”A”,”B”,”C”]是序列s的全排列中的第幾個排列,記作X(s’);

X(s’) =     3(在序列DABC中有3個比D小)*3!+
            0(在剩下的序列ABC中有0個比A小)*2!+
            0(在剩下的序列BC中有0個比B小)*1!+
            0(在剩下的序列C中有0個比C小)*0!
    =18

那麼序列s’是s的全排列中第18個排列(從0開始計數); 
同理可以根據s算第18個排列序列。 

總結:康託展開是一個全排列到一個自然數的一一對映

下列程式碼轉自 https://www.cnblogs.com/AdaByron/archive/2011/09/21/2200970.html 

正向展開:

//value陣列存放當前排列   
const int fac[]={1,1,2,6,24,120,720,5040,40320};//康託序列   
inline int cantor(){  
    int ans=0;  
    for(int i=0;i<N;i++)  
    {  
        int cnt=0;  
        for(int k=i+1;k<N;k++)  
        {  
            if(value[k]<value[i])  
                cnt++;  
        }  
        ans+=fac[8-i]*cnt;  
    }  
    return ans;  
}

反向展開:

//value陣列存放當前排列   
const int fac[]={1,1,2,6,24,120,720,5040,40320};//康託序列   
inline int cantor(Point p){  
    int ans=0;  
    for(int i=0;i<N;i++)  
    {  
        int cnt=0;  
        for(int k=i-1;k>=0;k--)  
        {  
            if(value[k]>value[i])  
                cnt++;  
        }  
        ans+=fac[i]*cnt;  
    }  
    return ans;  
}