Hash函数的设计优化.doc

上传人:e****s 文档编号:61750527 上传时间:2022-11-21 格式:DOC 页数:20 大小:392.50KB
返回 下载 相关 举报
Hash函数的设计优化.doc_第1页
第1页 / 共20页
Hash函数的设计优化.doc_第2页
第2页 / 共20页
点击查看更多>>
资源描述

《Hash函数的设计优化.doc》由会员分享,可在线阅读,更多相关《Hash函数的设计优化.doc(20页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、Hash函数的设计优化天津南开中学 李羽修【摘要】Hash是一种在信息学竞赛中经常用到的数据结构。一个好的Hash函数可以很大程度上提高程序的整体时间效率和空间效率。本文对面向各种不同标本(关键值)的Hash函数进行讨论,并对多种常用的Hash函数进行了分析和总结。【关键字】Hash 函数,字符串,整数,实数,排列组合【正文】对于一个Hash函数,评价其优劣的标准应为随机性,即对任意一组标本,进入Hash表每一个单元(cell)之概率的平均程度,因为这个概率越平均,数据在表中的分布就越平均,表的空间利用率就越高。由于在竞赛中,标本的性质是无法预知的,因此数学推理将受到很大限制。我们用实验的方法

2、研究这个随机性。一、整数的Hash函数常用的方法有三种:直接取余法、乘积取整法、平方取中法。下面我们对这三种方法分别进行讨论。以下假定我们的关键字是,Hash表的容量是,Hash函数为。1直接取余法我们用关键字除以,取余数作为在Hash表中的位置。函数表达式可以写成:。例如,表容量,关键值,那么。该方法的好处是实现容易且速度快,是很常用的一种方法。但是如果选择的不好而偏偏标本又很特殊,那么数据在Hash中很容易扎堆而影响效率。对于的选择,在经验上,我们一般选择不太接近的一个素数;如果关键字的值域较小,我们一般在此值域1.11.6倍范围内选择。例如的值域为,那么即为一个不错的选择。竞赛的时候可以

3、写一个素数生成器或干脆自己写一个“比较像素数”的数。我用4000个数插入一个容量为701的Hash表,得到的结果是:测试数据随机数据连续数据最小单元容量:05最大单元容量:156期望容量:标准差:可见对于随机数据,取余法的最大单元容量达到了期望容量的将近3倍。经测试,在我的机器(Pentium III 866MHz,128MB RAM)上,该函数的运行时间大约是39ns,即大约35个时钟周期。2乘积取整法我们用关键字乘以一个在中的实数(最好是无理数),得到一个之间的实数;取出其小数部分,乘以,再取整数部分,即得在Hash表中的位置。函数表达式可以写成:;其中表示的小数部分,即。例如,表容量,种

4、子(是一个实际效果很好的选择),关键值,那么。同样用4000个数插入一个容量为701的Hash表(),得到的结果是:测试数据随机数据连续数据最小单元容量:04最大单元容量:157期望容量:标准差:从公式中可以看出,这个方法受的影响是很小的,在的值比较不适合直接取余法的时候这个方法的表现很好。但是从上面的测试来看,其表现并不是非常理想,且由于浮点运算较多,运行速度较慢。经反复优化,在我的机器上仍需892ns才能完成一次计算,即810个时钟周期,是直接取余法的23倍。3平方取中法我们把关键字平方,然后取中间的位作为Hash函数值返回。由于的每一位都会对其平方中间的若干位产生影响,因此这个方法的效果

5、也是不错的。但是对于比较小的值效果并不是很理想,实现起来也比较繁琐。为了充分利用Hash表的空间,最好取2的整数次幂。例如,表容量,关键值,那么。用4000个数插入一个容量为512的Hash表(注意这里没有用701,是为了利用Hash表的空间),得到的结果是:测试数据随机数据连续数据最小单元容量:01最大单元容量:1717期望容量:标准差:效果比我们想象的要差,尤其是对于连续数据。但由于只有乘法和位运算,该函数的速度是最快的。在我的机器上,一次运算只需要23ns,即19个时钟周期,比直接取余法还要快一些。比较一下这三种方法:实现难度实际效果运行速度其他应用直接取余法易好较快字符串乘积取整法较易

6、较好慢浮点数平方取中法中较好快无从这个表格中我们很容易看出,直接取余法的性价比是最高的,因此也是我们竞赛中用得最多的一种方法。对于实数的Hash函数,我们可以直接利用乘积取整法;而对于标本为其他类型数据的Hash函数,我们可以先将其转换为整数,然后再将其插入Hash表。下面我们来研究把其他类型数据转换成整数的方法。二、字符串的Hash函数字符串本身就可以看成一个256进制(ANSI字符串为128进制)的大整数,因此我们可以利用直接取余法,在线性时间内直接算出Hash函数值。为了保证效果,仍然不能选择太接近的数;尤其是当我们把字符串看成一个进制数的时候,选择会使得该字符串的任意一个排列的Hash

7、函数值都相同。(想想看,为什么?)常用的字符串Hash函数还有ELFHash,APHash等等,都是十分简单有效的方法。这些函数使用位运算使得每一个字符都对最后的函数值产生影响。另外还有以MD5和SHA1为代表的杂凑函数,这些函数几乎不可能找到碰撞(MD5前一段时间才刚刚被破解)。我从Mark Twain的一篇小说中分别随机抽取了1000个不同的单词和1000个不同的句子,作为短字符串和长字符串的测试数据,然后用不同的Hash函数把它们变成整数,再用直接取余法插入一个容量为1237的Hash表,遇到冲突则用新字符串覆盖旧字符串。通过观察最后“剩下”的字符串的个数,我们可以粗略地得出不同的Has

8、h函数实际效果。短字符串长字符串平均编码难度直接取余数667676易P. J. Weinberger Hash683676难ELF Hash683676较难SDBM Hash694680易BKDR Hash665710较易DJB Hash694683较易AP Hash684698较难RS Hash691693较难JS Hash684708较易把1000个随机数用直接取余法插入容量为1237的Hash表,其覆盖单元数也只达到了694,可见后面的几种方法已经达到了极限,随机性相当优秀。然而我们却很难选择,因为不存在完美的、既简单又实用的解决方案。我一般选择JS Hash或SDBM Hash作为字符

9、串的Hash函数。这两个函数的代码如下:unsigned int JSHash(char *str)unsigned int hash = 1315423911; / nearly a prime - 1315423911 = 3 * 438474637while (*str)hash = (hash 2);return (hash & 0x7FFFFFFF);unsigned int SDBMHash(char *str)unsigned int hash = 0;while (*str)/ equivalent to: hash = 65599*hash + (*str+);hash =

10、(*str+) + (hash 6) + (hash 16) - hash;return (hash & 0x7FFFFFFF);JSHash的运算比较复杂,如果对效果要求不是特别高的话SDBMHash是一个很好的选择。三、排列的Hash函数准确的说,这里我们的研究不再仅仅局限在“Hashing”的工作,而是进化到一个“numerize”的过程,也就是说我们可以在排列和1到的自然数之间建立一一对应的关系。这样我们就可以利用这个关系来直接定址,或者用作Hash函数;在基于状态压缩的动态规划算法中也能用上。1背景知识自然数的进制表示法我们已经很熟悉了,即:, 比如便是二进制数,便是十进制数。引理:

11、,。证明:对使用数学归纳法。1) 时,等式显然成立。2) 假设时等式成立,即。则时,即时等式亦成立。3) 综上所述,成立。把这个式子变形一下:上式和类似。不难证明,从0到的任何自然数可唯一地表示为其中,。甚至在式子后面加上一个也无妨,在后面我们把这一项忽略掉。所以从0到的个自然数与(*)一一对应。另一方面,不难从算出。我们可以把序列理解为一个“变进制数”,也就是第一位二进制,第二位三进制,第位进制,第位进制。这样,我们就可以方便的使用类似“除取余法”的方法从一个自然数算出序列。由于这样的序列共有个,我们很自然的想到把这个序列和个元素的全排列建立一一对应。2全排列与自然数之一一对应为了方便起见,

