织梦CMS - 轻松建站从此开始!

罗索

经典字符串Hash函数比较

落鹤生 发布于 2011-01-03 17:43 点击:次 
测试文本采用单词表,测试过程中从一个输入文件中读取全部不重复单词构造一个Hash表,测 试内容分别是函数总调用次数、函数总调用时间、最大拉链长度、 平均拉链长度、桶利用率(使用过的桶所占的比率)
TAG:

1 概述

链表查找的时间效率为O(N),二分法为log2N,B+ Tree为log2N,但Hash链表查找的时间效率为O(1)。

设计高效算法往往需要使用Hash链表,常数级的查找速度是任何别的算法无法比拟的,Hash链表的构造和冲突的不同实现方法对效率当然有一定的影响,然 而Hash函数是Hash链表最核心的部分,本文尝试分析一些经典软件中使用到的字符串Hash函数在执行效率、离散性、空间利用率等方面的性能问题。

2 经典字符串Hash函数介绍

作者阅读过大量经典软件原代码,下面分别介绍几个经典软件中出现的字符串Hash函数。

2.1 PHP中出现的字符串Hash函数

  1. static unsigned long hashpjw(char *arKey, unsigned int nKeyLength) 
  2. unsigned long h = 0, g; 
  3. char *arEnd=arKey+nKeyLength; 
  4. while (arKey < arEnd) { 
  5. h = (h << 4) + *arKey++; 
  6. if ((g = (h & 0xF0000000))) { 
  7. h = h ^ (g >> 24); 
  8. h = h ^ g; 
  9. return h; 

2.2 OpenSSL中出现的字符串Hash函数

  1. unsigned long lh_strhash(char *str) 
  2. int i,l; 
  3. unsigned long ret=0; 
  4. unsigned short *s; 
  5.   
  6. if (str == NULL) return(0); 
  7. l=(strlen(str)+1)/2; 
  8. s=(unsigned short *)str; 
  9. for (i=0; i 
  10. ret^=(s[i]<<(i&0x0f)); 
  11. return(ret); 
  12. } */ 
  13.   
  14. /* The following hash seems to work very well on normal text strings 
  15. * no collisions on /usr/dict/words and it distributes on %2^n quite 
  16. * well, not as good as MD5, but still good. 
  17. */ 
  18. unsigned long lh_strhash(const char *c) 
  19. unsigned long ret=0; 
  20. long n; 
  21. unsigned long v; 
  22. int r; 
  23.   
  24. if ((c == NULL) || (*c == '\0')) 
  25. return(ret); 
  26. /* 
  27. unsigned char b[16]; 
  28. MD5(c,strlen(c),b); 
  29. return(b[0]|(b[1]<<8)|(b[2]<<16)|(b[3]<<24)); 
  30. */ 
  31.   
  32. n=0x100; 
  33. while (*c) 
  34. v=n|(*c); 
  35. n+=0x100; 
  36. r= (int)((v>>2)^v)&0x0f; 
  37. ret=(ret(32-r)); 
  38. ret&=0xFFFFFFFFL; 
  39. ret^=v*v; 
  40. c++; 
  41. return((ret>>16)^ret); 

在下面的测量过程中我们分别将上面的两个函数标记为OpenSSL_Hash1和OpenSSL_Hash2,至于上面的实现中使用MD5算法的实现函数我们不作测试。

2.3 MySql中出现的字符串Hash函数

  1. #ifndef NEW_HASH_FUNCTION 
  2. /* Calc hashvalue for a key */ 
  3. static uint calc_hashnr(const byte *key,uint length) 
  4. register uint nr=1, nr2=4; 
  5. while (length--) 
  6. nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8); 
  7. nr2+=3; 
  8. return((uint) nr); 
  9.   
  10. /* Calc hashvalue for a key, case indepenently */ 
  11. static uint calc_hashnr_caseup(const byte *key,uint length) 
  12. register uint nr=1, nr2=4; 
  13. while (length--) 
  14. nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8); 
  15. nr2+=3; 
  16. return((uint) nr); 
  17.   
  18. #else 
  19.   
  20. /* 
  21. * Fowler/Noll/Vo hash 
  22. * 
  23. * The basis of the hash algorithm was taken from an idea sent by email to the 
  24. * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and 
  25. * Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com) 
  26. * later improved on their algorithm. 
  27. * 
  28. * The magic is in the interesting relationship between the special prime 
  29. * 16777619 (2^24 + 403) and 2^32 and 2^8. 
  30. * 
  31. * This hash produces the fewest collisions of any function that we've seen so 
  32. * far, and works well on both numbers and strings. 
  33. */ 
  34.   
  35. uint calc_hashnr(const byte *key, uint len) 
  36. const byte *end=key+len; 
  37. uint hash; 
  38. for (hash = 0; key < end; key++) 
  39. hash *= 16777619; 
  40. hash ^= (uint) *(uchar*) key; 
  41. return (hash); 
  42.  
  43. uint calc_hashnr_caseup(const byte *key, uint len) 
  44. const byte *end=key+len; 
  45. uint hash; 
  46. for (hash = 0; key < end; key++) 
  47. hash *= 16777619; 
  48. hash ^= (uint) (uchar) toupper(*key); 
  49. return (hash); 
  50.  
  51. #endif 

Mysql中对字符串Hash函数还区分了大小写,我们的测试中使用不区分大小写的字符串Hash函数,另外我们将上面的两个函数分别记为MYSQL_Hash1和MYSQL_Hash2。

2.4 另一个经验字符串Hash函数

  1. unsigned int hash(char *str) 
  2. register unsigned int h; 
  3. register unsigned char *p; 
  4.  
  5. for(h=0, p = (unsigned char *)str; *p ; p++) 
  6. h = 31 * h + *p; 
  7.  
  8. return h; 

3 测试及结果

3.1 测试说明

从上面给出的经典字符串Hash函数中可以看出,有的涉及到字符串大小敏感问题,我们的测试 中只考虑字符串大小写敏感的函数,另外在上面的函数中有的函数 需要长度参数,有的不需要长度参数,这对函数本身的效率有一定的影响,我们的测试中将对函数稍微作一点修改,全部使用长度参数,并将函数内部出现的计算长 度代码删除。

我们用来作测试用的Hash链表采用经典的拉链法解决冲突,另外我们采用静态分配桶(Hash链表长度)的方法来构造Hash链表,这主要是为了简化我们的实现,并不影响我们的测试结果。

测试文本采用单词表,测试过程中从一个输入文件中读取全部不重复单词构造一个Hash表,测 试内容分别是函数总调用次数、函数总调用时间、最大拉链长度、 平均拉链长度、桶利用率(使用过的桶所占的比率),其中函数总调用次数是指Hash函数被调用的总次数,为了测试出函数执行时间,该值在测试过程中作了一 定的放大,函数总调用时间是指Hash函数总的执行时间,最大拉链长度是指使用拉链法构造链表过程中出现的最大拉链长度,平均拉链长度指拉链的平均长度。

测试过程中使用的机器配置如下:

PIII600笔记本,128M内存,windows 2000 server操作系统。

3.2 测试结果

以下分别是对两个不同文本文件中的全部不重复单词构造Hash链表的测试结果,测试结果中函数调用次数放大了100倍,相应的函数调用时间也放大了100倍。

 

(mikenoodle)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201101/10705.html]
本文出处:百度博客 作者:mikenoodle
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容