哈希函數的構造方法



哈希函數的構造方法

本文闡述了哈希函數的構造方法有很多,但應注意兩個原則:第一,函數值應在1至記錄總數之間;第二,盡可能避免沖突。

設要存放的數據元素有n個,存放數據元素的內存單元有m個,設計哈希函數的目標就是要使通過哈希函數得到的n個數據元素的哈希地址盡可能均勻地分布在m個連續內存單元上,同時使計算過程盡可能簡單以達到盡可能高的時間效率。

 

 

                

引 言

構造哈希函數的方法很多。如何構造一個“好”的哈希函數是很強的技術性和實踐性問題,這里的“好”指的是哈希函數構造比較簡單,並且用此哈希函數產生的映射所發生的沖突可能性最小,換句話說一個好的哈希函數能將給定數據集合均勻地映射到給定的地址區間中。

Hash的原意是“弄亂,切碎”,這里的含義是“雜湊”。基本做法是,根據集合元素值的分布情況,設計一個哈希函數h(ki),存儲之素ki時,計算ki的哈希函數值,元素ki存儲在a(h)中。

如果“幸運”,所設計的哈希函數很均勻,即任何ki≠kj,都有h(ki)≠h(kj),那么在查找ki時(再計算ki的哈希函數函數值h),就能在a[h]中找到元素ki。

1.直接定址法

  直接定址法是以數據元素關鍵字k本身或它的線性函數作為它的哈希地址,即:H(k)=k  或 H(k)=a×k+b ; (其中a,b為常數)

  例1,有一個人口統計表,記錄了從1歲到100歲的人口數目,其中年齡作為關鍵字,哈希函數取關鍵字本身,如圖(1):

地址

A1

A2

……

A99

A100

年齡

1

2

……

99

100

人數

980

800

……

495

107

可以看到,當需要查找某一年齡的人數時,直接查找相應的項即可。如查找99歲的老人數,則直接讀出第99項即可。這種哈希函數簡單,並且對於不同的關鍵字不會產生沖突,但可以看出這是一種較為特殊的哈希函數,實際生活中,關鍵字的元素很少是連續的。用該方法產生的哈希表會造成空間大量的浪費,因此這種方法適應性並不強。[2]↑

2.數字分析法

 2.1數字分析法是取數據元素關鍵字中某些取值較均勻的數字位作為哈希地址的方法。即當關鍵字的位數很多時,可以通過對關鍵字的各位進行分析,丟掉分布不均勻的位,作為哈希值。它只適合於所有關鍵字值已知的情況。通過分析分布情況把關鍵字取值區間轉化為一個較小的關鍵字取值區間。

   例2,要構造一個數據元素個數n=80,哈希長度m=100的哈希表。不失一般性,我們這里只給出其中8個關鍵字進行分析,8個關鍵字如下所示:

K1=61317602      K2=61326875      K3=62739628      K4=61343634

K5=62706815      K6=62774638      K7=61381262      K8=61394220

分析上述8個關鍵字可知,關鍵字從左到右的第1、2、3、6位取值比較集中,不宜作為哈希地址,剩余的第4、5、7、8位取值較均勻,可選取其中的兩位作為哈希地址。設選取最后兩位作為哈希地址,則這8個關鍵字的哈希地址分別為:2,75,28,34,15,38,62,20。[1]↑

2. 2設有n個d 位數,每一位可能有r種不同的符號,這r種不同的符號在各位上出現的頻率不一定相同,可能在某位上分布均勻些,每種符號出現的機會均等;在某位上分布不均勻,只有某幾種符號經常出現。可根據哈希表的大小,選取其中各種符號分布均勻的若干位作為哈希地址。計算各位數字中符號分布均勻度rk的公式為:rk=其中,aki表示第i個符號k位上出現的的期望值。計算出rk值越小,

i=1

表明在該位(第k位)各種符號分布越不均勻。 

例3,有一組關鍵字,對其各位編碼如下:

9   2   1   4   8

9   1   2   6   9

9   0   5   2   7

