打造最快的Hash表

转载 2007年10月11日 11:29:00

最近在网上看到篇文章,一起拜一拜暴雪
先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?

有一个方法最简单,老老实实从头查到尾,一个一个比较,直到找到为止,我想只要学过程序设计的人都能把这样一个程序作出来,但要是有程序员把这样的程序交给用户,我只能用无语来评价,或许它真的能工作,但...也只能如此了。

最合适的算法自然是使用HashTable(哈希表),先介绍介绍其中的基本知识,所谓Hash,一般是一个整数,通过某种算法,可以把一个字符串"压缩" 成一个整数,这个数称为Hash,当然,无论如何,一个32位整数是无法对应回一个字符串的,但在程序中,两个字符串计算出的Hash值相等的可能非常小,下面看看在MPQ中的Hash算法

unsigned long HashString(char *lpszFileName, unsigned long dwHashType)
{
unsigned char *key = (unsigned char *)lpszFileName;
unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;
int ch;

while(*key != 0)
{
ch = toupper(*key++);

seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
}
return seed1;
}

Blizzard的这个算法是非常高效的,被称为"One-Way Hash",举个例子,字符串"unitneutralacritter.grp"通过这个算法得到的结果是0xA26067F3。
是不是把第一个算法改进一下,改成逐个比较字符串的Hash值就可以了呢,答案是,远远不够,要想得到最快的算法,就不能进行逐个的比较,通常是构造一个哈希表(Hash Table)来解决问题,哈希表是一个大数组,这个数组的容量根据程序的要求来定义,例如1024,每一个Hash值通过取模运算 (mod)对应到数组中的一个位置,这样,只要比较这个字符串的哈希值对应的位置又没有被占用,就可以得到最后的结果了,想想这是什么速度?是的,是最快的O(1),现在仔细看看这个算法吧
int GetHashTablePos(char *lpszString, SOMESTRUCTURE *lpTable, int nTableSize)
{
int nHash = HashString(lpszString), nHashPos = nHash % nTableSize;

if (lpTable[nHashPos].bExists && !strcmp(lpTable[nHashPos].pString, lpszString))
return nHashPos;
else
return -1; //Error value
}

看到此,我想大家都在想一个很严重的问题:"如果两个字符串在哈希表中对应的位置相同怎么办?",毕竟一个数组容量是有限的,这种可能性很大。解决该问题的方法很多,我首先想到的就是用"链表",感谢大学里学的数据结构教会了这个百试百灵的法宝,我遇到的很多算法都可以转化成链表来解决,只要在哈希表的每个入口挂一个链表,保存所有对应的字符串就OK了。

事情到此似乎有了完美的结局,如果是把问题独自交给我解决,此时我可能就要开始定义数据结构然后写代码了。然而Blizzard的程序员使用的方法则是更精妙的方法。基本原理就是:他们在哈希表中不是用一个哈希值而是用三个哈希值来校验字符串。

中国有句古话"再一再二不能再三再四",看来Blizzard也深得此话的精髓,如果说两个不同的字符串经过一个哈希算法得到的入口点一致有可能,但用三个不同的哈希算法算出的入口点都一致,那几乎可以肯定是不可能的事了,这个几率是1:18889465931478580854784,大概是10的 22.3次方分之一,对一个游戏程序来说足够安全了。

现在再回到数据结构上,Blizzard使用的哈希表没有使用链表,而采用"顺延"的方式来解决问题,看看这个算法:
int GetHashTablePos(char *lpszString, MPQHASHTABLE *lpTable, int nTableSize)
{
const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;
int nHash = HashString(lpszString, HASH_OFFSET);
int nHashA = HashString(lpszString, HASH_A);
int nHashB = HashString(lpszString, HASH_B);
int nHashStart = nHash % nTableSize, nHashPos = nHashStart;

while (lpTable[nHashPos].bExists)
{
if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB)
return nHashPos;
else
nHashPos = (nHashPos + 1) % nTableSize;

if (nHashPos == nHashStart)
break;
}

