若干经典的字符串哈希函数

若干经典的字符串哈希函数




	
//  RS Hash Function 
 unsigned  int  RSHash( char   * 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 Function 
 unsigned  int  JSHash( char   * str)
  {
        unsigned  int  hash  =   1315423911 ;

         while  ( * str)
          {
                hash  ^=  ((hash  <<   5 )  +  ( * str ++ )  +  (hash  >>   2 ));
        } 
 
         return  (hash  &   0x7FFFFFFF );
} 
 
 //  P. J. Weinberger Hash Function 
 unsigned  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 )  <<  (BitsInU
nignedInt  -  OneEighth);
        unsigned  int  hash              =   0 ;
        unsigned  int  test              =   0 ;

         while  ( * str)
          {
                hash  =  (hash  <<  OneEighth)  +  ( * str ++ );
                 if  ((test  =  hash  &  HighBits)  !=   0 )
                  {
                        hash  =  ((hash  ^  (test  >>  ThreeQuarters))  &  ( ~ HighBits)
);
                } 
        } 
 
         return  (hash  &   0x7FFFFFFF );
} 
 
 //  ELF Hash Function 
 unsigned  int  ELFHash( char   * str)
  {
        unsigned  int  hash  =   0 ;
        unsigned  int  x     =   0 ;

         while  ( * str)
          {
                hash  =  (hash  <<   4 )  +  ( * str ++ );
                 if  ((x  =  hash  &   0xF0000000L )  !=   0 )
                  {
                        hash  ^=  (x  >>   24 );
                        hash  &=   ~ x;
                } 
        } 
 
         return  (hash  &   0x7FFFFFFF );
} 
 
 //  BKDR Hash Function 
 unsigned  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 Function 
 unsigned  int  SDBMHash( char   * str)
  {
        unsigned  int  hash  =   0 ;

         while  ( * str)
          {
                hash  =  ( * str ++ )  +  (hash  <<   6 )  +  (hash  <<   16 )  -  hash;
        } 
 
         return  (hash  &   0x7FFFFFFF );
} 
 
 //  DJB Hash Function 
 unsigned  int  DJBHash( char   * str)
  {
        unsigned  int  hash  =   5381 ;

         while  ( * str)
          {
                hash  +=  (hash  <<   5 )  +  ( * str ++ );
        } 
 
         return  (hash  &   0x7FFFFFFF );
} 
 
 //  AP Hash Function 
 unsigned  int  APHash( char   * str)
  {
        unsigned  int  hash  =   0 ;
         int  i;

         for  (i = 0 ;  * str; i ++ )
          {
                 if  ((i  &   1 )  ==   0 )
                  {
                        hash  ^=  ((hash  <<   7 )  ^  ( * str ++ )  ^  (hash  >>   3 ));
                } 
                 else 
                   {
                        hash  ^=  ( ~ ((hash  <<   11 )  ^  ( * str ++ )  ^  (hash  >>   5 )));
                } 
        } 
 
         return  (hash  &   0x7FFFFFFF );
} 
 比较经典的字符串hash就这些了吧,"ELF Hash Function" <-这个比较常用. 


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

 
  
 
 2 经典字符串Hash函数介绍  
 
作者阅读过大量经典软件原代码,下面分别介绍几个经典软件中出现的字符串Hash函数。 
 
 2.1 PHP中出现的字符串Hash函数  
 
static unsigned long hashpjw(char *arKey, unsigned int nKeyLength) 
 
{ 
 
unsigned long h = 0, g; 
 
char *arEnd=arKey+nKeyLength; 
 

 
 
while (arKey < arEnd) { 
 
h = (h << 4) + *arKey++; 
 
if ((g = (h & 0xF0000000))) { 
 
h = h ^ (g >> 24); 
 
h = h ^ g; 
 
} 
 
} 
 
return h; 
 
} 
 
 2.2 OpenSSL中出现的字符串Hash函数  
 
unsigned long lh_strhash(char *str) 
 
{ 
 
int i,l; 
 
unsigned long ret=0; 
 
unsigned short *s; 
 

 
 
if (str == NULL) return(0); 
 
l=(strlen(str)+1)/2; 
 
s=(unsigned short *)str; 
 
for (i=0; i 
 
ret^=(s[i]<<(i&0x0f)); 
 
return(ret); 
 
} */ 
 

 
 
/* The following hash seems to work very well on normal text strings 
 
* no collisions on /usr/dict/words and it distributes on %2^n quite 
 
* well, not as good as MD5, but still good. 
 
*/ 
 
unsigned long lh_strhash(const char *c) 
 
{ 
 
unsigned long ret=0; 
 
long n; 
 
unsigned long v; 
 
int r; 
 

 
 
if ((c == NULL) || (*c == '\0')) 
 
return(ret); 
 
/* 
 
unsigned char b[16]; 
 
MD5(c,strlen(c),b); 
 
return(b[0]|(b[1]<<8)|(b[2]<<16)|(b[3]<<24));  
 
*/ 
 

 
 
n=0x100; 
 
while (*c) 
 
{ 
 
v=n|(*c); 
 
n+=0x100; 
 
r= (int)((v>>2)^v)&0x0f; 
 
ret=(ret(32-r)); 
 
ret&=0xFFFFFFFFL; 
 
ret^=v*v; 
 
c++; 
 
} 
 
return((ret>>16)^ret); 
 
} 
 
在下面的测量过程中我们分别将上面的两个函数标记为OpenSSL_Hash1和OpenSSL_Hash2,至于上面的实现中使用MD5算法的实现函数我们不作测试。 
 
 2.3 MySql中出现的字符串Hash函数  
 
#ifndef NEW_HASH_FUNCTION 
 

 
 
/* Calc hashvalue for a key */ 
 

 
 
static uint calc_hashnr(const byte *key,uint length) 
 
{ 
 
register uint nr=1, nr2=4; 
 
while (length--) 
 
{ 
 
nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8); 
 
nr2+=3; 
 
} 
 
return((uint) nr); 
 
} 
 

 
 