9   1   6   3   0

9   1   8   0   5

9   1   5   5   8

9   2   0   4   7

9   0   0   0   1   

①  ②  ③  ④  ⑤

①位僅“9”出現8次r1=(8-8/10)2*1+(0-8/10)2*9=57.60

②位“0,2”各出現2次,“1”出現4次r2=(2-8/10)2*2+(4-8/10)2*1+(0-8/10)2*7=17.60

③位“0,5”各出現2次,“1,2,6,8”各出現1次r3=(2-8/10)2*2+(1-8/10)2*4+(0-8/10)2*4=5.60

④位“0,4”各出現2次,“2,3,5,6”各出現1次

⑤位“7,8”各出現2次,“0,1,5,9”各出現1次

r3 =r4 =r5 =5.60

若哈希表地址范圍有3位數字,取各關鍵字的③④⑤位作為記錄的哈希地址。也可以把第①②和第⑤位想加,舍去進位,變成一位數,再與第③④位合起來哈希地址等。顯然數字分析法僅適用於事先知道表中所有關鍵字每一位數值的分布情況,它完全依賴於關鍵字集合。如果換一個關鍵字集合,選擇哪幾位重新決定。

3.折疊法

  所謂折疊法是將關鍵字分割成位數相同的幾部分(最后一部分的位數可以不同),然后取這幾部分的疊加和(舍去進位),這方法稱為折疊法。這種方法適用於關鍵字位數較多,而且關鍵字中每一位上數字分布大致均勻的情況。

  折疊法中數位折疊又分為移位疊加和邊界疊加兩種方法,移位疊加是將分割后是每一部分的最低位對齊,然后相加;邊界疊加是從一端向另一端沿分割界來回折疊,然后對齊相加。

例4,當哈希表長為1000時,關鍵字key=110108331119891,允許的地址空間為三位十進制數,則這兩種疊加情況如圖(2):

       移位疊加                                 邊界疊加

       8 9 1                                     8 9 1

       1 1 9                                     9 1 1

       3 3 1                                     3 3 1

       1 0 8                                     8 0 1

    +  1 1 0                                   + 1 1 0              

   (1) 5 5 9                                  (3)0 4 4

                 圖(2)由折疊法求哈希地址

     用移位疊加得到的哈希地址是559,而用邊界疊加所得到的哈希地址是44。如果關鍵字不是數值而是字符串,則可先轉化為數。轉化的辦法可以用ASCⅡ字符或字符的次序值。[3]↑

 

4.平方取中法

  這是一種常用的哈希函數構造方法。這個方法是先取關鍵字的平方,然后根據可使用空間的大小,選取平方數是中間幾位為哈希地址。

哈希函數 H(key)=“key2的中間幾位”因為這種方法的原理是通過取平方擴大差別,平方值的中間幾位和這個數的每一位都相關,則對不同的關鍵字得到的哈希函數值不易產生沖突,由此產生的哈希地址也較為均勻。

例5,若設哈希表長為1000則可取關鍵字平方值的中間三位,如圖(3)所示:

關鍵字

關鍵字的平方

哈希函數值

1234

1522756

227

2143

4592449

924

4132

17073424

734

3214

10329796

297

圖(3)平方取中哈希函數示例    [4] ↑

有人曾用“輪盤賭”的統計分析方法對它們進行了模擬分析,結論是平方取中法最接近“隨機化”。

 

  例6,設有一組關鍵字值為ABC,BCD,CDE,DEF其相應的機內碼分別為010203,020304,030405,040506。假設可利用地址空間大小為103,平方后取平方數的中間三位作為相當記錄的存儲地址。如圖(4)所示:         

關鍵字

機內碼

機內碼的平方

哈希地址

ABC

010203

0104101209

101

BCD

020304

0412252416

252

CDE

030405

0924464025

464

DEF

040506

1640736036

736

