现在的位置: 首页 > 综合 > 正文

全排列完美哈希

2013年10月12日 ⁄ 综合 ⁄ 共 1638字 ⁄ 字号 评论关闭

1.组合数学。

强调不一样的是,组合数学以数作为逆序的依据,bk表示与数k逆序的数有多少个0<=bk<=n-k

因此转换的公式是:

sum=bn*(0)!+bn-1*(1)!+……+bk*(n-k)!+……+b2*(n-2)!+b1*(n-1)!

bn=0恒成立

sum最大值为当bk取n-k得最大值n!-1最小值为0,共n!种状态,与n!种排列一一对应。

解码时bk放在第bk+1个空位置上,原理就不解释了,太长了,书上有。

2.还可以参考http://bbs.chinaunix.net/viewthread.php?tid=1283459作为资料。

这里的依据是以下标di表示下标为i的数与前面互逆的数的个数,因此0<=di<=i;

公式为:

sum=d0*(0)!+d1*(1)!+……+di*(i)!+……dn-1*(n-1)!+dn*(n)!

d0=0恒成立

3.附上以组合数学上为原理的代码

#include<cstdio>
#include<iostream>
using namespace std;

int p[9]= {1,1,2,6,24,120,720,5040,40320};
int encode(int *perm)//n=9
{
    //八数码问题,九个格子,相当于总的排列数为9!
    int sum=0;
    for(int i=0; i<9; i++)
    {
        int count=0;
        for(int j=0; j<i; j++)
        {
            if(perm[j]>perm[i])count++;
        }
        sum+=count*p[9-perm[i]];
    }
    return sum;
}

void decode(int sum,int *perm)
{
    for(int i=0; i<9; i++)perm[i]=0;

    for(int i=1; i<=9; i++)
    {
        int tot=sum/p[9-i]+1;
        sum%=p[9-i];
        int loc,count=0;
        for(loc=0; loc<9; loc++)
        {
            if(!perm[loc])
            {
                count++;
                if(count==tot)break;//!!找到空第tot个空位置
            }
        }
        perm[loc]=i;
    }
}

int main()
{
    freopen("data.in","r",stdin);
    int a[10];
    for(int i=0;i<9;i++)cin>>a[i];//1,2,3,4,5,6,7,8,9
    int t[10];
    decode(encode(a),t);//编码后解码,若得原码则可认为是正确的
    for(int i=0; i<9; i++)cout<<t[i]<<' ';
    cout<<endl;
    return 0;
}

4.最后还附上一个很神的代码,至今还没怎么看懂!

#include<cstdio>
#include<iostream>
using namespace std;

int p[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320};
int encode(int* s){//升序做为正序
	int x=0;
	int i,j;
	for(i=0;i<9;i++){
		int k =s[i];
		for(j=0;j<i;j++)
			if(s[j]<s[i])k--;
		x+=k*p[8-i];
	}
	return x;
}

void decode(int x,int* s){
	int i,j;
	for(i=0; i<9;i++){
		s[i]=x/p[8-i];
		x%=p[8-i];
	}
	for(i=8;i>=0;i--)
		for(j=8;j>i;j--)
			if(s[j]>=s[i])s[j]++;
}
int main()
{
    freopen("data.in","r",stdin);
    int a[10];
    for(int i=0;i<9;i++)cin>>a[i];//0,1,2,3,4,5,6,7,8
    int t[10];
    decode(encode(a),t);//编码后解码,若得原码则可认为是正确的
    for(int i=0; i<9; i++)cout<<t[i]<<' ';
    cout<<endl;
    return 0;
}

抱歉!评论已关闭.