return -1; //Error value
}

1. 计算出字符串的三个哈希值(一个用来确定位置,另外两个用来校验)
2. 察看哈希表中的这个位置
3. 哈希表中这个位置为空吗?如果为空,则肯定该字符串不存在,返回
4. 如果存在,则检查其他两个哈希值是否也匹配,如果匹配,则表示找到了该字符串,返回
5. 移到下一个位置,如果已经越界,则表示没有找到,返回
6. 看看是不是又回到了原来的位置,如果是,则返回没找到
7. 回到3

怎么样,很简单的算法吧,但确实是天才的idea, 其实最优秀的算法往往是简单有效的算法.

from:http://www.firstdev.net/bbs/simple/index.php?t1435.html 

打造最快的Hash表(和Blizzard的对话)(转载,有删改)

   先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?    有一个方法最简单,老老实实从头查到尾,一个一个比较,直...
  • angelo_yz
  • angelo_yz
  • 2005年10月15日 18:02
  • 8629

打造最快的Hash表

打造最快的Hash表(暴雪用的MPQ文件) 最近在网上看到篇文章,一起拜一拜暴雪先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,...
  • goodluckyxl
  • goodluckyxl
  • 2005年10月10日 10:17
  • 2265

C语言 打造最快的Hash表

转贴:http://www.vcgood.com/bbs/forum_posts.asp?tid=771 (暴雪用的MPQ文件) 最近在网上看到篇文章,一起拜一拜暴雪 先提一个简单的问题,如果有...
  • Mirage520
  • Mirage520
  • 2011年08月26日 10:02
  • 933

Blizzard经典之打造最快的Hash表

      大学时代,对于Blizzard情有独钟,特别是100M左右的程序,能够打造出来魔兽争霸,简直是叹为观止!一个能在386上运行流畅的游戏,仅仅用了256色的颜色索引表,就打造了其游戏中丰富的...
  • chinacodec
  • chinacodec
  • 2009年03月04日 22:16
  • 1180

打造最快的Hash表-暴雪游戏(Blizzard)的高效哈希算法

(暴雪用的MPQ文件)最近在网上看到篇文章,一起拜一拜暴雪先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?有一个方法...
  • zhulei632
  • zhulei632
  • 2010年06月05日 09:44
  • 1503

打造最快的Hash表(和Blizzard的对话)

 開元最近学习了一下Blizzard的MPQ文件格式,颇有一些心得,其中一条就是对HastTable的理解,很想写出来给大家共享,感谢Justin Olbrantz的文章《Inside MoPaQ》,...
  • xjtuse_mal
  • xjtuse_mal
  • 2007年01月23日 21:37
  • 732

打造最快的哈希表(转)

先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?有一个方法最简单,老老实实从头查到尾,一个一个比较,直到找到为止,我...
  • self_control
  • self_control
  • 2008年06月07日 12:43
  • 1962

最快的Java缓存──Whirlycache

Whirlycache竟然是SUN的一个项目!它的网站上发布了一个横向测评,结果显示所需时间最少。这回可能不用担心JSF太慢了。官方网址:https://whirlycache.dev.java.ne...
  • tipfoo
  • tipfoo
  • 2007年10月10日 20:36
  • 2041

平均速度最快的排序算法是?----阿里巴巴2015实习生笔试题

平均速度最快的排序算法是? 正确答案: B   你的答案: B (正确) Shell排序 快速排序 冒泡排序 插入排序 添加笔记 求解答(0) 收藏 纠错 解析...
  • chengonghao
  • chengonghao
  • 2016年07月08日 16:02
  • 1567

Times33算法与最快的Hash表

关于times33算法 不约而同的,几乎所有的流行的hash map都采用了DJB hash function,俗称“Times33”算法。Perl、Berkeley DB 、Apache、MF...
  • wangpengqi
  • wangpengqi
  • 2013年08月02日 14:58
  • 866
收藏助手
不良信息举报
您举报文章:打造最快的Hash表
举报原因:
原因补充:

(最多只允许输入30个字)