Blizzard的MPQ文件格式搜索算法

Blizzard被称为最卓越的游戏制作公司,不愧于此。

最近学习了一下Blizzard的MPQ文件格式,颇有一些心得,其中一条就是对HastTable的理解,很想写出来给大家共享,感谢Justin Olbrantz的文章《Inside MoPaQ》,大多认识来源于此。

先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?有一个方法最简单,老老实实从头查到尾,一个一个比较,直到找到为止,我想只要学过程序设计的人都能把这样一个程序作出来,但要是有程序员把这样的程序交给用户,我只
能用无语来评价,或许它真的能工作,但...也只能如此了。最合适的算法自然是使用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也深得此话的精髓,如果说两个不同的字符串经过一个哈希算法得到的入口点一致有可能,但用三个 不同的哈希算法算出的入口点都一致,那几乎可以肯定是不可能的事了,这个几率是:1888946593147858085478
4,大概是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, 其实最优秀的算法往往是简单有效的算法.

/

1。解决hash表碰撞有很多方法,最开始提到的链表(chaining)是其中一种,这种方法并不是我原先认为的“糟糕”的方法,相反,这种方法是最普遍的,C++标准库中的hash表用的正是这种方法。

2. 暴雪没有使用链表,而是使用线性挖掘方式(Linear probing),这是由于这种不需要动态分配内存,而且更方便将数据序列化到磁盘文件上。

2. 更好的方法是"幂次挖掘"方法(quadratic probing),发生碰撞时,按照"+1,-1,+4,-4,+9,-9,...,+n^2,-n^2"方式移动,这种方式下还需要要求容器的长度是质数,可以达到更快的速度

3. 这些方法都不是什么秘密,稍微详细一些的数据结构课程里都有,所以有一点可以肯定,上课认真听讲是没有坏处的

4. 最后是一个好消息,2008 C++标准里已经有hash表了,只不过换了一个官方名称"unordered",有点怪哈

Blizzard的MPQ文件格式搜索算法相关推荐

  1. Blizzard的MPQ文件格式搜索算法---来自Gameres bbs

    Blizzard的MPQ文件格式搜索算法 Blizzard被称为最卓越的游戏制作公司,不愧于此. 作者:ghost2006 最近学习了一下Blizzard的MPQ文件格式,颇有一些心得,其中一条就是对 ...

  2. Blizzard经典之打造最快的Hash表

    大学时代,对于Blizzard情有独钟,特别是100M左右的程序,能够打造出来魔兽争霸,简直是叹为观止!一个能在386上运行流畅的游戏,仅仅用了256色的颜色索引表,就打造了其游戏中丰富的视觉感受,从 ...

  3. MPQ技术内幕(转载)

    开始尝试翻译一些英文文章,最近正好对mpq产生兴趣,看到一片文章叫做 inside MPQ,于是翻译一下,就当锻炼自己吧.这篇文章非常的不厚道,在关键地方戛然而止,而且没有更新的迹象.让人郁闷无比.但 ...

  4. JAR 文件格式的强大功能

    大多数 Java 程序员都熟悉对 JAR 文件的基本操作.但是只有少数程序员了解 JAR 文件格式的强大功能.在本文中,作者探讨了 JAR 格式的许多功能和优势,包括打包.可执行的 JAR 文件.安全 ...

  5. JAR文件(文件格式)

    JAR 文件是什么? JAR 文件格式以流行的 ZIP 文件格式为基础,用于将许多个文件聚集为一个文件.与 ZIP 文件不同的是,JAR 文件不仅用于压缩和发布,而且还用于部署和封装库.组件和插件程序 ...

  6. JAR 文件格式的强大功能(java中jar工具的使用)

    大多数 Java 程序员都熟悉对 JAR 文件的基本操作.但是只有少数程序员了解 JAR 文件格式的强大功能.在本文中,作者探讨了 JAR 格式的许多功能和优势,包括打包.可执行的 JAR 文件.安全 ...

  7. 《Java虚拟机规范》阅读(三):Class文件格式

    每一个Class都对应着唯一的一个类或借口的定义信息.这里,我们称为"Class文件格式"只是通俗的将任意一个符合有效的类或借口的格式这么称呼,但是它并不一定是以磁盘文件的形式存在 ...

  8. linux 压缩文件夹格式,Linux下常见文件格式的压缩、解压小结

    Linux下常见文件格式的压缩.解压小结 .tar 解包: tar xvf FileName.tar 打包:tar cvf FileName.tar DirName (注:tar是打包,不是压缩!) ...

  9. C语言局部搜索算法(爬山法,模拟退火法,遗传算法)求解八皇后问题

    C语言局部算法求解八皇后问题 写在前面 八皇后问题及局部搜索算法 爬山法(hill-climbing searching) 算法介绍 代码实现 退火法(simulated annealing) 算法介 ...

  10. 二、OCR训练时,将txt文件和图片数据转为lmdb文件格式

    文章目录 前言 一.背景? 二.直接上内容 1.代码 2.文件说明 前言 随着人工智能的不断发展,机器学习这门技术也越来越重要,本文就介绍OCR训练前ldmb文件制作的基础内容. 提示:以下是本篇文章 ...

最新文章

  1. android手机播放pc音乐播放器,最强手机音乐播放器?Foobar2K安卓版体验
  2. java中bjt和utc转化_如何在Java中转换UTC和本地时区
  3. 记录:自制的小说推荐程序(一)
  4. css工程化和浏览器兼容性问题
  5. 2018年春阅读计划---阅读笔记4
  6. 电脑办公人员必看:效率翻倍的11个Excel技巧!
  7. 转:高等数学、线性代数、概率论数理统计书籍推荐
  8. C++学习之路 | PTA乙级—— 1004 成绩排名 (20分)(精简)
  9. 牛顿法与拟牛顿法学习笔记
  10. 网站移动端500错误_PC网站和移动端网站有什么差异?
  11. 漫画 | 从搬家到容器技术 Docker 应用场景解析
  12. 无线传感器网络 | 期末复习知识点1
  13. linux_network
  14. Linux下使用nohup部署java 后台程序
  15. µC/OS-II Release Notes
  16. java.lang.NegativeArraySizeException
  17. mysql cbrt函数_PostgreSQL学习笔记5之函数和操作符一
  18. 机器人李继红_李继宏
  19. 【乌拉喵.教程】不同负载下继电器的保护电路设计(解决继电器触点粘接的问题)
  20. 过分依赖计算机教学的反思,多媒体教学反思

热门文章

  1. VSTOOutlook发邮件时To中和中按照名字首字母排序
  2. 好物推荐:眼科OCT影像自动分割工具推荐
  3. 题:斐波那契数列(Fibonacci数列)——一个数最少几步变成斐波那契数列的数
  4. 银行家算法02--申请资源 (操作系统)
  5. 建造者模式(Builder和Director)
  6. android 浏览器 该网站的安全证书有问题
  7. 作文组装计算机,组装电脑作文200字
  8. 小公司程序员怎么进大公司
  9. 【正点原子Linux连载】第三章 初识Qt摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2
  10. python 今日头条视频自动上传_抖音视频怎么上传到今日头条?这个软件可一键操作很方便...