12、不妨设个元素为。对应的规则如下:设序列(*)对应的某一排列,其中可以看做是排列中数所在位置右边比小的数的个数。以排列4213为例,它是元素1,2,3,4的一个排列。4的右边比4小的数的数目为3,所以。3右边比3小的数的数目为0,即。同理。所以排列4213对应于变进制的301,也就是十进制的19;反过来也可以从19反推到301,再反推到排列4213。3更一般性的排列受到这个思路启发,我们同样可以把更一般性的排列与自然数之间建立一一对应关系。想一想从个元素中选个的排列数的公式是怎么来的?根据乘法原理,我们有这是由于在排列的第1个位置有种选择,在排列的第2个位置有种选择,在排列的第个位置有种选择。既

13、然这样,我们可以定义一种“m-n变进制数”,使其第1位是进制,第2位是进制,第位是进制。这样,0到之间的任意一个自然数都可以唯一地表示成:其中,。注意到(证明略,可直接变形结合前面的引理推得),所以从0到的个自然数可以与序列一一对应。类似地,可以用取余法从自然数算出。我们设个元素为,从中取出个。对应关系如下:维护一个首元素下标为0的线性表,初始时。对于某一排列,我们从开始处理。首先在中找到的下标记为,然后删除;接着在中找到的下标记为,然后删除直到被删除为止。以在5个元素1,2,3,4,5中取出2,4,3为例,这时。首先在中取出2,记下,变为1,3,4,5;在中取出4,记下,变为1,3,5;在中

