HASH 碰撞問題一直沒真正搞懂?這下不用慌了

這是我參與8月更文挑戰的第10天,活動詳情查看:8月更文挑戰程序員

============================================================================================================================算法

HASH 算法介紹

散列函數(英語:Hash function)又稱散列算法、哈希函數,是一種從任何一種數據中建立小的數字「指紋」的方法。散列函數把消息或數據壓縮成摘要,使得數據量變小,將數據的格式固定下來。該函數將數據打亂混合,從新建立一個叫作散列值(hash values,hash codes,hash sums,或 hashes)的指紋。散列值一般用一個短的隨機字母和數字組成的字符串來表明。編程

哈希表就是一種以 鍵-值(key-indexed) 存儲數據的結構,咱們只要輸入待查找的值即 key,便可查找到其對應的值。數組

哈希的思路很簡單,若是全部的鍵都是整數,那麼就可使用一個簡單的無序數組來實現:將鍵做爲索引,值即爲其對應的值,這樣就能夠快速訪問任意鍵的值。這是對於簡單的鍵的狀況,咱們將其擴展到能夠處理更加複雜的類型的鍵。安全

Hash 算法能夠將一個數據轉換爲一個標誌,這個標誌和源數據的每個字節都有十分緊密的關係。Hash 算法還具備一個特色,就是很難找到逆向規律。服務器

Hash 算法也被稱爲散列算法,Hash 算法雖然被稱爲算法,但實際上它更像是一種思想。Hash 算法沒有一個固定的公式,只要符合散列思想的算法均可以被稱爲是 Hash 算法。markdown

經常使用 HASH 算法

常見 Hash 算法介紹:數據結構

1)MD4架構

MD4(RFC 1320)是 MIT 的 Ronald L. Rivest 在 1990 年設計的,MD 是 Message Digest(消息摘要) 的縮寫。它適用在 32 位字長的處理器上用高速軟件實現——它是基於 32 位操做數地位操做來實現的。負載均衡

2)MD5

MD5(RFC 1321)是 Rivest 於 1991 年對 MD4 的改進版本。它對輸入仍以 512 位分組,其輸出是 4 個 32 位字的級聯,與 MD4 相同。MD5 比 MD4 來得複雜,而且速度較之要慢一點,但更安全,在抗分析和抗差分方面表現更好。

3)SHA-1 及其餘

SHA1 是由 NIST NSA 設計爲同 DSA 一塊兒使用的,它對長度小於 264 的輸入,產生長度爲 160bit 的散列值,所以抗窮舉(brute-force)性更好。SHA-1 設計師基於和 MD4 相同原理,而且模仿了該算法。

HASH 算法的性質

全部散列函數都有以下一個基本特性:若是兩個散列值是不相同的(根據同一函數),那麼這兩個散列值的原始輸入也是不相同的。這個特性是散列函數具備肯定性的結果,具備這種性質的散列函數稱爲單向散列函數。

散列表,它是基於快速存取的角度設計的,也是一種典型的「空間換時間」的作法。顧名思義,該數據結構能夠理解爲一個線性表,可是其中的元素不是緊密排列的,而是可能存在空隙。

好比咱們存儲 70 個元素,但咱們可能爲這 70 個元素申請了 100 個元素的空間。70/100=0.7,這個數字稱爲負載因子。

咱們之因此這樣作,也是爲了「快速存取」的目的。

咱們基於一種結果儘量隨機平均分佈的固定函數 H 爲每一個元素安排存儲位置,這樣就能夠避免遍歷性質的線性搜索,以達到快速存取。

這相似於 70 我的去一個有 100 個椅子的飯店吃飯。散列函數的計算結果是一個存儲單位地址,每一個存儲單位稱爲「桶」。設一個散列表有 m 個桶,則散列函數的值域應爲[0,m-1]。

哈希碰撞是什麼?

若是不一樣的輸入經哈希映射獲得了同一個哈希值,就發生了"哈希碰撞"(collision)。

假設 hash 表的大小爲 11(即有 11 個槽),如今要把一串數據存到表裏:1,2,3,4,5,6...

簡單計算一下:hash(1) = 5, 即數據 1 應該放在 hash 表的第 5 個槽裏;hash(2)=1,因此數據 2 應該放在 hash 表的第 1 個槽裏;hash(3)=1,也就是說,數據 3 也應該放在 hash 表的第 1 個槽裏——因而就形成了碰撞(也稱爲衝突)。

Hash 衝突經常使用解決方案

經常使用的 Hash 衝突解決方法有如下幾種:

1. 開放尋址法

這種方法也稱再散列法,其基本思想是:當關鍵字 key 的哈希地址 p=H(key)出現衝突時,以 p 爲基礎,產生另外一個哈希地址 p1,若是 p1 仍然衝突,再以 p 爲基礎,產生另外一個哈希地址 p2,…,直到找出一個不衝突的哈希地址 pi ,將相應元素存入其中。這種方法有一個通用的再散列函數形式:

Hi=(H(key)+di)% m i=1,2,…,n
複製代碼
  • 線性探測再散列

    dii=1,2,3,…,m-1

  • 二次探測再散列

    di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 )

  • 僞隨機探測再散列

di=僞隨機數序列。

具體實現時,應創建一個僞隨機數發生器,(如 i=(i+p) % m),並給定一個隨機數作起點。

例如,已知哈希表長度 m=11,哈希函數爲:

H(key)= key % 11
複製代碼

則 H(47)=3,H(26)=4,H(60)=5,假設下一個關鍵字爲 69,則 H(69)=3,與 47 衝突。

