哈希概述

一、哈希概述

Hash,一般翻譯做“散列”,也有直接音譯爲“哈希”的,就是把任意長度的輸入(又叫做預映射pre-image)通過散列算法變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不同的輸入可能會散列成相同的輸出,所以不可能從散列值來確定唯一的輸入值。(by百度百科)

簡單來說,哈希是一種映射的體現,可以說是數據離散化、map等等的同胞了。

主要原理就是把大範圍映射到小範圍,因此輸入範圍必須和小範圍相當或者比它更小,否則增加衝突。

二、哈希表

哈希表是一種根據關鍵碼去尋找值的數據映射結構,它將大型數據增加、刪除、查找,每次操作算法複雜度均攤複雜度爲o(n)。

舉個形象的例子,我新建了一個通訊錄:

假如說我要找xuao恩聊天,我會找x,假如我要打電話給dalao gmk 我會找d。 

哈希表建立了一個映射關係f(key) =address,對於這個情景來說f(xuao恩)=x,f(dalao gmk)=d。

 

三、哈希函數

哈希函數(Hash Function),也稱爲散列函數或雜湊函數。哈希函數是一個公開函數

哈希函數是一個公開函數,可以將任意長度的消息M映射成爲一個長度較短且長度固定的值H(M),稱H(M)爲哈希值、散列值(Hash Value)、雜湊值或者消息摘要(Message Digest)。

對於c++、noip使用來說,Hash函數具有如下特點:

易壓縮:對於任意大小的輸入,Hash值的長度很小,在實際應用中,Hash值長度是固定。

易計算:對於任意給定的消息,計算求其Hash值比較容易。

單向性:哈希函數逼近單向函數,所以可以用來對數據進行加密。(單項函數:如果某個函數在給定輸入的時候,很容易計算出其結果來;而當給定結果的時候,很難計算出輸入來)

抗碰撞性:理想的Hash函數是無碰撞的,但在實際算法的設計中很難做到這一點。

有兩種抗碰撞性:一種是弱抗碰撞性,即對於給定的消息,要發現另一個消息,滿足在計算上是不可行的;另一種是強抗碰撞性,即對於任意一對不同的消息,使得在計算上也是不可行的。

憑藉上述規律,我們來解釋一下內容

四、哈希算法

一般的說,Hash函數可以簡單的劃分爲如下幾類:
1. 加法Hash;
2. 位運算Hash;
3. 乘法Hash; 
4. 除法Hash;
5. 查表Hash;
6. 混合Hash;

哈希種類多種多樣,可以自行設計(只要你覺得自己很dalao)。

五、取餘法

哈希表示一個索引1到m的數組,m根據你的空間限制自己決定。函數的值爲[1,m]。

如果兩個函數f(u1)==f(u2)這就產生了哈希衝突。

取餘法是一種很簡單的常用方法,即h(k)=k mod m,其中 seed 和 m 都是大小合適的質數,m就是hash表的大小。

int hash(string x)
{
	int seed=131;
	int m=10007;
	int Hash=0;
	int len=x.length()-1;
	for(int i=0;i<=len;++i)
	{
		Hash=(Hash*seed+x[i]-'0')%m;//‘0’都可以 
	}
	return Hash;
}

五、哈希衝突

爲了方便理解,我再延續上面那個例子:

假如我又加了喜羊羊爲好友,這可怎麼辦呢,f(xuao恩)=f(喜羊羊),我和xuao恩關係非常好,可不能將xuao恩擠掉,但我又想結識青青草原的大名人,衝突由此產生。

具體定義:在哈希表中,不同的關鍵字值對應到同一個存儲位置的現象。即關鍵字K1≠K2,但f(K1)= f(K2)。均勻的哈希函數可以減少衝突,但不能避免衝突。發生衝突後,必須解決;也即必須尋找下一個可用地址。

 

六、解決衝突的方法(提供幾個額外的思路)

1.直接定址法:

直接定址法是根據關鍵字得到的某一線性函數值作爲散列表地址。f(key) = a*key+b,(其中a,b都爲常數)

這種方法比較簡單,但也有缺陷,這種定址方法需要事先知道關鍵字的分佈情況,且查找時比較適合在表小且連續的表中查找。

2.除留餘數法:

除留餘數法是經常用來構造散列表的一種方法。看名字很容易想到這種定址方法就是:用關鍵字除某一個數p得到的餘數作爲散列表的地址,f(key) = key % p,(這裏的p就爲表的大小)。

但是這裏又引來問題了,很可能有好幾個關鍵字都映射在一個地址上,比如:當表長p爲5,關鍵字key有1,15,26,30等時,1和26就都映射在散列地址爲1的位置上,這種情況就是哈西衝突(底下有講^-^)。

所以,這裏選取表的大小p就顯得尤爲重要。一般來講,p都儘量取素數值,這樣能降低哈希衝突的概率,注意,哈希衝突是不能被避免的,任意的散列表都有可能存在哈希衝突!!!

3.數字分析法:

假如我們現在需要對某一公司員工進行登記,用他們的電話當作關鍵字來存儲,由於電話號碼中只有後四位纔是真正的用戶,那麼很可能前7位數字都是一樣的。

130 **** 1212

130 **** 2291

137 **** 0912

137 **** 9238

137 **** 1832

所以,我們就用後四位當作散列地址進行存儲。當然,這樣也會出現衝突,我們就可以對抽取的數字進行左環移位、右環移位、反轉後者將前兩位與後兩位疊加的方法(如:1832----18+32=50)

如果事先知道關鍵字分佈且關鍵字的若干位分佈均勻時,就可以選用此方法。

4.摺疊法:

首先,我們將關鍵字均勻分割成位數相等的幾組(最後一組假如位數不夠也可以短一些),然後將這幾組數進行相加,得到和以後,根據表的大小取後幾位作爲散列地址。

例如:現已知表長爲3,有關鍵字:9876543210,我們將其分割稱987,654,321,0四組,987+654+321+0=1962,我們就取962爲散列地址。

這種方法事先不需要知道關鍵字的分佈情況,且關鍵字位數較大時適合用摺疊法。

5.平方取中法:

如現在有關鍵字1234,我們將1234取平方後得到1522756,我們就取中間的三位227作爲其散列地址。如果關鍵字時4321, 4321^2=18671041,我們就可以取671或者710作爲其散列地址。

用這種方法時事先不需要知道關鍵字的分佈情況,且當關鍵字位數又不是很大!

6.隨機數法:

隨機數法需要用到隨機數函數random,即 f(key) = random(key)

當關鍵字的長度不等時可以採用這種方法構造散列表。。

7、bkdr方法:

int BKDR(char s[20]){
    int seed=131;
    int Hash=0,i=0,Len=strlen(s);
    while(i<Len)Hash=Hash*seed+str[i++];
    return (Hash%Max);//保證Hash值在[0,Max)之間
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章