暴雪的hash算法[翻译]
原文来自:http://sfsrealm.hopto.org/inside_mopaq/chapter2.htm#hashes
促进历史进步的大多数契机都是在解决特定问题的过程中产生的,本文讨论一下MPQ格式的合适解决方案。
----MPQ是暴雪的一种文本压缩格式,可以压缩包括坐标、算法、声音、动画、字符串等。
HASHS
问题:你可能有一个非常长的字符串数组,现在有一个新字符串,想要判断该字符串是否在数组中,简单粗暴的方法是挨个比较,但最大的弊端就是大部分场合下速度慢到不能忍。
解决方案:hash,hash是一种较小的数据类型,用来替代其他的较大的数据类型(通常是字符串),上述的问题可以存储字符串的hash到数组中,然后比较新字符串的hash是否在数组中,如果数组中的一个hash匹配了新字符串的hash,该字符串就存在于字符串数组中。hash能够将该比较过程提速约100倍,具体的效率提升依赖于数组的长度和字符串的平均长度,下面是一个简单的hash算法
unsigned long HashString(char *lpszString) |
||
unsigned long ulHash = 0xf1e2d3c4;
|
||
ulHash <<= 1; |
||
}
|
||
} |
||
上面的代码提供了一种简单的hash算法,累加字符串的所有字符,在累加字符串之前hash的基准值左移一位。这个算法简单也很有用,但会生成大量相似的输出,并且经常是在较小的值域内产生大量冲突,冲突是指两个不同的字符串hash后值一样。
MPQ格式使用了非常复杂的hash算法来生成全部不可预期的值,事实上该hash算法是一个单向算法,单向算法是指无法通过hash值反算出原始字符串。下面是这个特别的算法:
unsigned long HashString(char *lpszFileName, unsigned long dwHashType) |
||
unsigned char *key = (unsigned char *)lpszFileName; |
||
ch = toupper(*key++);
|
||
} |
||
} |
||
hash表
问题:你使用一个类似上面例子的索引,但你的程序要求极快的速度,但是索引并没有足够快,你唯一能让索引速度变快的方法是不检索数组中的每一个hash值。更或者你可以只做一次比较就能确定字符串是否存在于数组的任何一个位置,听起来爽吧?
解决方案:hash表是一种特殊的数组,目标字符串的偏移量是目标字符串的hash值。根据应用程序需要创建指定大小的数组,(比如说1024,通常是2的n次方),想要确认新字符串是否在表里,为了获取新字符串在hash表的位置,将新字符串的hash按照hash表大小进行取模运算,余数就是新字符串在hash表的偏移量。然后将hash表指定偏移位置的字符串同新增字符串进行比较,如果不存在或者不相等,那么该字符串没有存在于hash表也即字符串数组中。代码如下:
int GetHashTablePos(char *lpszString, SOMESTRUCTURE *lpTable, int nTableSize)
|
||
int nHash = HashString(lpszString), nHashPos = nHash % nTableSize;
|
||
return nHashPos; |
||
else |
||
return -1; //Error value |
||
} |
||
上面的方法有一个明显的问题,如果发生了冲突即两个不同的字符串hash后的值是一样的,又该怎么办?显然它们不能在hash表占用同样的位置。一个常用的解决方式是hash的每一个节点不再代表一个元素,而是一个列表的指针,该列表保存hash值为该偏移的所有字符串。
MPQs使用一个文件名称表来跟踪文件内部信息,但这个表和普通的hash表并不完全一样。首先:不是使用文件名称的hash值作为偏移量并存储真实的文件名来做校验;MPQs根本不会存储文件名称,而是使用三个不同的hash值:一个用来做hash表的偏移位置,两个用来做校验,两个用来校验的hash代替了真实的文件名称。这样还是有可能会出现两个不同的字符串hash到同样的三个值上,不过这样的概率非常小,大概是1:18889465931478580854784,谁识数的数一数,我是看不懂了,这样的概率对每一个人来说都是足够安全的。
另外MPQs和通常的hash实现不一样的地方是:没有为hash表的每一个入口保存一个链表,在冲突发生的时候继续向下找,直到找到一个未被占用的槽。
下面的代码描述了MPQs定位一个用来读的文件的过程
int GetHashTablePos(char *lpszString, MPQHASHTABLE *lpTable, int nTableSize) |
|||
const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2; |
|||
if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB) |
|||
return nHashPos; |
|||
else |
|||
nHashPos = (nHashPos + 1) % nTableSize; |
|||
if (nHashPos == nHashStart) |
|||
break; |
|||
}
|
|||
} |
|||
下面是对代码的解释,大致符合程序查找并读取文件的过程。
1.计算三个hash值并保存在变量里。
2.移动到hash偏移量的入口。
3.该入口是否未使用,如果是,停止搜索,返回查找失败。
4.是否入口的两个检验hash等于计算出来的两个检验hash,如果是,停止搜索并返回该入口。
5.移动到链表的下一个入口,如果到了末尾就重新从开始查找。
6.如果查找的偏移量和计算的hash偏移量是一样的,已经搜索了整个表仍然没有找到,返回未查找到。
7.从第三步重新开始。
如果留心的话,可能会注意到该表是不可扩展的,如果所有的入口都被占据,那就不能够插入任何的文件名称,是的,这个表就是这样设计的,装载因子最大为1.0.甚至该表不可以动态扩大,因为扩大会导致所有的hash入口失效,并且不能重新生成该表,因为不知道相应的文件名称。
-----如果只是为了一个传奇的hash,这里就应该够了,hash的原理、hash的应用都有足够的说明。
压缩
问题:如果你有一个巨大的程序,比如50m,想要通过网络传输,但这是一个非常巨大的下载量,人们也可能没有足够的耐心来等待它下载完毕。
解决方案:压缩。压缩是将大量数据存储在较小空间的技术,差不多有上百种压缩算法,工作方式各不相同。MPQs使用的算法是数据压缩lib的方式,太过复杂不做介绍,会另开文章介绍。
本文的后半部分和主题关系不大,分篇章翻译,over。
暴雪的hash算法[翻译]的更多相关文章
- Hash冲突的解决--暴雪的Hash算法
Hash冲突的解决--暴雪的Hash算法https://usench.iteye.com/blog/2199399https://www.bbsmax.com/A/kPzOO7a8zx/
- 暴雪HASH算法(转)
暴雪公司有个经典的字符串的hash公式 先提一个简单的问题,假如有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做? 有一个方法最简单,老老实实 ...
- hash算法总结收集
hash算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系,(每一个真实值只能有一个键值,但是一个键值可以对应多个真实值),这样可以快速在数组等条件中里面存取数据. ...
- 【学】常用hash算法的介绍
基本知识 Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映 ...
- hash算法
作者:July.wuliming.pkuoliver 说明:本文分为三部分内容, 第一部分为一道百度面试题Top K算法的详解:第二部分为关于Hash表算法的详细阐述:第三部分为打造一个最快的Hash ...
- Hash算法初见
hash算法 (hashmap 实现原理) Hash ,一般翻译做“ 散列” ,也有直接音译为“ 哈希” 的,就是把任意长度的输入(又叫做预映射, pre-image ),通过散列算法,变换成固定 ...
- 【整理】hash算法原理及常见函数
简介 Hash,一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值. 散列表 ...
- [速成]了解一致性hash算法
定义 一致性hash算法,在维基百科的定义是: Consistent hashing is a special kind of hashing such that when a hash table ...
- 【数据结构与算法】一致性Hash算法及Java实践
追求极致才能突破极限 一.案例背景 1.1 系统简介 首先看一下系统架构,方便解释: 页面给用户展示的功能就是,可以查看任何一台机器的某些属性(以下简称系统信息). 消息流程是,页面发起请求查看指定机 ...
随机推荐
- xpath定位实列
这次将讲Xpath定位! 什么是Xpath: Path就是路径,xpath也类似,就像 在DOS中 D:\Auto\jack. Xpath的使用: 1:在火狐下载 FirePath 插件(图1),然后 ...
- 定位IO瓶颈的方法,iowait低,IO就没有到瓶颈?
通过分析mpstat的iowait和iostat的util%,判断IO瓶颈 IO瓶颈往往是我们可能会忽略的地方(我们常会看top.free.netstat等等,但经常会忽略IO的负载情况),今天给大家 ...
- 高举 Vue-SSR
将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序. SSR的目的 To solve 首屏渲染问题 SEO问 ...
- Golang - 爬虫案例实践
目录 Golang - 爬虫案例实践 1. 爬虫步骤 2. 正则表达式 3. 并发爬取美图 Golang - 爬虫案例实践 1. 爬虫步骤 明确目标(确定在哪个网址搜索) 爬(爬下数据) 取(去掉没用 ...
- java 反射之获取泛型对象的所有字段与对应的值(包括父类的)
上代码: public static void main(String[] args) throws IntrospectionException { SysUser obj = new SysUse ...
- (转载)关于初学者上传文件到github的方法
说来也惭愧,我是最近开始用github,小白一个,昨天研究了一个下午.终于可以上传了,所以今天写点,一来分享是自己的一些经验,二来也是做个记录,万一哪天又不记得了:) 废话不多说,直接来,这次主要介绍 ...
- Mysql学习总结(39)——30条MySql语句优化技巧
1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉 ...
- redis--周边知识点
一般Redis服务器内存超过128G内存的机器是非常少的 很少在redis中缓存视频,除非是快播,一般都是缓存文本字段 redis可视化的工具和SQL的管理工具是不一样的,最好是使用REDIS的she ...
- 【日常学习】【搜索/递归】codevs2802 二的幂次方题解
转载请注明出处 [ametake版权全部]http://blog.csdn.net/ametake欢迎来看 题目描写叙述 Description 不论什么一个正整数都能够用2的幂次方表示. 比如:13 ...
- POJ 1654
不知 是怎么看出的精度不够,吸经验吧. #include <iostream> #include <cstdio> #include <cstring> #incl ...