• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 126,  comments - 73,  trackbacks - 0

            先提一個(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),但要是有程序員把這樣的程序交給用戶(hù),我只能用無(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ó)有句古話(huà)"再一再二不能再三再四",看來(lái)Blizzard也深得此話(huà)的精髓,如果說(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)單有效的算法。

            http://blog.blogchina.com/article_85296.361466.html

            posted on 2007-08-21 11:51 我風(fēng) 閱讀(6957) 評(píng)論(8)  編輯 收藏 引用

            FeedBack:
            # re: 打造最快的Hash表(轉(zhuǎn))
            2007-11-03 01:07 | wo
            不是很懂,"從這個(gè)數(shù)組中查找是否有這個(gè)字符串并找到它",和這怎么聯(lián)系上的?謝謝  回復(fù)  更多評(píng)論
              
            # re: 打造最快的Hash表(轉(zhuǎn))
            2007-12-05 17:07 | 祁祁
            果然精妙  回復(fù)  更多評(píng)論
              
            # re: 打造最快的Hash表(轉(zhuǎn))
            2009-03-25 17:03 | akore
            無(wú)聊, 跟在字符串?dāng)?shù)組中找到目標(biāo)字符串有個(gè)屁聯(lián)系!  回復(fù)  更多評(píng)論
              
            # re: 打造最快的Hash表(轉(zhuǎn))
            2009-04-21 14:41 | brightcoder
            跟你提的問(wèn)題有什么關(guān)聯(lián)  回復(fù)  更多評(píng)論
              
            # re: 打造最快的Hash表(轉(zhuǎn))[未登錄](méi)
            2009-05-22 00:41 | joe
            @akore
            對(duì)你相當(dāng)無(wú)語(yǔ)..先看懂再說(shuō)吧  回復(fù)  更多評(píng)論
              
            # re: 打造最快的Hash表(轉(zhuǎn))
            2009-09-01 13:59 | charrie
            頂,最后三個(gè)哈希值的還沒(méi)有太明白,前面的正是我需要的,謝謝樓主!  回復(fù)  更多評(píng)論
              
            # re: 打造最快的Hash表(轉(zhuǎn))
            2009-10-12 16:32 | river
            如果只進(jìn)行一次這樣的查找操作,那么順序查找,當(dāng)然是唯一選擇;
            你說(shuō)的高效,是針對(duì)要進(jìn)行無(wú)數(shù)次這樣在同一個(gè)長(zhǎng)串中查找子串的操作的情況,對(duì)吧?道理是很簡(jiǎn)單,確實(shí)還不錯(cuò)。
            你這個(gè)文章,意思沒(méi)有表達(dá)的很清楚,怨不得別人提出質(zhì)疑;
            同時(shí),題目為“打造最快的hash表”,我不太認(rèn)同。  回復(fù)  更多評(píng)論
              
            # re: 打造最快的Hash表(轉(zhuǎn))
            2009-10-13 23:17 | Veiir
            @river
            Hash表的目的是讓搜索時(shí)間穩(wěn)定在一個(gè)數(shù)字上。

            比如有幾百個(gè)模型包,要依靠名稱(chēng)取其中一個(gè),那么如果你逐個(gè)比較文件名篩選,如果是這個(gè)文件名是a開(kāi)頭還好,萬(wàn)一是z開(kāi)頭呢?會(huì)不會(huì)花費(fèi)的時(shí)間很長(zhǎng)?那么你這個(gè)游戲畫(huà)面的載入時(shí)間豈不是很不穩(wěn)定?

            如果把這些模型包通過(guò)文件名hash運(yùn)算得出來(lái)一個(gè)數(shù)字,比如20,放到同一個(gè)數(shù)組array的這個(gè)數(shù)字的位置array[20],那么以后要讀取這個(gè)模型包,僅用知道文件名,通過(guò)hash運(yùn)算得出同樣的數(shù)字20,那么你可以直接去取數(shù)組array的這個(gè)數(shù)字位置就好了array[20]。那么你只用了一個(gè)hash運(yùn)算,這個(gè)運(yùn)算所花費(fèi)的時(shí)間是一定的,要比不知道做多少次文件名比較花的時(shí)間短多了,取址當(dāng)然是很快的,那么你游戲畫(huà)面載入時(shí)間就不會(huì)很長(zhǎng),也比較穩(wěn)定.  回復(fù)  更多評(píng)論
              

            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            <2012年10月>
            30123456
            78910111213
            14151617181920
            21222324252627
            28293031123
            45678910

            常用鏈接

            留言簿(12)

            隨筆分類(lèi)

            隨筆檔案

            文章檔案

            相冊(cè)

            收藏夾

            C++

            MyFavorite

            搜索

            •  

            積分與排名

            • 積分 - 326203
            • 排名 - 75

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            色妞色综合久久夜夜| 久久国产精品一区二区| 青青草原精品99久久精品66| 国产精品欧美久久久天天影视| 久久噜噜电影你懂的| 久久久精品国产| 99久久国产综合精品五月天喷水 | 日批日出水久久亚洲精品tv| 狠狠色狠狠色综合久久| segui久久国产精品| 亚洲欧美成人综合久久久| 精品国产乱码久久久久久浪潮| 蜜臀久久99精品久久久久久小说 | 久久久久久精品免费免费自慰| 久久不射电影网| 日韩精品久久久久久免费| 久久夜色撩人精品国产小说| 国产99精品久久| 久久天天躁狠狠躁夜夜96流白浆| 性做久久久久久久久老女人| 国产精品综合久久第一页| 2022年国产精品久久久久| 东方aⅴ免费观看久久av| 伊人久久成人成综合网222| 狠狠色综合久久久久尤物| 久久―日本道色综合久久| 久久精品国产清高在天天线| 久久亚洲AV无码精品色午夜麻豆| 色欲综合久久躁天天躁| 久久精品无码一区二区三区免费 | 精品久久久久久无码国产| 999久久久无码国产精品| 国内精品人妻无码久久久影院 | 国产精品久久久久影院嫩草| 久久亚洲精品中文字幕| 亚洲va久久久噜噜噜久久男同 | 国产精品国色综合久久| 久久久久久人妻无码| 成人国内精品久久久久影院| 久久久久亚洲Av无码专| 99久久婷婷国产综合亚洲|