哈希字符串—暴雪

BS一下自己的物質(zhì),支持一下 狗父叙,同時(shí)貼出

《暴雪公司有個(gè)經(jīng)典的字符串的hash公式》(網(wǎng)上搜的)

打造最快的Hash表(和Blizzard的對(duì)話)

開(kāi)元最近學(xué)習(xí)了一下Blizzard的MPQ文件格式几莽,頗有一些心得迅办,其中一條就是對(duì)HastTable的理解,很想寫(xiě)出來(lái)給大家共享章蚣,感謝Justin Olbrantz的文章《Inside MoPaQ》站欺,大多認(rèn)識(shí)來(lái)源于此。

先提一個(gè)簡(jiǎn)單的問(wèn)題纤垂,如果有一個(gè)龐大的字符串?dāng)?shù)組矾策,然后給你一個(gè)單獨(dú)的字符串,讓你從這個(gè)數(shù)組中查找是否有這個(gè)字符串并找到它峭沦,你會(huì)怎么做蝴韭?

有一個(gè)方法最簡(jiǎn)單,老老實(shí)實(shí)從頭查到尾熙侍,一個(gè)一個(gè)比較,直到找到為止履磨,我想只要學(xué)過(guò)程序設(shè)計(jì)的人都能把這樣一個(gè)程序作出來(lái)蛉抓,但要是有程序員把這樣的程序交給用戶,我只能用無(wú)語(yǔ)來(lái)評(píng)價(jià)剃诅,或許它真的能工作巷送,但...也只能如此了。

最合適的算法自然是使用HashTable(哈希表)矛辕,先介紹介紹其中的基本知識(shí)笑跛,所謂Hash,一般是一個(gè)整數(shù)聊品,通過(guò)某種算法飞蹂,可以把一個(gè)字符串"壓縮" 成一個(gè)整數(shù),這個(gè)數(shù)稱(chēng)為Hash翻屈,當(dāng)然陈哑,無(wú)論如何,一個(gè)32位整數(shù)是無(wú)法對(duì)應(yīng)回一個(gè)字符串的,但在程序中惊窖,兩個(gè)字符串計(jì)算出的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的這個(gè)算法是非常高效的,被稱(chēng)為"One-Way Hash"界酒,舉個(gè)例子圣拄,字符串"unitneutralacritter.grp"通過(guò)這個(gè)算法得到的結(jié)果是0xA26067F3毁欣。

是不是把第一個(gè)算法改進(jìn)一下族铆,改成逐個(gè)比較字符串的Hash值就可以了呢,答案是逝淹,遠(yuǎn)遠(yuǎn)不夠,要想得到最快的算法欣簇,就不能進(jìn)行逐個(gè)的比較,通常是構(gòu)造一個(gè)哈希表(Hash Table)來(lái)解決問(wèn)題横殴,哈希表是一個(gè)大數(shù)組,這個(gè)數(shù)組的容量根據(jù)程序的要求來(lái)定義文狱,例如1024,每一個(gè)Hash值通過(guò)取模運(yùn)算 (mod)對(duì)應(yīng)到數(shù)組中的一個(gè)位置尚猿,這樣,只要比較這個(gè)字符串的哈希值對(duì)應(yīng)的位置又沒(méi)有被占用,就可以得到最后的結(jié)果了,想想這是什么速度忍捡?是的,是最快的O(1)凌埂,現(xiàn)在仔細(xì)看看這個(gè)算法吧

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

}

看到此伏恐,我想大家都在想一個(gè)很?chē)?yán)重的問(wèn)題:"如果兩個(gè)字符串在哈希表中對(duì)應(yīng)的位置相同怎么辦?",畢竟一個(gè)數(shù)組容量是有限的,這種可能性很大闻鉴。解決該問(wèn)題的方法很多,我首先想到的就是用"鏈表",感謝大學(xué)里學(xué)的數(shù)據(jù)結(jié)構(gòu)教會(huì)了這個(gè)百試百靈的法寶,我遇到的很多算法都可以轉(zhuǎn)化成鏈表來(lái)解決,只要在哈希表的每個(gè)入口掛一個(gè)鏈表送巡,保存所有對(duì)應(yīng)的字符串就OK了蔽介。

事情到此似乎有了完美的結(jié)局,如果是把問(wèn)題獨(dú)自交給我解決圆凰,此時(shí)我可能就要開(kāi)始定義數(shù)據(jù)結(jié)構(gòu)然后寫(xiě)代碼了。然而B(niǎo)lizzard的程序員使用的方法則是更精妙的方法累铅≡拘耄基本原理就是:他們?cè)诠1碇胁皇怯靡粋€(gè)哈希值而是用三個(gè)哈希值來(lái)校驗(yàn)字符串。

