字符串hash算法比较(转)

字符串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&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 ([email protected]) and

 

* Glenn Fowler ([email protected]). Landon Curt Noll ([email protected])

 

* 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函数的方法不同,但是它们的效率都是不错的,相互之间差距很小,读者可以参考实际情况从其中借鉴使用。

 

转自:http://blog.163.com/tianyin_pang/blog/static/644592482009727103822350/

你可能感兴趣的:(字符串hash算法比较(转))