圖(4)平方取中法關鍵字及其存儲地址[6]↑

   下面給出平方取中法的哈希函數

     //平方取中法哈希函數,結設關鍵字值32位的整數

     //哈希函數將返回key * key的中間10位

       Int  Hash (int key)

         {

     //計算key的平方

      Key * = key ;

     //去掉低11位

     Key>>=11;

     // 返回低10位(即key * key的中間10位)

       Return key %1024;

          }

 

5.減去法

   減去法是數據的鍵值減去一個特定的數值以求得數據存儲的位置。

例7,公司有一百個員工,而員工的編號介於1001到1100,減去法就是員工編號減去1000后即為數據的位置。編號1001員工的數據在數據中的第一筆。編號1002員工的數據在數據中的第二筆…依次類推。從而獲得有關員工的所有信息,因為編號1000以前並沒有數據,所有員工編號都從1001開始編號。

 

6.基數轉換法

  將十進制數X看作其他進制,比如十三進制,再按照十三進制數轉換成十進制數,提取其中若干為作為X的哈希值。一般取大於原來基數的數作為轉換的基數,並且兩個基數應該是互素的。

 

例8,Hash(80127429)=(80127429)13=8*137+0*136+1*135+2*134+7*133+4*132+2*131+9=(502432641)10如果取中間三位作為哈希值,得Hash(80127429)=432

 為了獲得良好的哈希函數,可以將幾種方法聯合起來使用,比如先變基,再折疊或平方取中等等,只要散列均勻,就可以隨意拼湊。[5] ↑

 

7.除留余數法

取關鍵字被某個不大於哈希表表長m的數p除后所得余數為哈希地址,即設定哈希函數為  Hash(key)=key mod p (p≤m),其中,除數p稱作模。

除留余數法不僅可以對關鍵字直接取模,也可以在折疊、平方取中等運算后取模。對於除留余數法求哈希地址,關鍵在於模p的選擇。使得數據元素集合中每一個關鍵字通過該哈希函數映射到內存單元的任意地址上的概率相等,從而盡可能減少發生哈希沖突的可能性。

理論研究表明,除留余數法的模p取不大於表長且最接近表長m素數時效果最好,且p最好取1.1n~1.7n之間的一個素數(n為存在的數據元素個數)。例如:當n=7時,p最好取11、13等素數。 又例圖(5):

表長m

8

16

32

64

128

256

512

1000

模p

7

13

31

61

127

251

503

997

由於除留余數法的地址計算方法簡單,而且在許多情況下效果較好。[2]↑

例9,公司有236個員工,而員工編號介於1000到9999,除留余數法就是員工編號除以數據個數236后,去余數即為數據的位置。編號5428員工的數據(編號5428除以236取余數得0)放數據中的第一筆,編號3512員工數據(編號3512除以236取余數得8)放數據中的第九筆…依次類推。

 

8.隨機乘數法

  亦稱為“乘余取整法”。隨機乘數法使用一個隨機實數f,0≤f<1,乘積f*k的分數部分在0~1之間,用這個分數部分的值與n(哈希表的長度)相乘,乘積的整數部分就是對應的哈希值,顯然這個哈希值落在0~n-1之間。其表達公式為:Hash(k)=「n*(f*k%1)」其中“f*k%1”表示f*k 的小數部分,即f*k%1=f*k-「f*k」[5] ↑

  例10,對下列關鍵字值集合采用隨機乘數法計算哈希值,隨機數f=0.103149002 哈希表長度n=100得圖(6):

 

k

f*k

n*((f*k)的小數部分)

Hash(k)

319426

32948.47311

47.78411

47

718309

74092.85648

86.50448

86

629443

64926.41727

42.14427

42

919697

84865.82769

83.59669

83

  此方法的優點是對n的選擇不很關鍵。通常若地址空間為p位就是選n=2p.Knuth對常數f的取法做了仔細的研究,他認為f取任何值都可以,但某些值效果更好。如f=(-1)/2=0.6180329...比較理想。[8] ↑

9.字符串數值哈希法

在很都情況下關鍵字是字符串,因此這樣對字符串設計Hash函數是一個需要討論的問題。下列函數是取字符串前10個字符來設計的哈希函數

