`
085567
  • 浏览: 219198 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多

文章转摘自http://www.cmykrgb123.cn/blog/string-hash-compare/

 

常用的字符串Hash函数还有ELFHash,APHash等等,都是十分简单有效的方法。这些函数使用

位运算使得每一个字符都对最后的函数值产生影响。另外还有以MD5和SHA1为代表的杂凑函数,

这些函数几乎不可能找到碰撞。

常用字符串哈希函数有BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,

PJWHash,ELFHash等等。对于以上几种哈希函数,我对其进行了一个小小的评测。

 

Hash函数 数据1 数据2 数据3 数据4 数据1得分 数据2得分 数据3得分 数据4得分 平均分
BKDRHash 2 0 4774 481 96.55 100 90.95 82.05 92.64
APHash 2 3 4754 493 96.55 88.46 100 51.28 86.28
DJBHash 2 2 4975 474 96.55 92.31 0 100 83.43
JSHash 1 4 4761 506 100 84.62 96.83 17.95 81.94
RSHash 1 0 4861 505 100 100 51.58 20.51 75.96
SDBMHash 3 2 4849 504 93.1 92.31 57.01 23.08 72.41
PJWHash 30 26 4878 513 0 0 43.89 0 21.95
ELFHash 30 26 4878 513 0 0 43.89 0 21.95

其中数据1为100000个字母和数字组成的随机串哈希冲突个数。数据2为100000个有意义的英文句

子哈希冲突个数。数据3为数据1的哈希值与1000003(大素数)求模后存储到线性表中冲突的个数。

数据4为数据1的哈希值与10000019(更大素数)求模后存储到线性表中冲突的个数。

经过比较,得出以上平均得分。平均数为平方平均数。可以发现,BKDRHash无论是在实际效果还是

编码实现中,效果都是最突出的。APHash也是较为优秀的算法。DJBHash,JSHash,RSHash与

SDBMHash各有千秋。PJWHash与ELFHash效果最差,但得分相似,其算法本质是相似的。

在信息修竞赛中,要本着易于编码调试的原则,个人认为BKDRHash是最适合记忆和使用的。

CmYkRgB123原创,欢迎建议、交流、批评和指正。

 

附:各种哈希函数的C语言程序代码

{ unsigned int hash = 0;   while (*str) { // equivalent to: hash = 65599*hash + (*str++); hash = (*str++) + (hash << 6) + (hash << 16) - hash; }   return (hash & 0x7FFFFFFF); }   // RS Hash 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 unsigned int JSHash(char *str) { unsigned int hash = 1315423911;   while (*str) { hash ^= ((hash << 5) + (*str++) + (hash >> 2)); }   return (hash & 0x7FFFFFFF); }   // P. J. Weinberger Hash 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) << (BitsInUnignedInt
unsigned int SDBMHash(char *str)
                                               - 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 
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 
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);
}
 
// DJB Hash 
unsigned int DJBHash(char *str)
{
	unsigned int hash = 5381;
 
	while (*str)
	{
		hash += (hash << 5) + (*str++);
	}
 
	return (hash & 0x7FFFFFFF);
}
 
// AP Hash 
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。如百度查找一组字符串是否出现在某个文本中,这个不是考什么kmp,他们想听到的是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]<<(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_
 
/* Calc hash 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 hash 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 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函数的方法不同,但是它们的效率都是不错的,相互之间差距很小,读者可以参考实际情况从其中借鉴使用。
分享到:
评论

相关推荐

    主流字符串哈希算法简明介绍及实例代码

    主流字符串哈希算法简明介绍及实例代码 哈希算法是计算机科学中的一种基础算法,广泛应用于数据存储、检索、加密等领域。字符串哈希算法是哈希算法中的一种特殊类型,专门用于处理字符串类型的数据。在本文中,我们...

    python-leetcode面试题解之第249题移位字符串分组.zip

    3. 哈希函数:可能需要利用哈希函数来快速比较字符串之间的相似性,例如计算字符串的异或值,可以快速判断两个字符串是否可以通过有限次旋转达到相同状态。 4. 分组问题:这是数据结构和算法中的经典问题,可能需要...

    《柔性字符串(英文版)》

    Rabin-Karp算法采用哈希函数预计算子串的哈希值,快速判断两个子串是否可能相同,然后进行局部验证。后缀自动机是一种数据结构,能够高效地处理多个模式的匹配问题,对于大量查询的情况尤其适用。 除了这些经典算法...

    哈希查找算法

    哈希函数是将输入(通常是一个字符串或对象)映射到一个固定大小的数值(通常是一个数组的索引)的过程。这个过程的关键在于设计一个好的哈希函数,能够使得不同输入的映射结果尽可能地分散,从而减少冲突,即不同的...

    哈希表相关概念、hash函数、hash冲突解决方案、代码示例

    哈希表是一种高效的数据结构,它利用哈希函数将键(key)转化为数组索引,以便...无论是在字典、集合、去重等基本数据结构的实现,还是在字符串匹配、数据压缩、拼写检查等复杂应用场景,哈希表都是不可或缺的工具。

    哈希表的应用,介绍了一些哈希表的用例

    可以使用哈希表来存储已经出现过的字符串,每当遇到一个新的字符串时,先检查哈希表中是否已经存在该字符串。如果存在,则表示找到了重复项;如果不存在,则将其添加到哈希表中。这种方法不仅简单易懂,而且效率极高...

    PHP常用函数大全.pdf

    - `chunk_split` 将字符串分割成若干部分。 - `convert_cyr_string` 用于转换西里尔字符集的字符串。 - `convert_uudecode` 和 `convert_uuencode` 用于解码和编码Uuencode格式的字符串。 - `count_chars` 用于获取...

    计算字符串的MD5值 MD5 加密 C源码

    这个源代码通常包含若干个函数,如`MD5_Init`(初始化MD5上下文),`MD5_Update`(处理数据块),`MD5_Final`(生成最后的摘要),以及可能的一个用户友好的接口函数,如`MD5_String`或`MD5_File`,用于计算字符串或...

    c char 串 模式与匹配算法

    C语言标准库提供了若干字符串处理函数,如`strcpy`、`strlen`等,用于复制和计算字符串长度。 在实际应用中,串的常见操作包括: 1. `StrAssign(&T, chars)`:初始化字符串T,使其等于字符常量chars。 2. `StrCopy...

    c语言面试题之哈希表赎金信.zip

    此外,哈希函数的设计也很关键,一个好的哈希函数应能尽量减少冲突,提高查找效率。 总结一下,哈希表赎金信问题是C语言面试中考察哈希表应用的经典案例,通过解决这个问题,可以展示对数据结构、算法和C语言编程...

    MD5C++源码MD5C++源码

    MD5(Message-Digest Algorithm 5)是一种广泛用于数据完整性校验和加密的哈希函数。它是计算机科学中的一种加密算法,由美国麻省理工学院的Ronald Rivest在1991年设计。MD5的主要功能是将任意长度的数据转化为一个...

    Flash版MD5加密

    每个块由若干个32位的字组成,然后通过四个不同的函数(F, G, H, I)以及一系列的位操作(如位左移、异或等)对这些块进行处理,最终得到一个128位的摘要值,通常以16进制的形式表示,形成32个字符的字符串。...

    分治法求解最近对串匹配问题手写源代码

    在计算机科学中,最近对串匹配(Closest Pair of Strings Problem)是一个经典的字符串处理问题,主要目的是找到两个字符串集合中的最相似的两对字符串。在这个问题中,“最近对”指的是两个字符串之间的编辑距离...

    程序员编程艺术:面试和算法心得

    - **题目描述**: 给定一个字符串,如 "abcdef",要求把字符串前面的若干个字符移动到字符串的尾部,例如将 "a" 和 "b" 移动到尾部,使得原字符串变成 "cdefab"。要求实现一个函数,其时间复杂度为 O(n),空间复杂度...

    SHA1消息摘要的MFC实现

    SHA1是一种广泛应用于信息安全领域的哈希函数,全称为Secure Hash Algorithm 1。它由美国国家安全局(NSA)设计,并于1995年由美国国家标准与技术研究所(NIST)发布为标准。SHA1主要用于创建数据的数字指纹,通常...

    vc6.0对话框结构实现MD5算法

    MD5(Message-Digest Algorithm 5)是一种广泛使用的哈希函数,由计算机科学家Rivest在1991年设计,主要用于确保数据的完整性和一致性。在VC6.0环境下,我们可以利用C++来实现MD5算法,为用户提供字符串加密功能。 ...

    MD5 C++加密算法

    MD5(Message-Digest Algorithm 5)是一种广泛使用的哈希函数,它能够将任意长度的输入数据转换成固定长度的128位(16字节)摘要。在C++中实现MD5加密算法通常涉及到以下几个核心概念: 1. **哈希函数**:哈希函数...

    数据结构-数据结构历年考题及答案2.docx

    6. **哈希表**:使用线性探测解决哈希冲突,给定的哈希函数是H(key)=key mod 13,给出了如何将数据表存入长度为13的哈希表的例子。 7. **查找算法**:折半查找(Binary Search)是一种高效的查找方法,适用于有序...

    大一C语言实用函数整理50+

    23. Hash表进行若干次查找出现的元素:使用哈希表来快速查找和统计元素的出现频率。 24. 字符统计:涉及到对字符串中字符出现次数的统计。 25. 独立区间:可能涉及到数据结构中的区间树或区间覆盖问题。 26. 电话...

    2、省选+NOI-第二部分 字符串_2020.09.04.pdf

    - **哈希表**:基于哈希函数的数据结构,支持快速查找。 ### 5. 动态规划 - **基础DP**:如背包问题、编辑距离问题等。 - **树形DP**:应用于树形结构上的动态规划。 - **记忆化搜索**:通过缓存子问题的结果来...

Global site tag (gtag.js) - Google Analytics