hash算法

一、以下引用自什么是哈希算法

比如這里有一萬首歌,給你一首新的歌X,要求你確認這首歌是否在那一萬首歌之內。
無疑,將一萬首歌一個一個比對非常慢。但如果存在一種方式,能將一萬首歌的每首數據濃縮到一個數字(稱為哈希碼)中,于是得到一萬個數字,那么用同樣的算法計算新的歌X的編碼,看看歌X的編碼是否在之前那一萬個數字中,就能知道歌X是否在那一萬首歌中。
作為例子,如果要你組織那一萬首歌,一個簡單的哈希算法就是讓歌曲所占硬盤的字節數作為哈希碼。這樣的話,你可以讓一萬首歌“按照大小排序”,然后遇到一首新的歌,只要看看新的歌的字節數是否和已有的一萬首歌中的某一首的字節數相同,就知道新的歌是否在那一萬首歌之內了。
當然這個簡單的哈希算法很容易出現兩者同樣大小的歌曲,這就是發生了碰撞。而好的哈希算法發生碰撞的幾率非常小。

二、以下引用自哈希表(Hash Table)及散列法(Hashing)

數組的特點是:尋址容易,插入和刪除困難;而鏈表的特點是:尋址困難,插入和刪除容易。那么我們能不能綜合兩者的特性,做出一種尋址容易,插入刪除也容易的數據結構?答案是肯定的,這就是我們要提起的哈希表,哈希表有多種不同的實現方法,我接下來解釋的是最常用的一種方法——拉鏈法,我們可以理解為“鏈表的數組”,如圖:


image.png

左邊很明顯是個數組,數組的每個成員包括一個指針,指向一個鏈表的頭,當然這個鏈表可能為空,也可能元素很多。我們根據元素的一些特征把元素分配到不同的鏈表中去,也是根據這些特征,找到正確的鏈表,再從鏈表中找出這個元素。元素特征轉變為數組下標的方法就是散列法。散列法當然不止一種,我下面列出三種比較常用的。

  • 除法散列法
    最直觀的一種,上圖使用的就是這種散列法,公式:
    index = value % 16
    學過匯編的都知道,求模數其實是通過一個除法運算得到的,所以叫“除法散列法”。
  • 平方散列法
    求index是非常頻繁的操作,而乘法的運算要比除法來得省時(對現在的CPU來說,估計我們感覺不出來),所以我們考慮把除法換成乘法和一個位移操作。公式:
    index = (value * value) >> 28
    如果數值分配比較均勻的話這種方法能得到不錯的結果,但我上面畫的那個圖的各個元素的值算出來的index都是0——非常失敗。也許你還有個問題,value如果很大,value * value不會溢出嗎?答案是會的,但我們這個乘法不關心溢出,因為我們根本不是為了獲取相乘結果,而是為了獲取index。
  • 斐波那契(Fibonacci)散列法
    平方散列法的缺點是顯而易見的,所以我們能不能找出一個理想的乘數,而不是拿value本身當作乘數呢?答案是肯定的。
  • 對于16位整數而言,這個乘數是40503
  • 對于32位整數而言,這個乘數是2654435769
  • 對于64位整數而言,這個乘數是11400714819323198485

這幾個“理想乘數”是如何得出來的呢?這跟一個法則有關,叫黃金分割法則,而描述黃金分割法則的最經典表達式無疑就是著名的斐波那契數列,如果你還有興趣,就到網上查找一下“斐波那契數列”等關鍵字,我數學水平有限,不知道怎么描述清楚為什么,另外斐波那契數列的值居然和太陽系八大行星的軌道半徑的比例出奇吻合,很神奇,對么?
對我們常見的32位整數而言,公式:
index = (value * 2654435769) >> 28
如果用這種斐波那契散列法的話,那我上面的圖就變成這樣了:


Paste_Image.png
三、以下引自暴雪公司經典字符串hash公式

中國有句古話"再一再二不能再三再四",看來Blizzard也深得此話的精髓,如果說兩個不同的字符串經過一個哈希算法得到的入口點一致有可能,但用三個不同的哈希算法算出的入口點都一致,那幾乎可以肯定是不可能的事了,這個幾率是1:18889465931478580854784,大概是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)
{ //比較的是Table中存儲的另外兩個Hash函數的值,Table中不存儲字符串
  if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB) 
   return nHashPos; 
  else  //沖突處理
   nHashPos = (nHashPos + 1) % nTableSize;
  if (nHashPos == nHashStart) 
   break; 
 }
return -1; //Error value 
}
  • 計算出字符串的三個哈希值(一個用來確定位置,另外兩個用來校驗)
  • 察看哈希表中的這個位置
  • 哈希表中這個位置為空嗎?如果為空,則肯定該字符串不存在,返回
  • 如果存在,則檢查其他兩個哈希值是否也匹配,如果匹配,則表示找到了該字符串,返回
  • 移到下一個位置,如果已經越界,則表示沒有找到,返回
  • 看看是不是又回到了原來的位置,如果是,則返回沒找到
  • 回到3
    怎么樣,很簡單的算法吧,但確實是天才的idea, 其實最優秀的算法往往是簡單有效的算法,Blizzard被稱為最卓越的游戲制作公司,不愧于此。(轉載注:這種解決hash collision的方法相對于用linked list方法的缺點在于,hash表的entry只能代表一個字符串,如果hash表滿了則無法在向hash表中加入新的entry)

應用參見魔獸哈希算法封裝和測試
魔獸打包管理器源碼
暴雪公司經典字符串hash公式測試

四、常見hash算法

常見字符串hash函數
hask表算法

五、數據結構和算法(Golang實現)(26)查找算法-哈希表
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容