Hash Table(二)Hash Function 雜湊法?

講解 Hash Function 的意思

-7 min read

在講 hash table 之前,還必須先瞭解 hash function。 簡單來說,hash funtion 就是把一個值換成另外一個值。

在 hash table 中,會把原始資料的 KEY(例如球員編號)經過 hash 過後,得到 index,把資料存在 hash table 相對應的 index 欄位中。

如:20 => !@!2∂∑œ–¡™¡

特性

  • hash 的值是不可逆的,無法用 hash 值來推導出 hash 前的值。
  • 不同的值經過 hash function 之後也可能會得到相同的 hash 值。

舉例 假設有一個 hash function hashMethod,hash 過的值為 原始值 ÷ 6 的餘數

function hashMethod(key) {  return key % 6}

將五個數字 [6, 12, 15, 18, 21],經過 hash 之後會得到各自的 index [0, 0, 3, 0, 3]

就可以形成 Hash Table:

[  [6, 12, 18],  [],  [],  [15, 21],]

可以看到原始值都不一樣,但 hash 過後,卻出現重複的 0 跟 3,發生了很多衝突(collision),而且無法從 0 跟 3 來推出 hash 前的值是什麼。

衝突(Collision):當 hashtable 內經過 hash 後的值,有至少兩個以上的值相同,就稱作衝突。

如何處理衝突:同一個 index 需要存放很多的 value,這時就需要以 array 的方式記錄,使 hash table 變成二維陣列,上面的例子即為二維陣列。

Hash Function 的應用

世界上有成千上萬種 Hash Function,應用在不同的情境。

資料庫存放的密碼,密碼存放到資料庫前,會先經過 Hash Function 處理,將你的密碼改成一串亂碼,如:123456 經過 hash function 處理變成 @dsop™£åß∂¡,這樣做的原因是:

  • 對於資料庫管理員來說沒有辦法一眼就記下正確的密碼。
  • 且發生資料外洩事件的時候,只要不知道 hash 的方式,駭客也無法存取你真正的密碼。

Hash Funtion 可以對搜尋球員資料的方法有什麼幫助呢?

上一篇文章中舉的例子來說,Array 中最大的球員編號為 666,代表需要使用 666 個空間,但實際球員只有 13 個。

這時候我們就可以透過 hash function 把球員編號都改成另一個值,一個縮小的值。例如:把最大的編號 666 改成 89,把編號 666 的 Peter 的年薪是 92 萬美元 的資料,存在陣列中索引 89 的地方,這樣陣列的長度就從 666 減少到 89,可以有效地減少使用空間,且同時擁有陣列快速搜尋的便利。


舉例幾個 Hash Function

這邊介紹兩個基本的 hash function,但大部分的時候我們不需要深入研究 hash function 的實作方式,我們只需要在意:容不容易發生衝突、時間複雜度是多少這樣的特性,基本上只要看到前面就夠了,所以有興趣再看下去吧!

補充:Load Factor

Load Factor 是一個衡量 hash function 的一項指標。

m: 存放的元素數量 n: hash table 的長度 Load Factor = m / n

  • Load Factor 的值越小,衝突的機率越低,越多未使用的空間。
  • Load Factor 的值越大,衝突的機率越大,越少未使用的空間。
  • 0 < Load Factor < 1

一、Division Method 除法方法

index = Key % hashtable size

這個方法就跟前面提到的例子一樣,把 Key ÷ 雜湊表的長度,取餘數當作 index 存入 Array 中,這邊把 id 當作 Key。hash 後的值 會小於 hash table 的長度,並且大於 0。

0 < hash 後的值 < hash table 的長度

Division Method 除法方法範例

假設有一個 hashtable 的長度為 6,使用 Division Method hash 後,把資料放進 hash table,這樣 hash table 會長怎樣?

原始資料

名字ID
Mike11424
Drake4174
James6253
Kevin554

hash 後的值

名字ID÷ 6 的餘數
Mike114240
Drake41711
James62531
Kevin5542

Hash Table

[  [{ name: 'Mike', ID: 11424 }],  [{ name: 'Drake', ID: 4171 }, { name: 'James', ID: 6253 }],  [[{ name: 'Kevin', ID: 554 }],  [],  [],  [],]

Division Method 的優缺點

優點

  • 速度快,時間複雜度為 O(1)

缺點

  • hash table 的大小需盡量遠離 2 的次方值,若選擇 2 的次方值,較容易發生衝突,以下為例,若選擇 8 為 hash table 的大小,就發生了很多的衝突:
[10123, 123125, 5325, 6435, 1235, 12224, 42145].map(node => node % 8)  [3, 5, 5, 3, 3, 0, 1]

二、Multiplication Method (乘法方法)

A = (√5 - 1) / 2
m = hash table 的長度

公式

index = m(keyA % 1)

過程

- 先把 key * (√5 - 1) / 2- 再求 ÷ 1 的餘數,將會得到 0 ~ 1 的值- 再乘以 m 將會得到介於 0 ~ (m - 1)的值- Math.floor 去掉小數點- 得到 0 ~ (m - 1)的值

可以看到這個方法相對除法方法更複雜一些,雖然還是會有衝突,但衝突的機率較 division method 小。

Multiplication Method 除法方法範例

假設有一個 hashtable 的長度為 6,使用 Multiplication Method hash 後,把資料放進 hash table,這樣 hash table 會長怎樣?

原始資料

名字ID
Mike11424
Drake4174
James6254
Kevin554

Hash 後的值

名字IDhash function(乘法方法) 後的 index
Mike114242
Drake41714
James62541
Kevin5542

原始碼

const arr2 = [11424, 4171, 6254, 554]const A = (Math.sqrt(5) - 1) / 2arr2.map((node) => {  return Math.floor((node * A) % 1 * 6)})// [2, 4, 1, 2]

Hash Table

[  [],  [{ name: 'James', ID: 6253 }],  [{ name: 'Drake', ID: 4171 }, { name: 'Kevin', ID: 554 }]  [],  [[{ name: 'Mike', ID: 11424 }],  [],]