Cantor expansion

Cantor expansion的本质是将一个排列hash成为一个数,这个数就是这个排列rank值,将原本需要用nn的空间来记录的排列状态在n!的空间内记录下来,有效利用了空白的空间。而将排列变为排名的桥梁便是展开后的那个an数组。其实原理非常简单,但是对于解决类似N数码问题等牵涉的排列状态记录的问题时特别有用。

 

const int maxn = 8;//保存排列的长度

int fac[maxn + 1];//保存阶乘

/*

*康托展开的值与实际rank值相差1

*/

void initfac(){//初始化阶乘

    fac[0] = 1;

    for (int i = 1; i <= maxn; i++){

        fac[i] = fac[i - 1] * i;

    }

}



int cantorEncode(const int a[]){//将排列逆康托展开,排列下标从1开始,所得值为rank

    int x = 0;

    for (int i = 1; i <= maxn; i++){

        int cnt = 0;

        for (int j = i + 1; j <= maxn; j++){

            if (a[j]<a[i]){

                cnt++;

            }

        }

        x += fac[maxn - i] * cnt;

    }

    return x+1;

}

void cantorDecode(int rank, int a[]){//将x=rank-1康托展开,保存到a[]中,下标从1开始

    rank--;

    int tmp[maxn + 1] = { 0 };

    for (int i = 1; i <= maxn; i++){

        tmp[i] = rank / fac[maxn - i];

        rank -= fac[maxn - i] * tmp[i];

    }

    bool flag[maxn + 1] = { false };

    for (int i = 1; i <= maxn; i++){

        int cnt = 0;

        for (int j = 1; j <= maxn;j++){

            if (!flag[j]){

                cnt++;

                if (cnt == tmp[i]+1){

                    a[i] = j;

                    flag[j] = true;

                    break;

                }

            }

        }

    }

}

 


 

康托展开就是一种特殊的哈希函数

  把一个整数X展开成如下形式:

  X=a[n]*n!+a[n-1]*(n-1)!+...+a[2]*2!+a[1]*1!

  其中,a为整数,并且0<=a<i,i=1,2,..,n

  {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。

  代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。

  他们间的对应关系可由康托展开来找到。

  如我想知道321是{1,2,3}中第几个大的数可以这样考虑 :

  第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{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是第三个大数。

你可能感兴趣的:(ant)