Int Hash _ char (char *X)

{

  int I ,sum 

  i=0;

  while (i 10 && X[i]) 

  Sum +=X[i++];

  sum%=N;      //N是記錄的條數

  }

這種函數把字符串的前10個字符的ASCⅡ值之和對N取摸作為Hash地址,只要N較小,Hash地址將較均勻分布[0,N]區間內,因此這個函數還是可用的。對於N很大的情形,可使用下列函數

int ELFhash (char *key )

{

 Unsigned long h=0,g;

whie (*key)

h=(h<<4)+ *key;

key++;

g=h & 0 xF0000000L;

if (g) h^=g>>24;

h & =~g;

}

h=h % N

return (h);

}

  這個函數稱為ELFHash(Exextable and Linking Format ,ELF,可執行鏈接格式)函數。它把一個字符串的絕對長度作為輸入,並通過一種方式把字符的十進制值結合起來,對長字符串和短字符串都有效,這種方式產生的位置不可能不均勻分布。[7] ↑

10.旋轉法

  旋轉法是將數據的鍵值中進行旋轉。旋轉法通常並不直接使用在哈希函數上,而是搭配其他哈希函數使用。

  例11,某學校同一個系的新生(小於100人)的學號前5位數是相同的,只有最后2位數不同,我們將最后一位數,旋轉放置到第一位,其余的往右移。

新生學號

旋轉過程

旋轉后的新鍵值

5062101

5062101

1506210

5062102

5062102

2506210

5062103

5062103

3506210

5062104

5062104

4506210

5062105

5062105

5506210

                      如圖(7)

 運用這種方法可以只輸入一個數值從而快速地查到有關學生的信息。[9] ↑

11.偽隨機數法

偽隨機數法是將利用數據的鍵值經過隨機數法的運算后的結果作為數據存儲的位置。其公式如下(a和c為質數):

Y=(a * Key + c)mod 數組的大小

例12,某公司的某女員工的編號是321547,現該公司共有107個女職工,我們取a=13,c=5則

Y=(13*321547+5)%107

 =(4180111+5)%107

 =54

則取54當作該員工數據存儲的位置。[10] ↑

小 結

有許多種不同的哈希函數設計方法,這里主要討論幾種常用的不同類型關鍵字的希函數設計方法:直接定址法、數字分析法、折疊法、平方取中法、減去法、基數轉換法、除留余數法、隨機乘數法、字符串數值哈希法、偽隨機數法、旋轉法。

盡管哈希函數的構造方法有很多,但不同的方法適用於不同的情況。如:當鍵字是字符串時可以用字符串數值哈希法構造哈希函數;當關鍵字是整數類型時就可以用除留余數法、直接定址法和數字分析法等設計哈希函數;而關鍵字是小數類型常用偽隨機數法來構造哈希函數等。

 

參 考 文 獻

[1]朱戰立編著.數據結構(C++語言描述) 北京:高等教育出版社,2004

[2]陳明編著.實用數據結構基礎  北京:清華大學出版社,2002

[3]嚴蔚敏等編著.數據結構及應用算法教程  北京:清華大學出版社,2000

[4]殷人昆等編著.數據結構:面向對象方法與C++描述  北京:清華大學出版社,1999

[5]熊岳山等編著.數據結構:C++語言描述,  長沙:國防科技大學出版社,2002.2

[6]蘇光奎等編著. 數據結構導學。  北京:清華大學出版社,2002

[7]陳松喬等編著.算法與數據結構(C與C++描述)  北京:北方交通大學出版社  2002.8

[8]卓滋德克著.陳曙暉譯 數據結構與算法——C++  北京:清華大學出版社, 2003

[9]王慶瑞編著.數據結構教程(C++語言描述) 北京:高等教育出版社,2002.8

[10]黃國瑜等編著.數據結構(Java語言版) 北京:清華大學出版社,2002 

 

 

轉自http://wenku.baidu.com/view/61b121c06137ee06eff918c1.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM