取關鍵字key,使用線性函數 Hash(key) = a * key + b。
在一個班級裡,同齡學生很多。在取學生年齡作為key時,應避免以年份作為key組成部分。
key取平方,擷取中間的幾位作為新的key。數學計算的性質乘積中間幾位和乘數每一位都有關,充分混合key每一位對生成的雜湊值的影響,使對映分佈更均勻。
Hash(key) = key % m
Hash(key) = floor(frac(key * A), m), 0<A<1
Hash(key) = rand(key)
將關鍵字按固定長度分成幾段然後相加。
由於雜湊函數是無限集到有限集的對映,換而言之,有限集的元素對應n個無限集的元素,雜湊碰撞是不可避免的。
當關鍵字key的雜湊地址p=H(key)出現衝突時,遞迴呼叫p = Hi(p)直到沒有衝突。
Hi=(H(key)+di)Hi=(H(key)+di) % m i=1,2,,3....,ni=1,2,,3....,n
根據增量序列di的不同,又分為:
結構示意
pos1
pos2 -> val -> val
pos3 -> val
pos4
...
無限集對映到有限集,有限集的每個元素對應一個連結串列,連結串列儲存無限集對映到有限集的n個元素。
Hi=RHi(key)i=1,2,…,k
遞迴呼叫雜湊函數序列中的函數,直到沒有衝突。
建立溢位連結串列,如發生雜湊碰撞,則使用溢位連結串列。
這是取自lua5.4的
-- lua 5.4
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed,
size_t step) {
unsigned int h = seed ^ cast_uint(l);
for (; l >= step; l -= step)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
return h;
}
#define lmod(s,size) \
(check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1)))))
(h << 5) + (h >> 2)
= (((h << 5) << 2) + ((h >> 2) << 2) >> 2)
= ((h << 7) + h) >> 2
= (129 * h) >> 2
和偽亂數生成演演算法一樣,要讓生成的數儘量隨機--二進位制數的每一個位取0或1的概率都是50%。
移位,互斥或運算充分混合每一位的影響,而加法運算引起多個位的反轉,使hash值的每一個位更加不可預測,以接近不可逆的單向函數。
(h << 5) + (h >> 2) = (129 * h) >> 2。 乘法可以被拆分為加法和移位的組合(即(h << 7)+h ),以混合雜湊值。不過(h << 7 - h) = 127h 會更好些,127是梅森素數(2^n -1)。與線性同餘演演算法(LCG)生成偽亂數一樣,梅森素數127,只需一次移位運算和一次加法運算,且不會被分解,亂數分佈更加均勻。
a%b = a&(b-1) 當 b = 2^n 時等式成立,lua雜湊表的長度保證符合等式成立的條件,lmod使用位運算代替取餘運算,效率更高。
演演算法實際應用詳情請參考我的文章
下面是一些進階雜湊演演算法的思路,需要花費一些時間學習。