14、取出3,记下,变为1,5。因此排列243对应于“3-5变进制数”121,即十进制数19;反过来也可以从十进制数19反推到121,再反推到排列243。各序列及其对应的排列如下表:12300003412203012400113422213112500223452223213201033512303313401143522313413501253542323514202064123003614302174133013714502284153023815203094213103915303110423311401540321142531241213100124313204221410113432321

15、432151021443532244231110154513304523411116452331462351121745333247241120185124004824312119513401492451222051440250251130215214105125313122523411522541322352441253312200245314205431420125532421553152022653442256321210275414305732421128542431583252122954343259【总结】本文对几个常用的Hash函数进行了总结性的介绍和分析,并将其延伸到应用更加广

16、泛的“与自然数建立一一对应”的过程。Hash是一种相当有效的数据结构,充分体现了“空间换时间”的思想。在如今竞赛中内存限制越来越松的情况下,要做到充分利用内存空间来换取宝贵的时间,Hash能够给我们很大帮助。我们应当根据题目的特点,选择适合题目的数据结构来优化算法。对于组合与自然数的一一对应关系,我还没有想到好的方法,欢迎大家讨论。【参考文献】1 Thomas H Cormen, Charles E Leiserson, Ronald L Riverst, Clifford Stald L Riverst, Clifford Stald L Riverst, Clifford Stald L

17、Riverst, Clifford Stald L Riverst, Clifford Stald L Riverst, Clifford Stein. Introduction to Algorithms. Second Edition. The MIT Press, 20012 刘汝佳,黄亮. ald L Riverst, Clifford St信息学竞赛. 北京:清华大学出版社,20043 卢开澄,卢华明. 组合数学(第3版). 北京:清华大学出版社,2002【附录】常用的字符串Hash函数之源代码:/ RS Hash Functionunsigned int RSHash(char *

18、str)unsigned int b = 378551;unsigned int a = 63689;unsigned int hash = 0;while (*str)hash = hash * a + (*str+);a *= b;return (hash & 0x7FFFFFFF);/ JS Hash Functionunsigned int JSHash(char *str)unsigned int hash = 1315423911;while (*str)hash = (hash 2);return (hash & 0x7FFFFFFF);/ P. J. Weinberger Ha

19、sh Functionunsigned int PJWHash(char *str)unsigned int BitsInUnignedInt = (unsigned int)(sizeof(unsigned int) * 8);unsigned int ThreeQuarters = (unsigned int)(BitsInUnignedInt * 3) / 4);unsigned int OneEighth = (unsigned int)(BitsInUnignedInt / 8);unsigned int HighBits = (unsigned int)(0xFFFFFFFF) (

20、BitsInUnignedInt - OneEighth);unsigned int hash = 0;unsigned int test = 0;while (*str)hash = (hash ThreeQuarters) & (HighBits);return (hash & 0x7FFFFFFF);/ ELF Hash Functionunsigned int ELFHash(char *str)unsigned int hash = 0;unsigned int x = 0;while (*str)hash = (hash 24);hash &= x;return (hash & 0

21、x7FFFFFFF);/ BKDR Hash Functionunsigned int BKDRHash(char *str)unsigned int seed = 131; / 31 131 1313 13131 131313 etc.unsigned int hash = 0;while (*str)hash = hash * seed + (*str+);return (hash & 0x7FFFFFFF);/ SDBM Hash Functionunsigned int SDBMHash(char *str)unsigned int hash = 0;while (*str)hash

