先提一個簡單的問題,如果有一個龐大的字符串?dāng)?shù)組,然后給你一個單獨的字符串,讓你從這個數(shù)組中查找是否有這個字符串并找到它,你會怎么做?
有一個方法最簡單,老老實實從頭查到尾,一個一個比較,直到找到為止,我想只要學(xué)過程序設(shè)計的人都能把這樣一個程序作出來,但要是有程序員把這樣的程序交給用戶,我只能用無語來評價,或許它真的能工作,但...也只能如此了。
最合適的算法自然是使用HashTable(哈希表),先介紹介紹其中的基本知識,所謂Hash,一般是一個整數(shù),通過某種算法,可以把一個字符串"壓縮" 成一個整數(shù),這個數(shù)稱為Hash,當(dāng)然,無論如何,一個32位整數(shù)是無法對應(yīng)回一個字符串的,但在程序中,兩個字符串計算出的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"通過這個算法得到的結(jié)果是0xA26067F3。
是不是把第一個算法改進(jìn)一下,改成逐個比較字符串的Hash值就可以了呢,答案是,遠(yuǎn)遠(yuǎn)不夠,要想得到最快的算法,就不能進(jìn)行逐個的比較,通常是構(gòu)造一個哈希表(Hash Table)來解決問題,哈希表是一個大數(shù)組,這個數(shù)組的容量根據(jù)程序的要求來定義,例如1024,每一個Hash值通過取模運算 (mod)對應(yīng)到數(shù)組中的一個位置,這樣,只要比較這個字符串的哈希值對應(yīng)的位置又沒有被占用,就可以得到最后的結(jié)果了,想想這是什么速度?是的,是最快的O(1),現(xiàn)在仔細(xì)看看這個算法吧
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
}
看到此,我想大家都在想一個很嚴(yán)重的問題:"如果兩個字符串在哈希表中對應(yīng)的位置相同怎么辦?",畢竟一個數(shù)組容量是有限的,這種可能性很大。解決該問題的方法很多,我首先想到的就是用"鏈表",感謝大學(xué)里學(xué)的數(shù)據(jù)結(jié)構(gòu)教會了這個百試百靈的法寶,我遇到的很多算法都可以轉(zhuǎn)化成鏈表來解決,只要在哈希表的每個入口掛一個鏈表,保存所有對應(yīng)的字符串就OK了。
事情到此似乎有了完美的結(jié)局,如果是把問題獨自交給我解決,此時我可能就要開始定義數(shù)據(jù)結(jié)構(gòu)然后寫代碼了。然而Blizzard的程序員使用的方法則是更精妙的方法。基本原理就是:他們在哈希表中不是用一個哈希值而是用三個哈希值來校驗字符串。
中國有句古話"再一再二不能再三再四",看來Blizzard也深得此話的精髓,如果說兩個不同的字符串經(jīng)過一個哈希算法得到的入口點一致有可能,但用三個不同的哈希算法算出的入口點都一致,那幾乎可以肯定是不可能的事了,這個幾率是1:18889465931478580854784,大概是10的 22.3次方分之一,對一個游戲程序來說足夠安全了。
現(xiàn)在再回到數(shù)據(jù)結(jié)構(gòu)上,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. 移到下一個位置,如果已經(jīng)越界,則表示沒有找到,返回
6. 看看是不是又回到了原來的位置,如果是,則返回沒找到
7. 回到3
怎么樣,很簡單的算法吧,但確實是天才的idea, 其實最優(yōu)秀的算法往往是簡單有效的算法。
http://blog.blogchina.com/article_85296.361466.html
FeedBack:
# re: 打造最快的Hash表(轉(zhuǎn))
# re: 打造最快的Hash表(轉(zhuǎn))
# re: 打造最快的Hash表(轉(zhuǎn))
# re: 打造最快的Hash表(轉(zhuǎn))
2009-10-13 23:17 | Veiir
@river
Hash表的目的是讓搜索時間穩(wěn)定在一個數(shù)字上。
比如有幾百個模型包,要依靠名稱取其中一個,那么如果你逐個比較文件名篩選,如果是這個文件名是a開頭還好,萬一是z開頭呢?會不會花費的時間很長?那么你這個游戲畫面的載入時間豈不是很不穩(wěn)定?
如果把這些模型包通過文件名hash運算得出來一個數(shù)字,比如20,放到同一個數(shù)組array的這個數(shù)字的位置array[20],那么以后要讀取這個模型包,僅用知道文件名,通過hash運算得出同樣的數(shù)字20,那么你可以直接去取數(shù)組array的這個數(shù)字位置就好了array[20]。那么你只用了一個hash運算,這個運算所花費的時間是一定的,要比不知道做多少次文件名比較花的時間短多了,取址當(dāng)然是很快的,那么你游戲畫面載入時間就不會很長,也比較穩(wěn)定. 回復(fù) 更多評論
Hash表的目的是讓搜索時間穩(wěn)定在一個數(shù)字上。
比如有幾百個模型包,要依靠名稱取其中一個,那么如果你逐個比較文件名篩選,如果是這個文件名是a開頭還好,萬一是z開頭呢?會不會花費的時間很長?那么你這個游戲畫面的載入時間豈不是很不穩(wěn)定?
如果把這些模型包通過文件名hash運算得出來一個數(shù)字,比如20,放到同一個數(shù)組array的這個數(shù)字的位置array[20],那么以后要讀取這個模型包,僅用知道文件名,通過hash運算得出同樣的數(shù)字20,那么你可以直接去取數(shù)組array的這個數(shù)字位置就好了array[20]。那么你只用了一個hash運算,這個運算所花費的時間是一定的,要比不知道做多少次文件名比較花的時間短多了,取址當(dāng)然是很快的,那么你游戲畫面載入時間就不會很長,也比較穩(wěn)定. 回復(fù) 更多評論
| 只有注冊用戶登錄后才能發(fā)表評論。 | ||
|
||
網(wǎng)站導(dǎo)航:
博客園
IT新聞
BlogJava
博問
Chat2DB
管理
|
||
|
|
| |||||||||
| 日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
|---|---|---|---|---|---|---|---|---|---|
| 26 | 27 | 28 | 29 | 30 | 31 | 1 | |||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | |||
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | |||
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | |||
| 23 | 24 | 25 | 26 | 27 | 28 | 29 | |||
| 30 | 1 | 2 | 3 | 4 | 5 | 6 | |||
常用鏈接
留言簿(12)
隨筆分類
隨筆檔案
- 2011年6月 (1)
- 2011年4月 (1)
- 2011年3月 (1)
- 2011年1月 (1)
- 2010年12月 (1)
- 2010年11月 (2)
- 2010年10月 (5)
- 2010年8月 (10)
- 2010年7月 (7)
- 2010年6月 (11)
- 2010年5月 (4)
- 2010年4月 (1)
- 2010年3月 (1)
- 2009年12月 (3)
- 2009年11月 (1)
- 2009年10月 (2)
- 2009年7月 (2)
- 2009年6月 (1)
- 2009年4月 (1)
- 2008年10月 (3)
- 2008年9月 (1)
- 2008年5月 (2)
- 2008年4月 (4)
- 2008年1月 (1)
- 2007年12月 (2)
- 2007年11月 (4)
- 2007年10月 (2)
- 2007年9月 (4)
- 2007年8月 (7)
- 2007年7月 (2)
- 2007年6月 (3)
- 2007年5月 (3)
- 2007年4月 (6)
- 2007年3月 (9)
- 2007年2月 (4)
- 2007年1月 (13)
文章檔案
相冊
收藏夾
C++
- codejock.
- codeproject
- csdn
- http://www.codeguru.com/
- sdl
- sdl
- vc help
- vc知識庫
- 編程愛好者論壇
- 程序員聯(lián)合開發(fā)網(wǎng)
- 算法
- 天極vc社區(qū)
- 網(wǎng)絡(luò)技術(shù)論壇
- 文件格式大全
- 文件格式大全
- 語音技術(shù)
- 語音技術(shù)
- 中文MSDN
MyFavorite
搜索
積分與排名
- 積分 - 328995
- 排名 - 75
最新評論

- 1.?re: advanced installer 制作exe安裝程序
- Advanced Installer做的exe不能中文命名?
- --石浩
- 2.?re: CListCtrl選中問題
-
你的listctrl失去焦點了,重新SetFocus()即可。
listctrl.SetFocus(); - --Litjerk
- 3.?re: (轉(zhuǎn))用VC獲取本機(jī)MAC地址
-
你這個方法是要必須插入網(wǎng)線才能抓到MAC,不實用@as
- --潘志強(qiáng)
- 4.?re: ftp下載實現(xiàn)
- 但是不知怎么回事,總會在getFile函數(shù)時卡死,最后下載不成功。測試程序都是好的,一旦放到工程中就不行了
- --olive
- 5.?re: bitset 用法整理
- Thank you!
- --aa