case1:若是用線性探測再散列處理衝突

下一個哈希地址爲

H1=(3 + 1)% 11 = 4
複製代碼

仍然衝突,再找下一個哈希地址爲

H2=(3 + 2)% 11 = 5
複製代碼

仍是衝突,繼續找下一個哈希地址爲

H3=(3 + 3)% 11 = 6
複製代碼

此時再也不衝突,將 69 填入 5 號單元。

case2:二次探測再散列處理衝突

下一個哈希地址爲:

H1=(3 + 12)% 11 = 4
複製代碼

仍然衝突,再找下一個哈希地址爲

H2=(3 - 12)% 11 = 2
複製代碼

此時再也不衝突,將 69 填入 2 號單元。

case3:用僞隨機探測再散列處理衝突

且僞隨機數序列爲:2,5,9,……..,則下一個哈希地址爲

H1=(3 + 2)% 11 = 5
複製代碼

仍然衝突,再找下一個哈希地址爲

H2=(3 + 5)% 11 = 8
複製代碼

此時再也不衝突,將 69 填入 8 號單元。

2.再哈希法(Rehash)

這種方法是同時構造多個不一樣的哈希函數:

Hi=RH1(key) i=1,2,…,k

當哈希地址 Hi=RH1(key)發生衝突時,再計算 Hi=RH2(key)……,直到衝突再也不產生。這種方法不易產生彙集,但增長了計算時間。

3.鏈地址法(拉鍊法)

這種方法的基本思想是將全部哈希地址爲 i 的元素構成一個稱爲同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第 i 個單元中,於是查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於常常進行插入和刪除的狀況。

鏈地址法優缺點分析:

  • 優勢

1)拉鍊法處理衝突簡單,且無堆積現象,即非同義詞決不會發生衝突,所以平均查找長度較短;

2)因爲拉鍊法中各鏈表上的結點空間是動態申請的,故它更適合於造表前沒法肯定表長的狀況;

3)開放定址法爲減小衝突,要求裝填因子α較小,故當結點規模較大時會浪費不少空間。而拉鍊法中可取α≥1,且結點較大時,拉鍊法中增長的指針域可忽略不計,所以節省空間;

4)在用拉鍊法構造的散列表中,刪除結點的操做易於實現。只要簡單地刪去鏈表上相應的結點便可。

而對開放地址法構造的散列表,空地址單元(即開放地址)都是查找失敗的條件,刪除結點不能簡單地將被刪結點的空間置爲空,不然將截斷在它以後填入散列表的同義詞結點的查找路徑。只能在被刪結點上作刪除標記,而不能真正刪除結點。

  • 缺點

拉鍊法的缺點是:

指針須要額外的空間,故當結點規模較小時,開放定址法較爲節省空間,而若將節省的指針空間用來擴大散列表的規模,可以使裝填因子變小,這又減小了開放定址法中的衝突,從而提升平均查找速度。

Hash 算法用途

1.數據校驗

上面說到的 md5 就是其中的一個, 好像還有一個什麼 SHA, 不過我不知道, 也就不展開探討了.

md5 能夠將一個文件通過計算轉換成一個指定長度的字符串, 能夠防止文件被篡改, 可是經過加密後的字符串很難逆向推出原文.

前面那個例子能夠看到, 即便文件被修改了一點點, 也會致使計算後的值發生很大變化.

2.惟一標識

好比說, 如今有十萬個文件, 給你一個文件, 要你在這十萬個文件中查找是否存在. 一個很笨的辦法就是把每一文件都拿出來, 而後按照二進制串一一進行對比. 可是這個操做註定是比較費時的.

能夠用哈希算法對文件進行計算, 而後比較哈希值是否相同. 由於存在哈希衝突的狀況, 你能夠在相同哈希值的文件再進行二進制串比較.

3.哈希表

在哈希表中使用哈希函數已經並不陌生了, 在此再也不贅述。

4.負載均衡

好比說, 如今又多臺服務器, 來了一個請求, 如何肯定這個請求應該路由到哪一個路由器呢?固然, 必須確保相同的請求通過路由到達同一個服務器. 一種辦法就是保存一張路由關係的表, 好比客戶端 IP 和服務器編號的映射, 可是若是客戶端不少, 勢必查找的時間會很長. 這時, 能夠將客戶端的惟一標識信息(如:IP、username 等)進行哈希計算, 而後與服務器個數取模, 獲得的就是服務器的編號.

5.分佈式存儲

當咱們有大量數據時, 通常會選擇將數據存儲到多個服務器, 爲了提升讀取與寫入的速度嘛. 決定將文件存儲到哪臺服務器, 就能夠經過哈希算法取模的操做來獲得。

總結

HASH 算法做爲編程應用的基礎知識點,本文主要介紹了 HASH 算法碰撞,以及經常使用的碰撞解決方案以下:

  • 開放尋址法

  • 再哈希法

  • 鏈地址法

HASH 算法經常使用於:

  • 數據校驗

  • 惟一標識

  • 哈希表

  • 負載均衡

- END -

做者:架構精進之路,十年研發風雨路,大廠架構師,CSDN 博客專家,專一架構技術沉澱學習及分享,職業與認知升級,堅持分享接地氣兒的乾貨文章,期待與你一塊兒成長。
關注並私信我回復「01」,送你一份程序員成長進階大禮包,歡迎勾搭。

文章首發於同名公衆號《架構精進之路》,原文連接:HASH碰撞問題一直沒真正搞懂?這下不用慌了

Thanks for reading!

相關文章
相關標籤/搜索