中國(guó)有句古話"再一再二不能再三再四"娃兽,看來(lái)Blizzard也深得此話的精髓菇民,如果說(shuō)兩個(gè)不同的字符串經(jīng)過(guò)一個(gè)哈希算法得到的入口點(diǎn)一致有可能,但用三個(gè)不同的哈希算法算出的入口點(diǎn)都一致投储,那幾乎可以肯定是不可能的事了第练,這個(gè)幾率是1:18889465931478580854784,大概是10的 22.3次方分之一玛荞,對(duì)一個(gè)游戲程序來(lái)說(shuō)足夠安全了娇掏。

現(xiàn)在再回到數(shù)據(jù)結(jié)構(gòu)上,Blizzard使用的哈希表沒(méi)有使用鏈表勋眯,而采用"順延"的方式來(lái)解決問(wèn)題婴梧,看看這個(gè)算法:

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. 計(jì)算出字符串的三個(gè)哈希值(一個(gè)用來(lái)確定位置下梢,另外兩個(gè)用來(lái)校驗(yàn))

2. 察看哈希表中的這個(gè)位置

3. 哈希表中這個(gè)位置為空嗎?如果為空塞蹭,則肯定該字符串不存在孽江,返回

4. 如果存在,則檢查其他兩個(gè)哈希值是否也匹配浮还,如果匹配竟坛,則表示找到了該字符串,返回

5. 移到下一個(gè)位置钧舌,如果已經(jīng)越界担汤,則表示沒(méi)有找到,返回

6. 看看是不是又回到了原來(lái)的位置洼冻,如果是崭歧,則返回沒(méi)找到

7. 回到3

怎么樣,很簡(jiǎn)單的算法吧撞牢,但確實(shí)是天才的idea, 其實(shí)最優(yōu)秀的算法往往是簡(jiǎn)單有效的算法率碾,

Blizzard被稱(chēng)為最卓越的游戲制作公司,不愧于此屋彪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末所宰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子畜挥,更是在濱河造成了極大的恐慌仔粥,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟹但,死亡現(xiàn)場(chǎng)離奇詭異躯泰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)华糖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)麦向,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人客叉,你說(shuō)我怎么就攤上這事诵竭。” “怎么了兼搏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵卵慰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我向族,道長(zhǎng)呵燕,這世上最難降的妖魔是什么棠绘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任件相,我火速辦了婚禮再扭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夜矗。我一直安慰自己泛范,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布紊撕。 她就那樣靜靜地躺著罢荡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪对扶。 梳的紋絲不亂的頭發(fā)上区赵,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音浪南,去河邊找鬼笼才。 笑死,一個(gè)胖子當(dāng)著我的面吹牛络凿,可吹牛的內(nèi)容都是我干的骡送。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼絮记,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摔踱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起怨愤,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤派敷,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后憔四,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體膀息,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年了赵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潜支。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柿汛,死狀恐怖冗酿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情络断,我是刑警寧澤裁替,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站貌笨,受9級(jí)特大地震影響弱判,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锥惋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一昌腰、第九天 我趴在偏房一處隱蔽的房頂上張望开伏。 院中可真熱鬧,春花似錦遭商、人聲如沸固灵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)巫玻。三九已至,卻和暖如春祠汇,著一層夾襖步出監(jiān)牢的瞬間仍秤,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工可很, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徒扶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓根穷,卻偏偏與公主長(zhǎng)得像姜骡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屿良,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一.概念 哈希表就是一種以 鍵-值(key-indexed) 存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)圈澈,我們只要輸入待查找的值即key,即可...
    lfp901020閱讀 2,985評(píng)論 0 2
  • 按圖索驥尘惧。PHP中使用最為頻繁的數(shù)據(jù)類(lèi)型非字符串和數(shù)組莫屬康栈,PHP比較容易上手也得益于非常靈活的數(shù)組類(lèi)型。 在開(kāi)始...
    拉風(fēng)的老衲閱讀 732評(píng)論 0 3
  • 作者:July喷橙、wuliming啥么、pkuoliver 說(shuō)明:本文分為三部分內(nèi)容,第一部分為一道百度面試題Top K...
    cyj_ya閱讀 811評(píng)論 0 0
  • 目錄 1贰逾、演示數(shù)據(jù)類(lèi)型的實(shí)現(xiàn) 2悬荣、簡(jiǎn)單動(dòng)態(tài)字符串 3、鏈表 4疙剑、字典 5氯迂、跳躍表 6、整數(shù)集合 7言缤、壓縮列表 8嚼蚀、...
    匆匆歲月閱讀 1,177評(píng)論 0 28
  • 前言 哈希(Hash)或者說(shuō)散列表,它是一種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)管挟。Hash 表是一種特殊的數(shù)據(jù)結(jié)構(gòu)轿曙,它同數(shù)組、鏈表以及二叉...
    ZhengYaWei閱讀 17,141評(píng)論 13 107