22、= (*str+) + (hash 6) + (hash 16) - hash;return (hash & 0x7FFFFFFF);/ DJB Hash Functionunsigned int DJBHash(char *str)unsigned int hash = 5381;while (*str)hash += (hash 5) + (*str+);return (hash & 0x7FFFFFFF);/ AP Hash Functionunsigned int APHash(char *str)unsigned int hash = 0;int i;for (i=0; *str;

23、i+)if (i & 1) = 0)hash = (hash 3);elsehash = (hash 5);return (hash & 0x7FFFFFFF); while ilen do begin i:=nexti; write(linei.way); end; end;begin propare; init; bfs; print;我们经常使用的数的进制为“常数进制”,即始终逢p进1。例如,p进制数K可表示为K = a0*p0 + a1*p1 + a2*p2 + . + an*pn (其中0 = ai = p-1),它可以表示任何一个自然数。对于这种常数进制表示法,以及各种进制之间的转

24、换大家应该是很熟悉的了,但大家可能很少听说变进制数。这里我要介绍一种特殊的变进制数,它能够被用来实现 全排列的Hash函数,并且该Hash函数能够实现完美的防碰撞和空间利用(不会发生碰撞,且所有空间被完全使用,不多不少)。这种全排列Hash函数也 被称为全排列数化技术。下面,我们就来看看这种变进制数。我们考查这样一种变进制数:第1位逢2进1,第2位逢3进1,第n位逢n+1进1。它的表示形式为K = a1*1! + a2*2! + a3*3! + . + an*n! (其中0 = ai = i),也可以扩展为如下形式(因为按定义a0始终为0),以与p进制表示相对应:K = a0*0! + a1*

25、1! + a2*2! + a3*3! + . + an*n! (其中0 = ai = i)。(后面的变进制数均指这种变进制数,且采用前一种表示法)先让我们来考查一下该变进制数的进位是否正确。假设变进制数K的第i位ai为i+1,需要进位,而ai*i!=(i+1)*i!=1*(i+1)!,即正确的向高位进1。这说明该变进制数能够正确进位,从而是一种合法的计数方式。接下来我们考查n位变进制数K的性质:(1)当所有位ai均为i时,此时K有最大值MAXK = 1*1! + 2*2! + 3*3! + . + n*n! = 1! + 1*1! + 2*2! + 3*3! + . + n*n! - 1 =

26、(1+1)*1! + 2*2! + 3*3! + . + n*n! - 1 = 2! + 2*2! + 3*3! + . + n*n! - 1 = . = (n+1)!-1因此,n位K进制数的最大值为(n+1)!-1。(2)当所有位ai均为0时,此时K有最小值0。因此,n位变进制数能够表示0到(n+1)!-1的范围内的所有自然数,共(n+1)!个。在一些状态空间搜索算法中,我们需要快速判断某个状态是否已经出现,此时常常使用Hash函数来实现。其中,有一类特殊的状态空间,它们是由全排列产生 的,比如N数码问题。对于n个元素的全排列,共产生n!个不同的排列或状态。下面将讨论如何使用这里的变进制数来

27、实现一个针对全排列的Hash函数。从数的角度来看,全排列和变进制数都用到了阶乘。如果我们能够用0到n!-1这n!个连续的变进制数来表示n个元素的所有排列,那么就能够把全排列完全地 数化,建立起全排列和自然数之间一一对应的关系,也就实现了一个完美的Hash函数。那么,我们的想法能否实现呢?答案是肯定的,下面将进行讨论。假设我们有b0,b1,b2,b3,.,bn共n+1个不同的元素,并假设各元素之间有一种次序关系 b0b1b2.bn。对它们进行全排列,共产生(n+1)!种不同的排列。对于产生的任一排列 c0,c1,c2,.,cn,其中第i个元素ci(1 = i = n)与它前面的i个元素构成的逆序

28、对的个数为di(0 = di = i),那么我们得到一个逆序数序列d1,d2,.,dn(0 = di = i)。这不就是前面的n位变进制数的各个位么?于是,我们用n位变进制数M来表示该排列:M = d1*1! + d2*2! + . + dn*n!因此,每个排列都可以按这种方式表示成一个n位变进制数。下面,我们来考查n位变进制数能否与n+1个元素的全排列建立起一一对应的关系。由于n位变进制数能表示(n+1)!个不同的数,而n+1个元素的全排列刚好有(n+1)!个不同的排列,且每一个排列都已经能表示成一个n位变进制数。如果我们能够证明任意两个不同的排列产生两个不同的变进制数,那么我们就可以得出结

