最快的内容查找算法-----暴雪的Hash算法

暴雪公司有个经典的字符串的hash公式  :先提一个简单的问题,假如有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?  
有一个方法最简单,老老实实从头查到尾,一个一个比较,直到找到为止,我想只要学过程序设计的人都能把这样一个程序作出来,但要是有程序员把这样的程序交给用户,我只能用无语来评价,或许它真的能工作,但也只能如此了。  
最合适的算法自然是使用HashTable(哈希表),先介绍介绍其中的基本知识,所谓Hash,一般是一个整数,通过某种算法,可以把一个字符串"压缩" 成一个整数,这个数称为Hash,当然,无论如何,一个32位整数是无法对应回一个字符串的,但在程序中,两个字符串计算出的Hash值相等的可能非常小,下面看看在MPQ中的Hash算法 。

 1 unsigned long HashString(char *lpszFileName, unsigned long dwHashType)
 2 {
 3          unsigned char *key = (unsigned char *)lpszFileName;
 4          unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;
 5          int ch;
 6          while(*key != 0)
 7          {
 8                   ch = toupper(*key );
 9                   seed1 = cryptTable[(dwHashType < < 8) ch] ^ (seed1 seed2);
10                   seed2 = ch seed1 seed2 (seed2 < < 5) 3;
11           }
12          return seed1;
13 }  

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使用的哈希表没有使用链表,而采用"顺延"的方式来解决问题,看看这个算法:

 1 int GetHashTablePos(char *lpszString, MPQHASHTABLE *lpTable, int nTableSize)
 2 {
 3        const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;
 4        int nHash = HashString(lpszString, HASH_OFFSET);
 5        int nHashA = HashString(lpszString, HASH_A);
 6        int nHashB = HashString(lpszString, HASH_B);
 7        int nHashStart = nHash % nTableSize, nHashPos = nHashStart;
 8        while (lpTable[nHashPos].bExists)
 9        {
10               if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB)
11                      return nHashPos;
12               else
13                      nHashPos = (nHashPos 1) % nTableSize;
14               if (nHashPos == nHashStart)
15                      break;
16        }
17        return -1; //Error value
18 }  

1. 计算出字符串的三个哈希值(一个用来确定位置,另外两个用来校验)  
2. 察看哈希表中的这个位置  
3. 哈希表中这个位置为空吗?假如为空,则肯定该字符串不存在,返回 。 
4. 假如存在,则检查其他两个哈希值是否也匹配,假如匹配,则表示找到了该字符串,返回。  
5. 移到下一个位置,假如已经越界,则表示没有找到,返回。  
6. 看看是不是又回到了原来的位置,假如是,则返回没找到。 
7. 回到3  
怎么样,很简单的算法吧,但确实是天才的idea, 其实最优秀的算法往往是简单有效的算法。

原文链接:http://www.cnblogs.com/duzouzhe/archive/2009/10/14/1583359.html

时间: 2024-10-01 05:30:50

最快的内容查找算法-----暴雪的Hash算法的相关文章

【数据结构与算法】一致性Hash算法及Java实践

追求极致才能突破极限 一.案例背景 1.1 系统简介 首先看一下系统架构,方便解释: 页面给用户展示的功能就是,可以查看任何一台机器的某些属性(以下简称系统信息). 消息流程是,页面发起请求查看指定机器的系统信息到后台,后台可以查询到有哪些server在提供服务,根据负载均衡算法(简单的轮询)指定由哪个server进行查询,并将消息发送到Kafka,然后所有的server消费Kafka的信息,当发现消费的信息要求自己进行查询时,就连接指定的machine进行查询,并将结果返回回去. Server

hash算法总结收集

hash算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系,(每一个真实值只能有一个键值,但是一个键值可以对应多个真实值),这样可以快速在数组等条件中里面存取数据.     在网上看了不少HASH资料,所以对HASH的相关资料进行总结和收集.   //HashTable.h template class HashTable{ public : HashTable( int count ) ; void put( T* t ,int key ) ; T* get

逐步实现hash算法(基于BKDRhash函数)

哈希(Hash)算法,即散列函数.它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程.同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出.hash算法一般用于快速查找和加密. hash算法可以使用的哈希函数种类很多,处理冲突的方法也有开放定址.再哈希.链地址.公共溢出区等. 因此,在编写代码之前,首先需要根据所要处理的数据,选择合适的hash函数和冲突处理办法.开放定址需要空闲存储单元,所需要的表比实际容量大,而且容易产生二次聚集发生新冲突.链地

图解一致性hash算法和实现

更多内容,欢迎关注微信公众号:全菜工程师小辉.公众号回复关键词,领取免费学习资料. 一致性hash算法是什么? 一致性hash算法,是麻省理工学院1997年提出的一种算法,目前主要应用于分布式缓存当中. 一致性hash算法可以有效地解决分布式存储结构下动态增加和删除节点所带来的问题. 在Memcached.Key-Value Store.Bittorrent DHT.LVS中都采用了一致性hash算法,可以说一致性hash算法是分布式系统负载均衡的首选算法. 传统hash算法的弊端 常用的算法是

对一致性Hash算法,Java代码实现的深入研究

一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法和一致性Hash算法的算法原理做了详细的解读. 算法的具体原理这里再次贴上: 先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 232-1]),接着在

【转载】对一致性Hash算法,Java代码实现的深入研究

原文地址:http://www.cnblogs.com/xrq730/p/5186728.html 一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性Hash算法的算法原理做了详细的解读. 算法的具体原理这里再次贴上: 先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将服务器节点放置在这

一致性hash算法 – consistent hashing

consistent hashing 算法早在 1997 年就在论文 Consistent hashing and random trees 中被提出,目前在cache 系统中应用越来越广泛: 1 基本场景 比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache :澳门威尼斯人赌场 hash(object)%N

[转]海量数据解决思路之Hash算法

一.概述 本文将粗略讲述一下Hash算法的概念特性,里边会结合分布式系统负载均衡实例对Hash的一致性做深入探讨.另外,探讨一下Hash算法在海量数据处理方案中的通用性.最后,从源代码出发,具体分析一下Hash算法在MapReduce框架的中的应用. 二.Hash算法 Hash可以通过散列函数将任意长度的输入变成固定长度的输出,也可以将不同的输入映射成为相同的相同的输出,而且这些输出范围也是可控制的,所以起到了很好的压缩映射和等价映射功能.这些特性被应用到了信息安全领域中加密算法,其中等价映射这

再谈一致hash算法

consistent hashing 算法早在 1997 年就在论文 Consistent hashing and random trees 中被提出,目前在 cache 系统中应用越来越广泛: 1 基本场景 比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache : hash(object)%N 一切都运行正常