/* Calc hashvalue for a key, case indepenently */ 
 

 
 
static uint calc_hashnr_caseup(const byte *key,uint length) 
 
{ 
 
register uint nr=1, nr2=4; 
 
while (length--) 
 
{ 
 
nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8); 
 
nr2+=3; 
 
} 
 
return((uint) nr); 
 
} 
 

 
 
#else 
 

 
 
/* 
 
* Fowler/Noll/Vo hash 
 
* 
 
* The basis of the hash algorithm was taken from an idea sent by email to the 
 
* IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and 
 
* Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com) 
 
* later improved on their algorithm. 
 
* 
 
* The magic is in the interesting relationship between the special prime 
 
* 16777619 (2^24 + 403) and 2^32 and 2^8. 
 
* 
 
* This hash produces the fewest collisions of any function that we've seen so 
 
* far, and works well on both numbers and strings. 
 
*/ 
 

 
 
uint calc_hashnr(const byte *key, uint len) 
 
{ 
 
const byte *end=key+len; 
 
uint hash; 
 
for (hash = 0; key < end; key++) 
 
{ 
 
hash *= 16777619; 
 
hash ^= (uint) *(uchar*) key; 
 
} 
 
return (hash); 
 
} 
 

 
 
uint calc_hashnr_caseup(const byte *key, uint len) 
 
{ 
 
const byte *end=key+len; 
 
uint hash; 
 
for (hash = 0; key < end; key++) 
 
{ 
 
hash *= 16777619; 
 
hash ^= (uint) (uchar) toupper(*key); 
 
} 
 
return (hash); 
 
} 
 

 
 
#endif 
 
Mysql中对字符串Hash函数还区分了大小写,我们的测试中使用不区分大小写的字符串Hash函数,另外我们将上面的两个函数分别记为MYSQL_Hash1和MYSQL_Hash2。 
 
 2.4 另一个经验字符串Hash函数  
 
unsigned int hash(char *str) 
 
{ 
 
register unsigned int h; 
 
register unsigned char *p; 
 

 
 
for(h=0, p = (unsigned char *)str; *p ; p++) 
 
h = 31 * h + *p; 
 

 
 
return h; 
 
} 
 
 3 测试及结果  
 
 3.1 测试说明  
 
从上面给出的经典字符串Hash函数中可以看出,有的涉及到字符串大小敏感问题,我们的测试中只考虑字符串大小写敏感的函数,另外在上面的函数中有的函数 需要长度参数,有的不需要长度参数,这对函数本身的效率有一定的影响,我们的测试中将对函数稍微作一点修改,全部使用长度参数,并将函数内部出现的计算长 度代码删除。 
 
我们用来作测试用的Hash链表采用经典的拉链法解决冲突,另外我们采用静态分配桶(Hash链表长度)的方法来构造Hash链表,这主要是为了简化我们的实现,并不影响我们的测试结果。 
 
测试文本采用单词表,测试过程中从一个输入文件中读取全部不重复单词构造一个Hash表,测试内容分别是函数总调用次数、函数总调用时间、最大拉链长度、 平均拉链长度、桶利用率(使用过的桶所占的比率),其中函数总调用次数是指Hash函数被调用的总次数,为了测试出函数执行时间,该值在测试过程中作了一 定的放大,函数总调用时间是指Hash函数总的执行时间,最大拉链长度是指使用拉链法构造链表过程中出现的最大拉链长度,平均拉链长度指拉链的平均长度。  
 
测试过程中使用的机器配置如下: 
 
PIII600笔记本,128M内存,windows 2000 server操作系统。 
 
 3.2 测试结果  
 
以下分别是对两个不同文本文件中的全部不重复单词构造Hash链表的测试结果,测试结果中函数调用次数放大了100倍,相应的函数调用时间也放大了100倍。 
 

 
 
 

 
 
从上表可以看出,这些经典软件虽然构造字符串Hash函数的方法不同,但是它们的效率都是不错的,相互之间差距很小,读者可以参考实际情况从其中借鉴使用。 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值