29、论: 定理1 n+1个元素的全排列的每一个排列对应着一个不同的n位变进制数。/*补充: 什么是逆序数:跟标准列相反序数的总和 比如说 标准列是1 2 3 4 5 那么 5 4 3 2 1 的逆序数算法: 看第二个,4之前有一个5,在标准列中5在4的后面,所以记1个 类似的,第三个 3 之前有 4 5 都是在标准列中3的后面,所以记2个 同样的,2 之前有3个,1之前有4个 将这些数加起来就是逆序数=1+2+3+4=10 再举一个 2 4 3 1 5 4 之前有0个 3 之前有1个 1 之前有3个 5 之前有0个 所以逆序数就是1+3=4 */对于全排列的任意两个不同的排列p0,p1,p2,.,

30、pn(排列P)和q0,q1,q2,.,qn(排列Q),从后往前查找第一个不相同的元素,分别记为pi和qi(0 i pi,那么,如果在排列Q中qi之前的元素x与qi构成逆序对,即有x qi,则在排列P中pi之前也有相同元素x pi(因为x qi且qi pi),即在排列P中pi之前的元素x也与pi构成逆序对,所以pi的逆序数大于等于qi的逆序数。又qi与pi在排列P中构成pi的逆序对,所以pi的 逆序数大于qi的逆序数。(2)同理,如果pi qi,那么qi的逆序数大于pi的逆序数。因此,由(1)和(2)知,排列P和排列Q对应的变进制数至少有第i位不相同,即全排列的任意两个不同的排列具有不同的变进制

31、数。至此,定理1得证。计算n个元素的一个排列的变进制数的算法大致如下(时间复杂度为O(n2)):template size_t PermutationToNumber(const T permutation, int n)/ n不能太大,否则会溢出(如果size_t为32位,则n = 12)size_t result = 0;for (int j = 1; j n; +j) int count = 0; for (int k = 0; k permutationj) +count; / factorialsj保存着j! result += count * factorialsj;return

32、result;总结:/实际上,如果只是求逆序数 可以做到O(n logn),时间耗费在求逆序数上,求逆序数相当于求排列的次数,所以是O(n logn)的据说还可以用树状数组优化。举例:例如三个元素的排列排列 逆序 Hash123 000 0132 001 2213 010 1231 002 4312 011 3321 012 5说明:(1)由于n!是一个很大的数,因此一般只能用于较小的n。(2)有了计算排列的变进制数的算法,我们就可以使用一个大小为n!的数组来保存每一个排列的状态,使用排列的变进制数作为数组下标,从而实现状态的快速检索。如果只是标记状态是否出现,则可以用一位来标记状态。end很

33、简单的思想是用for一遍以前搜过的状态。但是,八数码问题的状态共9!,显然for在时间上无法承受。另外一种思想是把这9位数排成一列,转化成一个10进制的数进行hash,但空间上的问题有无法解决了。 显然,我们需要的是一个能够全排列的hash,而下面得这篇文章也就介绍了对于一个n12的全排列状态,我们应该怎样完成hash判重 我们经常使用的数的进制为“常数进制”,即始终逢p进1。例如,p进制数K可表示为 K = a0*p0 + a1*p1 + a2*p2 + . + an*pn (其中0 = ai = p-1),它可以表示任何一个自然数。对于这种常数进制表示法,以及各种进制之间的转换大家应该是很

34、熟悉的了,但大家可能很少听说变进制数。这里我要介绍一种特殊的变进制数,它能够被用来实现全排列的Hash函数,并且该Hash函数能够实现完美的防碰撞和空间利用(不会发生碰撞,且所有空间被完全使用,不多不少)。这种全排列Hash函数也被称为全排列数化技术。下面,我们就来看看这种变进制数。我们考查这样一种变进制数:第1位逢2进1,第2位逢3进1,第n位逢n+1进1。它的表示形式为 K = a1*1! + a2*2! + a3*3! + . + an*n! (其中0 = ai = i),也可以扩展为如下形式(因为按定义a0始终为0),以与p进制表示相对应: K = a0*0! + a1*1! + a2

35、*2! + a3*3! + . + an*n! (其中0 = ai = i)。(后面的变进制数均指这种变进制数,且采用前一种表示法)先让我们来考查一下该变进制数的进位是否正确。假设变进制数K的第i位ai为i+1,需要进位,而ai*i!=(i+1)*i!=1*(i+1)!,即正确的向高位进1。这说明该变进制数能够正确进位,从而是一种合法的计数方式。接下来我们考查n位变进制数K的性质:(1)当所有位ai均为i时,此时K有最大值 MAXK = 1*1! + 2*2! + 3*3! + . + n*n! = 1! + 1*1! + 2*2! + 3*3! + . + n*n! - 1 = (1+1)*

36、1! + 2*2! + 3*3! + . + n*n! - 1 = 2! + 2*2! + 3*3! + . + n*n! - 1 = . = (n+1)!-1 因此,n位K进制数的最大值为(n+1)!-1。(2)当所有位ai均为0时,此时K有最小值0。因此,n位变进制数能够表示0到(n+1)!-1的范围内的所有自然数,共(n+1)!个。在一些状态空间搜索算法中,我们需要快速判断某个状态是否已经出现,此时常常使用Hash函数来实现。其中,有一类特殊的状态空间,它们是由全排列产生的,比如N数码问题。对于n个元素的全排列,共产生n!个不同的排列或状态。下面将讨论如何使用这里的变进制数来实现一个针对

37、全排列的Hash函数。从数的角度来看,全排列和变进制数都用到了阶乘。如果我们能够用0到n!-1这n!个连续的变进制数来表示n个元素的所有排列,那么就能够把全排列完全地数化,建立起全排列和自然数之间一一对应的关系,也就实现了一个完美的Hash函数。那么,我们的想法能否实现呢?答案是肯定的,下面将进行讨论。假设我们有b0,b1,b2,b3,.,bn共n+1个不同的元素,并假设各元素之间有一种次序关系 b0b1b2.bn。对它们进行全排列,共产生(n+1)!种不同的排列。对于产生的任一排列 c0,c1,c2,.,cn,其中第i个元素ci(1 = i = n)与它前面的i个元素构成的逆序对的个数为di

38、(0 = di = i),那么我们得到一个逆序数序列d1,d2,.,dn(0 = di = i)。这不就是前面的n位变进制数的各个位么?于是,我们用n位变进制数M来表示该排列: M = d1*1! + d2*2! + . + dn*n!因此,每个排列都可以按这种方式表示成一个n位变进制数。下面,我们来考查n位变进制数能否与n+1个元素的全排列建立起一一对应的关系。由于n位变进制数能表示(n+1)!个不同的数,而n+1个元素的全排列刚好有(n+1)!个不同的排列,且每一个排列都已经能表示成一个n位变进制数。如果我们能够证明任意两个不同的排列产生两个不同的变进制数,那么我们就可以得出结论: 定理1

39、 n+1个元素的全排列的每一个排列对应着一个不同的n位变进制数。对于全排列的任意两个不同的排列p0,p1,p2,.,pn(排列P)和q0,q1,q2,.,qn(排列Q),从后往前查找第一个不相同的元素,分别记为pi和qi(0 i pi,那么,如果在排列Q中qi之前的元素x与qi构成逆序对,即有x qi,则在排列P中pi之前也有相同元素x pi(因为x qi且qi pi),即在排列P中pi之前的元素x也与pi构成逆序对,所以pi的逆序数大于等于qi的逆序数。又qi与pi在排列P中构成pi的逆序对,所以pi的逆序数大于qi的逆序数。(2)同理,如果pi qi,那么qi的逆序数大于pi的逆序数。因此

40、,由(1)和(2)知,排列P和排列Q对应的变进制数至少有第i位不相同,即全排列的任意两个不同的排列具有不同的变进制数。至此,定理1得证。 我把上面的定理总结简化了一下,具体的内容是这样的1、 计算出阶乘(factorial)1n-1的数值,存放在数组factorial中2、 对于一个随机得到的全排列a。从第二位开始(设为ai),计算在i之前有多少个比ai大的数(也就是ai和aj构成逆序对(ji),设为count个3、 让hash值+count*factoriali-1; 以下的伪代码给出了hash函数对一个全排列的过程function gethash(全排列a):longint; begin

41、gethash:=0; for i:=2 to n do /注意,一定是第二位,因为从第二位开始才可能出现逆序对 begin count:=0; for j:=1 to i-1 do /计算逆序对的数目 begin if aiaj then inc(count); end; gethash:=gethash+count*factoriali-1; end;end;算法的复杂度是n2,但是由于这里的n非常小,一般只适用于(n12),可视为在常数时间内完成hash。更何况,这个hash实现了空间上的最大节约,既不会出现冲突,同时一点也不浪费一点空间,因为这里n!个状态完完全全的能够表示n全排列的所有状态,达到了空间上的最大利用。 回到八数码问题,有了这样的一个强hash,我们要做的事情就只剩下简单的广度搜索而已了,为了使搜索起来更方便,可以把没有数的地方看成是0。这样,在移动的时候,只需要

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 标准材料 > 石化标准

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com