淺談散列

淺談散列
      

通俗的說,程序是能夠完成既定目標的具有特定邏輯組織形式的指令集序列。既然有現實的需求,那麼我們知道外界環境必然會給予程序某些特定形式的“輸入”,然而在機器的內部,這種“輸入”將轉換爲數據的形式,繼而這就要求我們爲用以描述現實世界需求的數據建立一個結構化的模型,使其能夠被機器指令高效的處理。通常,對於數據的處理無外乎以下幾種:讀取/更新/刪除數據項,或者插入新項,其中除插入外其他幾種操作均要求對集合進行搜索。而結構化的模型可以通過數組、鏈表或者樹形結構等建立,不同的建模方式對於數據處理中的各種操作有不同的性能表現。由於本文是偏向介紹算法,所以對於數組、鏈表等數據結構只做簡單介紹,並將由這兩種結構引出本文的主題——散列,而對樹結構的介紹及對其進行操作的算法將會寫在後續文章中。一般來講,數據結構將直接影響對其處理的算法的選擇,在本文中的散列函數算法又會反過來影響散列表這種結構之於數據的存貯效率,可以說,數據結構與算法的關係就好比是一卵雙生。

我們先簡單看一下數組與鏈表這兩種數據結構存儲數據的方式:


圖1

由上圖,我們可以得出以下幾點:

  • 通常總是會爲動態集合的字典操作預留一定的資源,並且通過額外的空間記錄現有元素集合的大小或是鏈表的表頭/尾,因此對於插入操作來說其運行時間爲O(1)。
  • 因爲讀取/更新操作均建立在搜索的基礎上,因此運行效率取決於定位待操作的數據項的時間。我們首先來看數組,如果所存儲的元素集是無序的,那麼搜索集合中的任一元素的期望時間E=(1+2+…+n)/n=O(n),因此搜索某個數據項幾乎總是需要遍歷整個集合。如果需要大量的搜索操作,那麼對於現有集合可以通過排序執行適當的預處理,並在此之後調用運行時間爲O(㏒n)的二分搜索以獲得較好的整體性能。但這幾乎總是受到外界環境不穩定因素的限制,例如動態插入操作將導致現有結構的序性質遭到破壞,並且這種預處理將會消耗O(n)或是O(n㏒n)的運行時間。再來看鏈表結構,因爲其無法提供隨機訪問能力,因此這決定了即使對鏈表進行排序也無法運用二分搜索快速查找具有某個固定值的元素,並且同未排序的普通數組一樣,鏈表的搜索操作的期望時間也爲O(n)。
  • 對於數據的刪除操作,我們發現在有序數組中還需要將已刪除數據項所佔位置之後的元素逐個前移,使得在維持序關係時整個數組仍然保持緊湊,而在鏈表中,雖然只需改變待刪除數據項的前驅的指向,但高效利用空間的前提條件將會要求我們能夠維護一個空閒鏈表(freelist),這些都需要額外的時間開銷。
  • 最後我們知道這兩種結構對存儲空間的利用率都很高,誰說這不是一個優點呢,至少對於散列來說是的。

需要注意的兩點是,在實際情況中還可以運用循環數組/鏈表,雙向鏈表等變形,數據結構的合理選擇總是需要考慮特定的應用需求和外部環境。


什麼是散列
在解釋散列的含義之前,我們先來深入的觀察一下普通數組這種數據結構,由圖1所示,將集合{a,b,c,d,e,f}存儲在數組中時佔據了索引爲0~5的6個存儲空間,我們可以形式化的表示爲有序對(An ordered pair)的集合,即{(a,0),(b,1),(c,2),(d,3),(e,4),(f,5)}。但其實這個集合中任意一個有序對的元素之間的關係是未定義的(undefined),因此這樣的有序對集合可以有6!個。然而我們是否可以構造一個關係,使得待存儲的集合{a,b,c,d,e,f}中的任意一個元素通過這個關係都能找到一個指定的索引值,由下圖更形象的表示:


圖2

根據上圖我們知道原有集合中的元素被散列之後所得到的有序對的集合爲{(d,0),(b,2),(c,2),(a,3),(f,4),(e,5)}。其中,散列表中的1號槽爲空,表示經過散列函數作用之後,原集合中沒有任何元素被散列至該位置,而2號槽中則存在兩個元素,將兩個不同元素散列至相同位置的情形我們稱爲碰撞(Collision)。如上圖所示,我們通過使用鏈表將不同元素鏈接起來解決碰撞,在後文中還將介紹另外一種稱爲開放尋址(Open addressing)的解決方法。這裏更具體的說明一下鏈接法的鏈接形式:因爲散列至同一個槽的元素並無順序上的先後要求,因此爲效率計,我們將總是採用在鏈表頭插入碰撞元素的做法,最終導致的結果是,在一個鏈表中,越靠近表頭的節點,在原數組中被散列的次序越靠後。綜上所述,可以對散列函數hash進行如下形式化定義:
    設在大小爲N的集合中存在元素elem,存儲原集合中元素的散列表共有m個槽位,則有
    hash: elem→γ∈{0,1,2,…,m-1}


散列分析
這裏主要是對鏈接散列進行分析,其中的問題分析與解決思路同樣適用於開放尋址。因爲通過設計某個散列函數hash我們定義了集合元素與散列表下標之間一一對應的關係,從而使得搜索操作只需在常量時間內即可完成,即假定存在某個待搜索的關鍵字key,那麼該關鍵字必定在槽hash(key)中。唯一需要關心的問題是,集合中的元素在被散列之後的分佈狀況如何?在最壞情況下,有可能選取的散列函數將所有元素均散列至同一個槽中,使得該散列表實際已退化爲鏈表,在這種情況下的搜索操作將花費O(n)的運行開銷。顯然,我們並非因爲散列具有這種最壞情況纔去使用它,而能在多數情況下避免這種“退化”,將要求我們更深入的思考以下兩個問題:

  • 如何設計一個好的散列函數?
  • 散列表的大小爲多少較好,是否正如圖2所示,使用與原集合大小相同的散列表?我們將通過量化證明。

首先針對第二個問題進行分析,即散列表的大小爲多少較好?

    設所有元素之間均相互獨立,且每個元素被散列至每個槽的概率均相等  ------------> 假設①
    並設原集合的尺寸爲n,散列表的大小爲m,裝載因子α=n/m  -------------->假設②
    最重要的是,我們並不考慮散列函數所消耗的計算開銷  ---------------->假設③
    由假設①,元素被散列至每個槽的概率相等,再根據假設②中散列表的大小爲m,有P{elem→γ∈{0,1,2,…,m-1}}=1/m
    設待查找的關鍵字爲k,因此存在兩種情況:Ⅰ、待查找關鍵字k不存在; Ⅱ、關鍵字k存在
    然而其實情況Ⅰ的搜索開銷即爲情況Ⅱ的最壞情況運行時間,因爲關鍵字k不存在時將查找整個鏈表
    以下是更詳細的分析:
    ------>Ⅰ、由假設①,並且根據假設②有裝載因子α=n/m,所以α表示每個槽的結點數。因爲當待查找的關鍵字k不存在時,我們必將搜索到該槽中的最後一個結點處,因此在這種情況下的搜索開銷爲O(α)。
    ------>Ⅱ、對於關鍵字k存在的情況,其實不做詳細分析我們就已經知道,這種情況下的運行時間必然不超過O(α),然而單單分析本身就是一件很有趣的事情,爲何不做得徹底一些?更重要的是,下面所用到的這種方法,其體現的思想對於分析隨機過程將具有極大的益處。

    綜上可知,如果我們將散列表設定爲與原集合大小相同時,裝載因子α=1,此時的搜索時間爲O(1),但其實設置爲原集合尺寸的1/2或是2倍並沒有多大區別。需要注意的是雖然散列表越大導致搜索時間減少,但其所佔內存空間將會增大。

設計散列函數
如何設計一個好的散列函數?需要一定的數論功底才能回答,因此這一部分不會去詳細解釋爲什麼這樣做。儘管脫離了能夠對其解釋的理論,工程實踐將會變得“矮”很多截,但這裏所用的一些東西在實際生活中具有廣泛應用,爲了應用而去了解並積累他們仍是有趣的。此外,無論是字符或是數據在機器內部總是以數值的形式表現,所以總能設置一種機制將任何輸入轉換爲數值,例如對字符'a'進行散列,若將'a'解釋爲與其對應的ASCII碼97,則hash('a')=hash(97),只是當輸入變得複雜時這種設計也將變得更加困難,這裏我們將避開這些複雜性而直接假設所有的輸入均爲某一個數。

  • 除餘散列法
    設 hash(key)=key mod m,其中key表示被散列的關鍵字,而m則表示散列表的大小,mod則爲取餘操作。
    這是一種比較簡單的散列函數,但簡單並不意味着高效。當待散列的元素之間存在某種模式時,這種散列法會有相當糟糕的性能表現。對該函數一個有用的指導原則是將m選取爲接近待散列集合大小的質數

  • 乘法散列法
    設 hash(key)=floor(m×(A×key mod 1)),其中floor()表示對表達式進行下取整,A∈(0,1),m如上同樣表示散列表的大小,且在這種方法中對m並無任何特殊的要求。
    [A×key mod 1]表示將key乘上某個在0~1之間的數並取乘積的小數部分,該表達式等價於A×key-floor(A×key)
    這裏最重要的是A的值應該如何設定,Don•Knuth老大認爲A=(√5-1)/2 [黃金分割點] 比較好。

  • 全域散列法(universal hashing)
    設 hasha,b(key)=(a×key+b) mod m,如同除餘散列法中一樣,m的值應爲質數,而a∈{1,2,3,…,m-1},b∈{0,1,2,…,m-1}且a,b的值應在運行時動態確定。
    全域散列的基本思想是給出hash函數的基本“骨架”,而其中的某些參數通過運行時在指定範圍內隨機選取確定,從而實際上形成了一個函數簇。根據上述a,b的取值範圍,我們知道這個函數簇中存在m×(m-1)個函數。因爲對於同一個輸入,每次執行時選取不同的參數將擁有不同的性能表現,因此設計的函數實際上獨立於任意被散列的關鍵字。只有當一個相對糟糕的輸入——即該輸入不構成隨機分佈,遇到一個選取的相對糟糕的散列函數時,纔將導致較差的性能,在多數情況下這類散列往往具有較好的運行時性能表現。

  • 完全散列法(perfect hashing)
    完全散列法與其說是一種函數設計方法,倒不如說是對碰撞(Collision)情況的另一種解決策略。具體來講,存在待查找的元素key,經過一次散列,該元素被存放在槽hash(key)處,然而其他元素也有可能被散列至該處,因此對散列至該槽的元素繼續進行散列,所得到的值hash'(hash(key))即爲元素key的確切存儲位置。其中外層hash過程稱爲一級散列,內層hash'過程稱爲二級散列。
    這個策略會存在幾個問題,首先二級散列之後可能還會存在碰撞問題,是否需要進行三級散列甚至更多級的散列?其次,某個槽對應的二級散列函數應如何選取?最後,一級散列中某個槽所對應的二級散列表的尺寸應該如何設置?
    這三個問題其實可以一併解決。首先對於是否需要更多級的散列,因爲外層的一級散列已經將原集合分爲一系列子集,若我們將一級散列表設置爲與原集合尺寸相近的大小,同時合理的選取一個散列函數,那麼在每個槽中發生碰撞的元素個數將在一個可控範圍內,此時如果爲了少部分元素再設置三級散列的話,不僅增加了整個結構的複雜性以及對於內存的要求,而且還要再次爲三級散列選取合理的散列函數,成本相對較大。接着來看解決二級散列函數的選取問題,我們發現在全域散列法中,只需設置不同的參數,即能生成特定於某個槽的散列函數,若生成的散列函數導致二級散列表中發生碰撞,那麼重新從全域散列函數簇中選取,直至不發生碰撞爲止即可。另外,只需合理設置二級散列表的大小,這種重新選取的概率將變得極低,一個可行的指導原則是將二級散列表的大小設置爲外層散列至該槽的元素個數的平方

    圖3
    完全散列的分析更形象化的說明如圖3所示。最後要說明的一點是,這種通過兩次散列的方法雖然提供了高效的搜索,但代價是花費了更多的內存空間,同時因爲插入操作有可能導致在二級散列表中發生碰撞,因此這種方法只適用於靜態關鍵字集合中,“靜態”意指該集合一旦確定,便不再發生動態變化,即不發生插入或是刪除操作

開放尋址(open addressing)
同鏈接法一樣,開放尋址是一種用於解決碰撞的策略。不同之處在於,在鏈接法中,每個關鍵字只能對應一個固定的槽,而在開放尋址中,每個關鍵字可以對應散列表中的多個槽,因爲有可能在首次尋址過程中,該槽已被先前的關鍵字所佔據,因而接下來我們根據事先所制定的某個規則繼續試探下一個槽是否可用,直至找到一個可用的槽並將關鍵字存儲在其中爲止。這裏存在的一個問題是,如果僅將待散列的關鍵字作爲函數的輸入,那麼最終必定只有與其對應的單個輸出,因此我們必須尋找一個誘導因子,使得在關鍵字不變的情況下誘使函數的輸出發生變化。因爲所有的關鍵字放在一個散列表中,因而將該誘導因子的值域設置成與散列表的大小相同,能夠更充分的利用整個散列表:
    設原集合中存在元素elem,誘導因子爲i,散列表的大小爲m,則有
    hash: (elem, i)→γ,其中 i,γ∈{0,1,2,…,m-1}
    並且該函數所形成的探查序列爲 <hash(elem,0), hash(elem,1), …, hash(elem,m-1)>

這裏誘導因子i被形式化地定義爲區間[0,m)中的某個值,其取值序列被固定爲<0,1,…,m-1>只是爲了方便散列函數的實現,實際可根據原集合中的元素所存在的模式制定更具針對性的值域及取值序列,這裏我們將重點放在開放尋址中散列函數的設計上。通常在開放尋址中所用的散列函數主要由以下幾種:

  • 線性試探法
    設 hash(key, i)=(hash'(key)+i) mod m,其中 i=0,1,…,m-1,hash'爲輔助散列函數
    在線性試探中,首次探查位置取決於hash'(key)的值,若發生碰撞,則接下來所探查的位置爲(hash'(key)+1) mod m,從而所形成的探查序列爲<hash'(key), hash'(key)+1,…, m-1, 0,…, hash'(key)-1>。因爲散列表的大小爲m,所以整個散列表總共提供m種不同的探查序列。並且由於線性試探使用連續探查的方式,因此隨着散列表中元素的增加,所佔用的槽逐漸呈連續分佈狀況,從而增加插入/搜索操作的期望時間。

  • 二次試探法
    設 hash(key,i)=(hash'(key)+c1i+c2i2) mod m,如上所述,i=0,1,…,m-1,hash'爲輔助散列函數,c1,c2爲輔助參數
    可以發現,線性試探法實際是二次試探的特殊情況,若取參數c1=1,c2=0,那麼二次試探將“退化”爲線性試探。同樣的,整個探查序列也是由hash'(key)所決定的,因爲雖然探查序列中各元素之間的增量[c1i+c2i2]不再以線性的方式進行,但對於每個元素來說,引導因子i總是以步長1的方式遞增,這就導致所有元素的探查序列的增加方式是相同的,因此整個散列表同樣提供m種不同的探查序列。但隨着散列表中元素的增加,這種跳躍式的增量方式使得插入/搜索操作的運行時間受到的影響較小。

  • 雙重試探法
    設 hash(key,i)=(hash'(key)+i×hash''(key)) mod m,其中i=0,1,…,m-1,hash',hash''均爲輔助散列函數
    雙重試探法的首個探查位置爲hash'(key),當產生碰撞之後,接下來的探查位置爲(hash'(key)+hash''(key)) mod m,因此我們發現在雙重試探法中,不僅初始探查位置依賴於關鍵字key,探查序列中的增量hash''(key)同樣依賴於關鍵字key,因而整個散列表提供了m2種不同的探查序列,較之於前兩種開放尋址具備了更多的靈活性。這裏還要注意的是應保證hash''(key)與m互質,因爲根據固定的偏移量所尋址的所有槽將形成一個羣,若最大公約數p=gcd(m, hash''(key))>1,那麼所能尋址的槽的個數爲m/p<m,使得對於一個關鍵字來說無法充分利用整個散列表。舉例來說,若初始探查位置爲1,偏移量爲3,整個散列表大小爲12,那麼所能尋址的槽爲<1, 4, 7, 10>,尋址個數爲12/gcd(12,3)=4。

最後要注意的一點是,因爲開放尋址將所有的關鍵字均存放在散列表中,因此我們必須保證散列表的大小始終大於原集合的元素規模,使得在任何時刻都能將元素存放在散列表中。但動態集合的插入操作有可能使得散列表被填滿,這種情況下如果還需進行插入操作,那麼我們就必須擴大散列表,由於散列表的大小m發生變化,考慮到m是散列函數中的參數,因此能保證m發生變化之後,同樣一個關鍵字能被散列至相同的槽中,所以存放在原先散列表中的關鍵字必須被重新散列,這就帶來了額外的開銷。因此我們首先應充分挖掘待存儲的數據的特點,比如作用在該集合上的操作是否在大多數情況下爲搜索,而動態的插入/刪除操作較少發生,或者是預留一個較大的散列表,能夠保證原集合必定不超過該散列表的大小,在這之後才考慮使用開放尋址。

以下是鏈接法[除餘散列,乘法散列,全域散列]和開放尋址[線性試探,二次試探,雙重試探]的性能測試,由於所使用的數據集爲隨機生成數,因而可以預知對於鏈接法中的三種散列方法來說,散列之後的元素分佈狀況並不會存在多大差別,但開放尋址中的三種散列方法仍具有測試價值,這裏我們一併進行測試:

  1. #include <iostream> 
  2. #include <cmath> 
  3. #include <ctime> 
  4. #include <string> 
  5. #include <exception> 
  6. #include <Windows.h> 
  7. #include <engine.h> 
  8. using namespace std; 
  9.  
  10. //#define __TEST__ 
  11. #pragma warning(disable:4244) 
  12. #pragma warning(disable:4996) 
  13. #pragma warning(disable:4290) 
  14. const size_t KEY_SCALE = 600;        //the number of keys in source set 
  15. const size_t SLOTS_IN_CHAIN = 45;        //the number of slots in chained hash table 
  16. const size_t SLOTS_IN_OPENADDR = KEY_SCALE;    /*in open addressing hash table*/ 
  17. const size_t COUNT_OF_EXECUTE = 60;        //counts of hash by open addressing 
  18.  
  19. class MatDraw{ 
  20. public
  21.     MatDraw(const mwSize&); 
  22.     ~MatDraw() 
  23.     { 
  24.         mxDestroyArray(paForXOrder); 
  25.         mxDestroyArray(paForYOrder); 
  26.         engClose(ep); 
  27.     } 
  28.     void mdPlot(double **, const size_t&); 
  29.     void EvalStr(string str) 
  30.     { engEvalString(ep, str.c_str()); } 
  31. private
  32.     MatDraw(const MatDraw&); 
  33.     MatDraw& operator=(const MatDraw&); 
  34. private
  35.     Engine *ep; 
  36.     mxArray *paForXOrder; 
  37.     mxArray *paForYOrder; 
  38.     size_t ArrLength; 
  39. }; 
  40.  
  41. class Hash{ 
  42. public
  43.     virtual void plot() = 0; 
  44.     virtual void RunAndGetCollision() = 0; 
  45.     Hash(const size_t&, const size_t&); 
  46.     virtual ~Hash() 
  47.     { if(SrcSet) delete [] SrcSet; } 
  48. protected
  49.     typedef size_t index; 
  50.     void GeneRand(const size_t&); 
  51.     size_t GeneFrom(size_t left, size_t right) const    //generate random [left,right] 
  52.     { return (rand()%(right-left+1))+left; } 
  53.     size_t getClosePrime() const
  54.     void Exit() const { system("pause"); exit(0); } 
  55.     size_t *SrcSet;        //source set to be hashed 
  56.     size_t szSet;            //size of source set 
  57.     size_t approSlot;        //let user choose approximate Slot in hash table 
  58.     size_t nSlot;        //the size of hash table 
  59. protected
  60.     /*this universal function provide service for chain
  61.      *hash & open address at the same time*/ 
  62.     index unihash(const size_t &key, const size_t &arg_a, const size_t &arg_b) 
  63.     { 
  64.         //hash(key)=(a*key+b) mod m -->a & b are random 
  65.         return (arg_a*key+arg_b) % nSlot; 
  66.     } 
  67. }; 
  68.  
  69. class chainHash : public Hash{ 
  70. private
  71.     typedef index (chainHash::*ChHsh)(const size_t&); 
  72.     struct ChainNode{ 
  73.         size_t key; 
  74.         ChainNode *next; 
  75.     }; 
  76.     struct HashTable{ 
  77.         ChainNode *head; 
  78.         size_t szCnt;        //the number of node in chain 
  79.     }; 
  80. public
  81.     typedef enum { REMAIN, MULTI, UNIVER } HashFunc; 
  82.     chainHash(const size_t&, const size_t&); 
  83.     ~chainHash(); 
  84.     void getCorresIndex(const HashFunc&); 
  85.     void IterEveryChain(const HashFunc &) const
  86.     void InsertIntoHashTable(const HashFunc&); 
  87.     void RunAndGetCollision(); 
  88.     void plot() 
  89.     { 
  90.         pmd->mdPlot(ColliMatrix,chainHash::NumOfHashF); 
  91.         pmd->EvalStr(string("legend('remainder','multiplicate','univerhash');").c_str()); 
  92.     } 
  93. private
  94.     chainHash(const chainHash&); 
  95.     chainHash& operator=(const chainHash&); 
  96.     /*three methods for chained hash*/ 
  97.     index remainder(const size_t &key) { return key%nSlot; } 
  98.     index multiplicate(const size_t &key) 
  99.     { 
  100.         //hash(key) = floor(m*(A*key mod 1)) 
  101.         float A = (sqrt(5.0)-1)/2;        //golden section ratio 
  102.         float temp = A*key; 
  103.         return (temp-static_cast<size_t>(temp))*nSlot; 
  104.     } 
  105.     index univerhash(const size_t &key) 
  106.     { 
  107.         //hash(key)=(a*key+b) mod m -->a & b are random 
  108.         return unihash(key, a, b); 
  109.     } 
  110. private
  111.     //here store the argument for the 'univerhash' in a instance 
  112.     size_t a, b; 
  113. private
  114.     HashTable *pht;        //dynamic allocate hash table 
  115.     ChainNode *pcn;        //allocate nodes in chain dynamically 
  116.     static ChHsh hfTable[];        //function table 
  117.     static size_t NumOfHashF; 
  118.     double **ColliMatrix;            //store the counts of Collision 
  119.     class MatDraw *pmd;        //picture pen 
  120. }; 
  121.  
  122. class openAddrHash : public Hash { 
  123. private
  124.     typedef index (openAddrHash::*OAHashF)(const size_t&, const size_t&); 
  125.     struct HashTable { 
  126.         size_t szCnt; 
  127.         bool IsNotEmpty; 
  128.     }; 
  129. public
  130.     typedef enum { LINEAR, QUADRATIC, DBHASH} HashFunc; 
  131.     openAddrHash(const size_t&, const size_t&); 
  132.     ~openAddrHash(); 
  133.     void plot()  
  134.     { 
  135.         pmd->EvalStr(string("figure;").c_str()); 
  136.         pmd->mdPlot(ColliMatrix, openAddrHash::NumOfHashF); 
  137.         pmd->EvalStr(string("legend('linear','quadratic','doublehash');").c_str()); 
  138.     } 
  139.     void RunAndGetCollision(); 
  140. private
  141.     /*three methods for Open Addressing -->hash' & hash'' are assist functions*/ 
  142.     /* hash(key, i)=(hash'(key)+i) mod m*/ 
  143.     index linearprob(const size_t &key, const size_t &ix) 
  144.     { return (unihash(key, arg[0], arg[1])+ix)%nSlot; } 
  145.     /* hash(key, i)=(hash'(key)+c1*i+c2*i*i) mod m*/ 
  146.     index quadprob(const size_t &key, const size_t &ix) 
  147.     { return (unihash(key, arg[2], arg[3])+c1*ix+c2*ix*ix)%nSlot; } 
  148.     /* hash(key, i)=(hash'(key)+i*hash''(key)) mod m*/ 
  149.     index dbhashprob(const size_t &key, const size_t &ix) 
  150.     { return (unihash(key, arg[4], arg[5])+ix*unihash(key, arg[6], arg[7]))%nSlot; } 
  151.     void getCorresIndex(const HashFunc&) throw(exception); 
  152.     void printInfo(const HashFunc&); 
  153. private
  154.     //used in assist hash functions 
  155.     /*in array 'arg'  ---->
  156.      *arg[0]~arg[1] is used for linearprob
  157.      *arg[2]~arg[3] is used for quadprob
  158.      *arg[4]~arg[7] is for dbhashprob since it has two assist hash functions*/ 
  159. #define ARG_IN_HASHFUNC 8 
  160.     size_t arg[ARG_IN_HASHFUNC]; 
  161.     void GeneArg(const HashFunc&); 
  162.     /*for quadprob*/ 
  163. #define START 1 
  164. #define END 5 
  165.     /*assist arguments ranged from [START, END] -- here can be changed*/ 
  166.     size_t c1, c2;  /*extra arguments used in quadprob function*/ 
  167. private
  168.     HashTable *pht; 
  169.     double **ColliMatrix;        //total counts of collision 
  170.     class MatDraw *pmd;        //picture pen 
  171.     static OAHashF hfTable[]; 
  172.     static size_t NumOfHashF; 
  173. }; 
  174.  
  175. chainHash::ChHsh chainHash::hfTable[] = {&chainHash::remainder, 
  176.                                            &chainHash::multiplicate, 
  177.                                            &chainHash::univerhash}; 
  178. size_t chainHash::NumOfHashF = sizeof(chainHash::hfTable)/sizeof(chainHash::hfTable[0]); 
  179.  
  180. openAddrHash::OAHashF openAddrHash::hfTable[] = {&openAddrHash::linearprob, 
  181.                                                 &openAddrHash::quadprob, 
  182.                                                 &openAddrHash::dbhashprob}; 
  183. size_t openAddrHash::NumOfHashF = sizeof(openAddrHash::hfTable)/ 
  184.     sizeof(openAddrHash::hfTable[0]); 
  185.  
  186. int main(int argc, char *argv[]) 
  187.     srand(static_cast<unsigned>(time(NULL))); 
  188.     Hash *ph = new chainHash(KEY_SCALE,SLOTS_IN_CHAIN); 
  189.     ph->RunAndGetCollision(); 
  190.     ph->plot(); 
  191.     delete ph; 
  192.     ph = new openAddrHash(KEY_SCALE,SLOTS_IN_OPENADDR); 
  193.     ph->RunAndGetCollision(); 
  194.     ph->plot(); 
  195.     delete ph; 
  196.     system("pause"); 
  197.     return EXIT_SUCCESS; 
  198.  
  199. Hash::Hash(const size_t &szKey, const size_t &slot) : szSet(szKey), 
  200.     approSlot(slot),nSlot(getClosePrime()) 
  201.     if(!(SrcSet = new size_t[szSet])) 
  202.     { 
  203.         cout<<"Fail to Allocate Memory!"<<endl; 
  204.         Exit(); 
  205.     } 
  206.     GeneRand(szSet); 
  207.  
  208. inline void Hash::GeneRand(const size_t &nSize) 
  209.     for(size_t index = 0; index != nSize; index++) 
  210.         SrcSet[index] = static_cast<size_t>(rand()); 
  211.  
  212. inline size_t Hash::getClosePrime() const 
  213.     size_t start = approSlot; 
  214.     for( ; ; ) 
  215.     { 
  216.         size_t index = 2, bound = sqrt(static_cast<double>(start)); 
  217.         for(; index <= bound; index++ ) 
  218.             if(start%index == 0) break
  219.         if(index > bound) break
  220.         else ++start; 
  221.     } 
  222.     return start; 
  223.  
  224. chainHash::chainHash(const size_t &nSize, const size_t &slot) : Hash(nSize, slot) 
  225.     if( !(pht = new HashTable[nSlot]) || 
  226.         !(pcn = new ChainNode[nSize]) ||  
  227.         !(ColliMatrix = new double*[chainHash::NumOfHashF])) 
  228.     { 
  229.         cout<<"Fail to Allocate Memory!"<<endl; 
  230.         Exit(); 
  231.     } 
  232.     if(!(pmd = new MatDraw(nSlot))) 
  233.     { 
  234.         cout<<"Fail to start initialize matlab engine!"<<endl; 
  235.         Exit(); 
  236.     } 
  237.     for(size_t index = 0; index != chainHash::NumOfHashF; index++) 
  238.     { 
  239.         ColliMatrix[index] = new double[nSlot]; 
  240.         if(!ColliMatrix[index]) { 
  241.             cout<<"Fail to Allocate Memory!"<<endl; 
  242.             Exit(); 
  243.         } 
  244.         memset(ColliMatrix[index], 0, nSlot*sizeof(double)); 
  245.     } 
  246.     a = GeneFrom(1,nSlot-1); 
  247.     b = GeneFrom(0,nSlot-1); 
  248.     memset(pht, 0, nSlot*sizeof(HashTable)); 
  249.     memset(pcn, 0, nSize*sizeof(ChainNode)); 
  250.     for(size_t index = 0; index != szSet; index++) 
  251.         pcn[index].key = SrcSet[index]; 
  252.  
  253. chainHash::~chainHash()  
  254.     if(pht) delete [] pht;  
  255.     if(pcn) delete [] pcn; 
  256.     if(pmd) delete pmd; 
  257.     for(size_t index = 0; index != chainHash::NumOfHashF; index++) 
  258.         delete [] ColliMatrix[index]; 
  259.     delete [] ColliMatrix; 
  260.  
  261. void chainHash::getCorresIndex(const HashFunc &hf) 
  262. #ifdef __TEST__ 
  263.     ChHsh phf = hfTable[hf]; 
  264.     for(size_t iter = 0; iter != szSet; iter++) 
  265.         cout<<SrcSet[iter]<<' '<<(this->*phf)(SrcSet[iter])<<endl; 
  266.     cout<<endl; 
  267. #endif 
  268.  
  269. void chainHash::InsertIntoHashTable(const HashFunc &hf) 
  270.     ChHsh phf = hfTable[hf]; 
  271.     size_t szLabel; 
  272.     for(size_t index = 0; index != szSet; index++) 
  273.     { 
  274.         szLabel = (this->*phf)(pcn[index].key); 
  275.         pcn[index].next = pht[szLabel].head; 
  276.         pht[szLabel].head = &pcn[index]; 
  277.         ++pht[szLabel].szCnt; 
  278.     } 
  279.  
  280. void chainHash::IterEveryChain(const HashFunc &hf) const 
  281. #ifdef __TEST__ 
  282.     if(hf == chainHash::REMAIN) 
  283.         cout<<"Iterate the chain hashed by <remainder>"<<endl; 
  284.     else if(hf == chainHash::MULTI) 
  285.         cout<<"Iterate the chain hashed by <multiplicate>"<<endl; 
  286.     else if(hf == chainHash::UNIVER) 
  287.         cout<<"Iterate the chain hashed by <univerhash>"<<endl; 
  288.     else { 
  289.         cout<<"Wrong : Iterate the chain hashed by NULL function"<<endl; 
  290.         Exit(); 
  291.     } 
  292. #endif 
  293.     for(size_t index = 0; index != nSlot; index++) 
  294.     { 
  295.         size_t szCnt = pht[index].szCnt; 
  296.         *(ColliMatrix[static_cast<size_t>(hf)]+index) = szCnt; 
  297. #ifdef __TEST__ 
  298.         cout<<"the number of nodes in the chain : "<<szCnt 
  299.             <<'\t'<<"the node :"
  300.         for(ChainNode *p=pht[index].head; p != NULL; p = p->next) 
  301.             cout<<p->key<<"-->"
  302.         cout<<endl; 
  303. #endif 
  304.     } 
  305.  
  306. void chainHash::RunAndGetCollision() 
  307.     size_t limit = sizeof(chainHash::hfTable)/sizeof(chainHash::hfTable[0]); 
  308.     for(size_t index = 0; index != limit; ++index) 
  309.     { 
  310.         getCorresIndex(static_cast<HashFunc>(index)); 
  311.         InsertIntoHashTable(static_cast<HashFunc>(index)); 
  312.         IterEveryChain(static_cast<HashFunc>(index)); 
  313.         memset(pht, 0, nSlot*sizeof(HashTable)); 
  314.         for(size_t ix = 0; ix != szSet; ix++) 
  315.             pcn[ix].next = NULL; 
  316. #ifdef __TEST__ 
  317.         for(size_t ix = 0; ix !=nSlot; ix++) 
  318.             cout<<*(ColliMatrix[index]+ix)<<' '
  319.         cout<<endl<<endl; 
  320. #endif 
  321.     } 
  322.  
  323. void openAddrHash::GeneArg(const HashFunc &hf) 
  324.     size_t start, end; 
  325.     switch(hf) { 
  326.     case openAddrHash::LINEAR: 
  327.         start = 0; end = 1+1; break
  328.     case openAddrHash::QUADRATIC: 
  329.         start = 2; end = 3+1; break
  330.     case openAddrHash::DBHASH: 
  331.         start = 4; end = 7+1; break
  332.     default
  333.         cout<<"Wrong : No existing hash function !"<<endl; 
  334.         Exit(); 
  335.     } 
  336.     for(size_t index = start; index != end; index += 2) 
  337.     { 
  338.         arg[index] = GeneFrom(1, nSlot-1); 
  339.         arg[index+1] = GeneFrom(0, nSlot-1); 
  340.     } 
  341.  
  342. openAddrHash::openAddrHash(const size_t &nSize, const size_t &slot) : Hash(nSize, slot) 
  343.     if(!(pht = new HashTable[nSlot]) || 
  344.         !(ColliMatrix = new double*[openAddrHash::NumOfHashF]) ) 
  345.     { 
  346.         cout<<"Fail to Allocate Memory!"<<endl; 
  347.         Exit(); 
  348.     } 
  349.     if(!(pmd = new MatDraw(COUNT_OF_EXECUTE))) 
  350.     { 
  351.         cout<<"Fail to start initialize matlab engine!"<<endl; 
  352.         Exit(); 
  353.     } 
  354.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++) 
  355.     { 
  356.         if(!(ColliMatrix[index] = new double[COUNT_OF_EXECUTE])) 
  357.         { 
  358.             cout<<"Fail to Allcate Memory!"<<endl; 
  359.             Exit(); 
  360.         } 
  361.         memset(ColliMatrix[index], 0, COUNT_OF_EXECUTE*sizeof(double)); 
  362.     } 
  363.     memset(pht, 0, nSlot*sizeof(HashTable)); 
  364.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++) 
  365.         GeneArg(static_cast<HashFunc>(index)); 
  366.     c1 = GeneFrom(START, END); 
  367.     c2 = GeneFrom(START, END); 
  368.  
  369. openAddrHash::~openAddrHash() 
  370.     if(pht) delete [] pht; 
  371.     if(pmd) delete pmd; 
  372.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++) 
  373.         delete [] ColliMatrix[index]; 
  374.     delete [] ColliMatrix; 
  375.  
  376. void openAddrHash::getCorresIndex(const HashFunc &hf) throw(exception) 
  377. #ifdef __TEST__ 
  378.     printInfo(hf); 
  379. #endif 
  380.     OAHashF pOpenAddrHf = hfTable[hf]; 
  381.     size_t szLabel, ix; 
  382.     for(size_t index = 0; index != szSet; index++) 
  383.     { 
  384.         for(ix = 0; ix != nSlot; ix++) 
  385.         { 
  386.             szLabel = (this->*pOpenAddrHf)(SrcSet[index], ix); 
  387.             ++pht[szLabel].szCnt; 
  388. #ifdef __TEST__ 
  389.             cout<<SrcSet[index]<<'\t'<<szLabel<<endl; 
  390. #endif 
  391.             if(!pht[szLabel].IsNotEmpty) { 
  392.                 pht[szLabel].IsNotEmpty = true
  393.                 break
  394.             } 
  395.         } 
  396.         if(ix == nSlot) 
  397.             throw exception("hash Failure, restart ...\n"); 
  398.     } 
  399. #ifdef __TEST__ 
  400.     for(size_t index = 0; index != nSlot; index++) 
  401.         cout<<"Slot : "<<index<<"\tCounts of Collision : "<<pht[index].szCnt<<endl; 
  402. #endif 
  403.  
  404. void openAddrHash::RunAndGetCollision() 
  405.     size_t totalCollision, totalKeyInHashTable; 
  406.     for(size_t index = 0; index != COUNT_OF_EXECUTE; index++) 
  407.     { 
  408.         for(size_t ix = 0; ix != openAddrHash::NumOfHashF; ix++) 
  409.         { 
  410.             try
  411.                 memset(pht, 0, nSlot*sizeof(HashTable)); 
  412.                 totalCollision = totalKeyInHashTable = 0; 
  413.                 getCorresIndex(static_cast<HashFunc>(ix)); 
  414.                 for(size_t j = 0; j != nSlot; j++) 
  415.                     totalCollision += pht[j].szCnt; 
  416.             } catch(exception &e) { 
  417.                 cout<<e.what(); 
  418.                 GeneArg(static_cast<HashFunc>(ix)); 
  419.                 --ix; continue
  420.             } 
  421.             ColliMatrix[ix][index] = totalCollision; 
  422.         } 
  423.         cout<<index<<" test has been finished!"<<endl; 
  424.         GeneRand(szSet);        //Regenerate source set & continue to test 
  425.     } 
  426. #ifdef __TEST__ 
  427.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++) 
  428.     { 
  429.         for(size_t ix = 0; ix != COUNT_OF_EXECUTE; ix++) 
  430.             cout<<ColliMatrix[index][ix]<<' '
  431.         cout<<endl; 
  432.     } 
  433. #endif 
  434.  
  435. void openAddrHash::printInfo(const HashFunc &hf) 
  436.     cout<<"hash' & hash'' assist hash function --->" 
  437.         " (a*key+b) mod m, here m="<<nSlot<<endl; 
  438.     switch(hf) { 
  439.     case openAddrHash::LINEAR: 
  440.         cout<<"Linear Probing : \n"<<"a1="<<arg[0]<<"\tb1="<<arg[1]<<'\n'
  441.         cout<<"hash(key,i)=(hash'(key)+i) mod m"<<endl; 
  442.         break
  443.     case openAddrHash::QUADRATIC: 
  444.         cout<<"Quadratic Probing : \n"<<"a1="<<arg[2]<<"\tb1="<<arg[3]<<'\n'
  445.         cout<<"c1="<<c1<<"\tc2="<<c2<<'\n'
  446.         cout<<"hash(key,i)=(hash'(key)+c1*i+c2*i*i) mod m"<<endl; 
  447.         break
  448.     case openAddrHash::DBHASH: 
  449.         cout<<"DoubleHash Probing : \n"<<"a1="<<arg[4]<<"\tb1="<<arg[5]<<'\n'
  450.         cout<<"a2="<<arg[6]<<"\tb2="<<arg[7]<<'\n'
  451.         cout<<"hash(key,i)=(hash'(key)+i*hash''(key)) mod m"<<endl; 
  452.         break
  453.     default
  454.         cout<<"Wrong : There has no More Hash Function!"<<endl; 
  455.         Exit(); 
  456.     } 
  457.  
  458. MatDraw::MatDraw(const mwSize &n_Length) : ArrLength(n_Length) 
  459.     double * InitForXOrder = new double[n_Length]; 
  460.     /*start matlab engine*/ 
  461.     if(!(ep = engOpen(NULL))) { 
  462.         cout<<"Fail to start Matlab!"<<endl; 
  463.         exit(0); 
  464.     } 
  465.     engSetVisible(ep, false);        /*hide the main window*/ 
  466.     /*create maxtrix as a variable*/ 
  467.     if(!(paForXOrder = mxCreateDoubleMatrix(1, n_Length, mxREAL)) || 
  468.         !(paForYOrder = mxCreateDoubleMatrix(1, n_Length, mxREAL))) 
  469.     { 
  470.         cout<<"Fail to create matrix!"<<endl; 
  471.         exit(0); 
  472.     } 
  473.     for(size_t index = 0; index != n_Length; index++) 
  474.         InitForXOrder[index] = index; 
  475.     /*copy InitForXOrder to mxArrary*/ 
  476.     memmove(mxGetPr(paForXOrder), InitForXOrder, ArrLength*sizeof(double)); 
  477.     /*write mxArray into matlab workerspace*/ 
  478.     engPutVariable(ep, "paForXOrder", paForXOrder); 
  479.     delete InitForXOrder; 
  480.  
  481. /*RowsOfMatrix from [0,8)*/ 
  482. void MatDraw::mdPlot(double **pd, const size_t &RowsOfMatrix) 
  483.     char temp[1024]; 
  484.     string EvalStr("title('collision statistics');"), colStr("rgbykmc"); 
  485.     sprintf(temp, "xlabel('x=[0 : %d]');", ArrLength); 
  486.     EvalStr += string(temp); 
  487.     EvalStr += string("ylabel('the count ofs collision');"); 
  488.     EvalStr += string("hold on;"); 
  489.     for(size_t index = 0; index != RowsOfMatrix; index++) 
  490.     { 
  491.         memmove(mxGetPr(paForYOrder), pd[index],  
  492.             ArrLength*sizeof(double)); 
  493.         engPutVariable(ep, "paForYOrder", paForYOrder); 
  494.         EvalStr += string("plot(paForXOrder,paForYOrder,\'"
  495.             +colStr[index]+string("*-\');"); 
  496.         engEvalString(ep, EvalStr.c_str()); 
  497.         EvalStr.clear(); 
  498.     } 
[cpp] view plain copy
  1. #include <iostream>  
  2. #include <cmath>  
  3. #include <ctime>  
  4. #include <string>  
  5. #include <exception>  
  6. #include <Windows.h>  
  7. #include <engine.h>  
  8. using namespace std;  
  9.   
  10. //#define __TEST__  
  11. #pragma warning(disable:4244)  
  12. #pragma warning(disable:4996)  
  13. #pragma warning(disable:4290)  
  14. const size_t KEY_SCALE = 600;        //the number of keys in source set  
  15. const size_t SLOTS_IN_CHAIN = 45;        //the number of slots in chained hash table  
  16. const size_t SLOTS_IN_OPENADDR = KEY_SCALE;    /*in open addressing hash table*/  
  17. const size_t COUNT_OF_EXECUTE = 60;        //counts of hash by open addressing  
  18.   
  19. class MatDraw{  
  20. public:  
  21.     MatDraw(const mwSize&);  
  22.     ~MatDraw()  
  23.     {  
  24.         mxDestroyArray(paForXOrder);  
  25.         mxDestroyArray(paForYOrder);  
  26.         engClose(ep);  
  27.     }  
  28.     void mdPlot(double **, const size_t&);  
  29.     void EvalStr(string str)  
  30.     { engEvalString(ep, str.c_str()); }  
  31. private:  
  32.     MatDraw(const MatDraw&);  
  33.     MatDraw& operator=(const MatDraw&);  
  34. private:  
  35.     Engine *ep;  
  36.     mxArray *paForXOrder;  
  37.     mxArray *paForYOrder;  
  38.     size_t ArrLength;  
  39. };  
  40.   
  41. class Hash{  
  42. public:  
  43.     virtual void plot() = 0;  
  44.     virtual void RunAndGetCollision() = 0;  
  45.     Hash(const size_t&, const size_t&);  
  46.     virtual ~Hash()  
  47.     { if(SrcSet) delete [] SrcSet; }  
  48. protected:  
  49.     typedef size_t index;  
  50.     void GeneRand(const size_t&);  
  51.     size_t GeneFrom(size_t left, size_t right) const    //generate random [left,right]  
  52.     { return (rand()%(right-left+1))+left; }  
  53.     size_t getClosePrime() const;  
  54.     void Exit() const { system("pause"); exit(0); }  
  55.     size_t *SrcSet;        //source set to be hashed  
  56.     size_t szSet;            //size of source set  
  57.     size_t approSlot;        //let user choose approximate Slot in hash table  
  58.     size_t nSlot;        //the size of hash table  
  59. protected:  
  60.     /*this universal function provide service for chain 
  61.      *hash & open address at the same time*/  
  62.     index unihash(const size_t &key, const size_t &arg_a, const size_t &arg_b)  
  63.     {  
  64.         //hash(key)=(a*key+b) mod m -->a & b are random  
  65.         return (arg_a*key+arg_b) % nSlot;  
  66.     }  
  67. };  
  68.   
  69. class chainHash : public Hash{  
  70. private:  
  71.     typedef index (chainHash::*ChHsh)(const size_t&);  
  72.     struct ChainNode{  
  73.         size_t key;  
  74.         ChainNode *next;  
  75.     };  
  76.     struct HashTable{  
  77.         ChainNode *head;  
  78.         size_t szCnt;        //the number of node in chain  
  79.     };  
  80. public:  
  81.     typedef enum { REMAIN, MULTI, UNIVER } HashFunc;  
  82.     chainHash(const size_t&, const size_t&);  
  83.     ~chainHash();  
  84.     void getCorresIndex(const HashFunc&);  
  85.     void IterEveryChain(const HashFunc &) const;  
  86.     void InsertIntoHashTable(const HashFunc&);  
  87.     void RunAndGetCollision();  
  88.     void plot()  
  89.     {  
  90.         pmd->mdPlot(ColliMatrix,chainHash::NumOfHashF);  
  91.         pmd->EvalStr(string("legend('remainder','multiplicate','univerhash');").c_str());  
  92.     }  
  93. private:  
  94.     chainHash(const chainHash&);  
  95.     chainHash& operator=(const chainHash&);  
  96.     /*three methods for chained hash*/  
  97.     index remainder(const size_t &key) { return key%nSlot; }  
  98.     index multiplicate(const size_t &key)  
  99.     {  
  100.         //hash(key) = floor(m*(A*key mod 1))  
  101.         float A = (sqrt(5.0)-1)/2;        //golden section ratio  
  102.         float temp = A*key;  
  103.         return (temp-static_cast<size_t>(temp))*nSlot;  
  104.     }  
  105.     index univerhash(const size_t &key)  
  106.     {  
  107.         //hash(key)=(a*key+b) mod m -->a & b are random  
  108.         return unihash(key, a, b);  
  109.     }  
  110. private:  
  111.     //here store the argument for the 'univerhash' in a instance  
  112.     size_t a, b;  
  113. private:  
  114.     HashTable *pht;        //dynamic allocate hash table  
  115.     ChainNode *pcn;        //allocate nodes in chain dynamically  
  116.     static ChHsh hfTable[];        //function table  
  117.     static size_t NumOfHashF;  
  118.     double **ColliMatrix;            //store the counts of Collision  
  119.     class MatDraw *pmd;        //picture pen  
  120. };  
  121.   
  122. class openAddrHash : public Hash {  
  123. private:  
  124.     typedef index (openAddrHash::*OAHashF)(const size_t&, const size_t&);  
  125.     struct HashTable {  
  126.         size_t szCnt;  
  127.         bool IsNotEmpty;  
  128.     };  
  129. public:  
  130.     typedef enum { LINEAR, QUADRATIC, DBHASH} HashFunc;  
  131.     openAddrHash(const size_t&, const size_t&);  
  132.     ~openAddrHash();  
  133.     void plot()   
  134.     {  
  135.         pmd->EvalStr(string("figure;").c_str());  
  136.         pmd->mdPlot(ColliMatrix, openAddrHash::NumOfHashF);  
  137.         pmd->EvalStr(string("legend('linear','quadratic','doublehash');").c_str());  
  138.     }  
  139.     void RunAndGetCollision();  
  140. private:  
  141.     /*three methods for Open Addressing -->hash' & hash'' are assist functions*/  
  142.     /* hash(key, i)=(hash'(key)+i) mod m*/  
  143.     index linearprob(const size_t &key, const size_t &ix)  
  144.     { return (unihash(key, arg[0], arg[1])+ix)%nSlot; }  
  145.     /* hash(key, i)=(hash'(key)+c1*i+c2*i*i) mod m*/  
  146.     index quadprob(const size_t &key, const size_t &ix)  
  147.     { return (unihash(key, arg[2], arg[3])+c1*ix+c2*ix*ix)%nSlot; }  
  148.     /* hash(key, i)=(hash'(key)+i*hash''(key)) mod m*/  
  149.     index dbhashprob(const size_t &key, const size_t &ix)  
  150.     { return (unihash(key, arg[4], arg[5])+ix*unihash(key, arg[6], arg[7]))%nSlot; }  
  151.     void getCorresIndex(const HashFunc&) throw(exception);  
  152.     void printInfo(const HashFunc&);  
  153. private:  
  154.     //used in assist hash functions  
  155.     /*in array 'arg'  ----> 
  156.      *arg[0]~arg[1] is used for linearprob 
  157.      *arg[2]~arg[3] is used for quadprob 
  158.      *arg[4]~arg[7] is for dbhashprob since it has two assist hash functions*/  
  159. #define ARG_IN_HASHFUNC 8  
  160.     size_t arg[ARG_IN_HASHFUNC];  
  161.     void GeneArg(const HashFunc&);  
  162.     /*for quadprob*/  
  163. #define START 1  
  164. #define END 5  
  165.     /*assist arguments ranged from [START, END] -- here can be changed*/  
  166.     size_t c1, c2;  /*extra arguments used in quadprob function*/  
  167. private:  
  168.     HashTable *pht;  
  169.     double **ColliMatrix;        //total counts of collision  
  170.     class MatDraw *pmd;        //picture pen  
  171.     static OAHashF hfTable[];  
  172.     static size_t NumOfHashF;  
  173. };  
  174.   
  175. chainHash::ChHsh chainHash::hfTable[] = {&chainHash::remainder,  
  176.                                            &chainHash::multiplicate,  
  177.                                            &chainHash::univerhash};  
  178. size_t chainHash::NumOfHashF = sizeof(chainHash::hfTable)/sizeof(chainHash::hfTable[0]);  
  179.   
  180. openAddrHash::OAHashF openAddrHash::hfTable[] = {&openAddrHash::linearprob,  
  181.                                                 &openAddrHash::quadprob,  
  182.                                                 &openAddrHash::dbhashprob};  
  183. size_t openAddrHash::NumOfHashF = sizeof(openAddrHash::hfTable)/  
  184.     sizeof(openAddrHash::hfTable[0]);  
  185.   
  186. int main(int argc, char *argv[])  
  187. {  
  188.     srand(static_cast<unsigned>(time(NULL)));  
  189.     Hash *ph = new chainHash(KEY_SCALE,SLOTS_IN_CHAIN);  
  190.     ph->RunAndGetCollision();  
  191.     ph->plot();  
  192.     delete ph;  
  193.     ph = new openAddrHash(KEY_SCALE,SLOTS_IN_OPENADDR);  
  194.     ph->RunAndGetCollision();  
  195.     ph->plot();  
  196.     delete ph;  
  197.     system("pause");  
  198.     return EXIT_SUCCESS;  
  199. }  
  200.   
  201. Hash::Hash(const size_t &szKey, const size_t &slot) : szSet(szKey),  
  202.     approSlot(slot),nSlot(getClosePrime())  
  203. {  
  204.     if(!(SrcSet = new size_t[szSet]))  
  205.     {  
  206.         cout<<"Fail to Allocate Memory!"<<endl;  
  207.         Exit();  
  208.     }  
  209.     GeneRand(szSet);  
  210. }  
  211.   
  212. inline void Hash::GeneRand(const size_t &nSize)  
  213. {  
  214.     for(size_t index = 0; index != nSize; index++)  
  215.         SrcSet[index] = static_cast<size_t>(rand());  
  216. }  
  217.   
  218. inline size_t Hash::getClosePrime() const  
  219. {  
  220.     size_t start = approSlot;  
  221.     for( ; ; )  
  222.     {  
  223.         size_t index = 2, bound = sqrt(static_cast<double>(start));  
  224.         for(; index <= bound; index++ )  
  225.             if(start%index == 0) break;  
  226.         if(index > bound) break;  
  227.         else ++start;  
  228.     }  
  229.     return start;  
  230. }  
  231.   
  232. chainHash::chainHash(const size_t &nSize, const size_t &slot) : Hash(nSize, slot)  
  233. {  
  234.     if( !(pht = new HashTable[nSlot]) ||  
  235.         !(pcn = new ChainNode[nSize]) ||   
  236.         !(ColliMatrix = new double*[chainHash::NumOfHashF]))  
  237.     {  
  238.         cout<<"Fail to Allocate Memory!"<<endl;  
  239.         Exit();  
  240.     }  
  241.     if(!(pmd = new MatDraw(nSlot)))  
  242.     {  
  243.         cout<<"Fail to start initialize matlab engine!"<<endl;  
  244.         Exit();  
  245.     }  
  246.     for(size_t index = 0; index != chainHash::NumOfHashF; index++)  
  247.     {  
  248.         ColliMatrix[index] = new double[nSlot];  
  249.         if(!ColliMatrix[index]) {  
  250.             cout<<"Fail to Allocate Memory!"<<endl;  
  251.             Exit();  
  252.         }  
  253.         memset(ColliMatrix[index], 0, nSlot*sizeof(double));  
  254.     }  
  255.     a = GeneFrom(1,nSlot-1);  
  256.     b = GeneFrom(0,nSlot-1);  
  257.     memset(pht, 0, nSlot*sizeof(HashTable));  
  258.     memset(pcn, 0, nSize*sizeof(ChainNode));  
  259.     for(size_t index = 0; index != szSet; index++)  
  260.         pcn[index].key = SrcSet[index];  
  261. }  
  262.   
  263. chainHash::~chainHash()   
  264. {  
  265.     if(pht) delete [] pht;   
  266.     if(pcn) delete [] pcn;  
  267.     if(pmd) delete pmd;  
  268.     for(size_t index = 0; index != chainHash::NumOfHashF; index++)  
  269.         delete [] ColliMatrix[index];  
  270.     delete [] ColliMatrix;  
  271. }  
  272.   
  273. void chainHash::getCorresIndex(const HashFunc &hf)  
  274. {  
  275. #ifdef __TEST__  
  276.     ChHsh phf = hfTable[hf];  
  277.     for(size_t iter = 0; iter != szSet; iter++)  
  278.         cout<<SrcSet[iter]<<' '<<(this->*phf)(SrcSet[iter])<<endl;  
  279.     cout<<endl;  
  280. #endif  
  281. }  
  282.   
  283. void chainHash::InsertIntoHashTable(const HashFunc &hf)  
  284. {  
  285.     ChHsh phf = hfTable[hf];  
  286.     size_t szLabel;  
  287.     for(size_t index = 0; index != szSet; index++)  
  288.     {  
  289.         szLabel = (this->*phf)(pcn[index].key);  
  290.         pcn[index].next = pht[szLabel].head;  
  291.         pht[szLabel].head = &pcn[index];  
  292.         ++pht[szLabel].szCnt;  
  293.     }  
  294. }  
  295.   
  296. void chainHash::IterEveryChain(const HashFunc &hf) const  
  297. {  
  298. #ifdef __TEST__  
  299.     if(hf == chainHash::REMAIN)  
  300.         cout<<"Iterate the chain hashed by <remainder>"<<endl;  
  301.     else if(hf == chainHash::MULTI)  
  302.         cout<<"Iterate the chain hashed by <multiplicate>"<<endl;  
  303.     else if(hf == chainHash::UNIVER)  
  304.         cout<<"Iterate the chain hashed by <univerhash>"<<endl;  
  305.     else {  
  306.         cout<<"Wrong : Iterate the chain hashed by NULL function"<<endl;  
  307.         Exit();  
  308.     }  
  309. #endif  
  310.     for(size_t index = 0; index != nSlot; index++)  
  311.     {  
  312.         size_t szCnt = pht[index].szCnt;  
  313.         *(ColliMatrix[static_cast<size_t>(hf)]+index) = szCnt;  
  314. #ifdef __TEST__  
  315.         cout<<"the number of nodes in the chain : "<<szCnt  
  316.             <<'\t'<<"the node :";  
  317.         for(ChainNode *p=pht[index].head; p != NULL; p = p->next)  
  318.             cout<<p->key<<"-->";  
  319.         cout<<endl;  
  320. #endif  
  321.     }  
  322. }  
  323.   
  324. void chainHash::RunAndGetCollision()  
  325. {  
  326.     size_t limit = sizeof(chainHash::hfTable)/sizeof(chainHash::hfTable[0]);  
  327.     for(size_t index = 0; index != limit; ++index)  
  328.     {  
  329.         getCorresIndex(static_cast<HashFunc>(index));  
  330.         InsertIntoHashTable(static_cast<HashFunc>(index));  
  331.         IterEveryChain(static_cast<HashFunc>(index));  
  332.         memset(pht, 0, nSlot*sizeof(HashTable));  
  333.         for(size_t ix = 0; ix != szSet; ix++)  
  334.             pcn[ix].next = NULL;  
  335. #ifdef __TEST__  
  336.         for(size_t ix = 0; ix !=nSlot; ix++)  
  337.             cout<<*(ColliMatrix[index]+ix)<<' ';  
  338.         cout<<endl<<endl;  
  339. #endif  
  340.     }  
  341. }  
  342.   
  343. void openAddrHash::GeneArg(const HashFunc &hf)  
  344. {  
  345.     size_t start, end;  
  346.     switch(hf) {  
  347.     case openAddrHash::LINEAR:  
  348.         start = 0; end = 1+1; break;  
  349.     case openAddrHash::QUADRATIC:  
  350.         start = 2; end = 3+1; break;  
  351.     case openAddrHash::DBHASH:  
  352.         start = 4; end = 7+1; break;  
  353.     default:  
  354.         cout<<"Wrong : No existing hash function !"<<endl;  
  355.         Exit();  
  356.     }  
  357.     for(size_t index = start; index != end; index += 2)  
  358.     {  
  359.         arg[index] = GeneFrom(1, nSlot-1);  
  360.         arg[index+1] = GeneFrom(0, nSlot-1);  
  361.     }  
  362. }  
  363.   
  364. openAddrHash::openAddrHash(const size_t &nSize, const size_t &slot) : Hash(nSize, slot)  
  365. {  
  366.     if(!(pht = new HashTable[nSlot]) ||  
  367.         !(ColliMatrix = new double*[openAddrHash::NumOfHashF]) )  
  368.     {  
  369.         cout<<"Fail to Allocate Memory!"<<endl;  
  370.         Exit();  
  371.     }  
  372.     if(!(pmd = new MatDraw(COUNT_OF_EXECUTE)))  
  373.     {  
  374.         cout<<"Fail to start initialize matlab engine!"<<endl;  
  375.         Exit();  
  376.     }  
  377.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++)  
  378.     {  
  379.         if(!(ColliMatrix[index] = new double[COUNT_OF_EXECUTE]))  
  380.         {  
  381.             cout<<"Fail to Allcate Memory!"<<endl;  
  382.             Exit();  
  383.         }  
  384.         memset(ColliMatrix[index], 0, COUNT_OF_EXECUTE*sizeof(double));  
  385.     }  
  386.     memset(pht, 0, nSlot*sizeof(HashTable));  
  387.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++)  
  388.         GeneArg(static_cast<HashFunc>(index));  
  389.     c1 = GeneFrom(START, END);  
  390.     c2 = GeneFrom(START, END);  
  391. }  
  392.   
  393. openAddrHash::~openAddrHash()  
  394. {  
  395.     if(pht) delete [] pht;  
  396.     if(pmd) delete pmd;  
  397.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++)  
  398.         delete [] ColliMatrix[index];  
  399.     delete [] ColliMatrix;  
  400. }  
  401.   
  402. void openAddrHash::getCorresIndex(const HashFunc &hf) throw(exception)  
  403. {  
  404. #ifdef __TEST__  
  405.     printInfo(hf);  
  406. #endif  
  407.     OAHashF pOpenAddrHf = hfTable[hf];  
  408.     size_t szLabel, ix;  
  409.     for(size_t index = 0; index != szSet; index++)  
  410.     {  
  411.         for(ix = 0; ix != nSlot; ix++)  
  412.         {  
  413.             szLabel = (this->*pOpenAddrHf)(SrcSet[index], ix);  
  414.             ++pht[szLabel].szCnt;  
  415. #ifdef __TEST__  
  416.             cout<<SrcSet[index]<<'\t'<<szLabel<<endl;  
  417. #endif  
  418.             if(!pht[szLabel].IsNotEmpty) {  
  419.                 pht[szLabel].IsNotEmpty = true;  
  420.                 break;  
  421.             }  
  422.         }  
  423.         if(ix == nSlot)  
  424.             throw exception("hash Failure, restart ...\n");  
  425.     }  
  426. #ifdef __TEST__  
  427.     for(size_t index = 0; index != nSlot; index++)  
  428.         cout<<"Slot : "<<index<<"\tCounts of Collision : "<<pht[index].szCnt<<endl;  
  429. #endif  
  430. }  
  431.   
  432. void openAddrHash::RunAndGetCollision()  
  433. {  
  434.     size_t totalCollision, totalKeyInHashTable;  
  435.     for(size_t index = 0; index != COUNT_OF_EXECUTE; index++)  
  436.     {  
  437.         for(size_t ix = 0; ix != openAddrHash::NumOfHashF; ix++)  
  438.         {  
  439.             try{  
  440.                 memset(pht, 0, nSlot*sizeof(HashTable));  
  441.                 totalCollision = totalKeyInHashTable = 0;  
  442.                 getCorresIndex(static_cast<HashFunc>(ix));  
  443.                 for(size_t j = 0; j != nSlot; j++)  
  444.                     totalCollision += pht[j].szCnt;  
  445.             } catch(exception &e) {  
  446.                 cout<<e.what();  
  447.                 GeneArg(static_cast<HashFunc>(ix));  
  448.                 --ix; continue;  
  449.             }  
  450.             ColliMatrix[ix][index] = totalCollision;  
  451.         }  
  452.         cout<<index<<" test has been finished!"<<endl;  
  453.         GeneRand(szSet);        //Regenerate source set & continue to test  
  454.     }  
  455. #ifdef __TEST__  
  456.     for(size_t index = 0; index != openAddrHash::NumOfHashF; index++)  
  457.     {  
  458.         for(size_t ix = 0; ix != COUNT_OF_EXECUTE; ix++)  
  459.             cout<<ColliMatrix[index][ix]<<' ';  
  460.         cout<<endl;  
  461.     }  
  462. #endif  
  463. }  
  464.   
  465. void openAddrHash::printInfo(const HashFunc &hf)  
  466. {  
  467.     cout<<"hash' & hash'' assist hash function --->"  
  468.         " (a*key+b) mod m, here m="<<nSlot<<endl;  
  469.     switch(hf) {  
  470.     case openAddrHash::LINEAR:  
  471.         cout<<"Linear Probing : \n"<<"a1="<<arg[0]<<"\tb1="<<arg[1]<<'\n';  
  472.         cout<<"hash(key,i)=(hash'(key)+i) mod m"<<endl;  
  473.         break;  
  474.     case openAddrHash::QUADRATIC:  
  475.         cout<<"Quadratic Probing : \n"<<"a1="<<arg[2]<<"\tb1="<<arg[3]<<'\n';  
  476.         cout<<"c1="<<c1<<"\tc2="<<c2<<'\n';  
  477.         cout<<"hash(key,i)=(hash'(key)+c1*i+c2*i*i) mod m"<<endl;  
  478.         break;  
  479.     case openAddrHash::DBHASH:  
  480.         cout<<"DoubleHash Probing : \n"<<"a1="<<arg[4]<<"\tb1="<<arg[5]<<'\n';  
  481.         cout<<"a2="<<arg[6]<<"\tb2="<<arg[7]<<'\n';  
  482.         cout<<"hash(key,i)=(hash'(key)+i*hash''(key)) mod m"<<endl;  
  483.         break;  
  484.     default:  
  485.         cout<<"Wrong : There has no More Hash Function!"<<endl;  
  486.         Exit();  
  487.     }  
  488. }  
  489.   
  490. MatDraw::MatDraw(const mwSize &n_Length) : ArrLength(n_Length)  
  491. {  
  492.     double * InitForXOrder = new double[n_Length];  
  493.     /*start matlab engine*/  
  494.     if(!(ep = engOpen(NULL))) {  
  495.         cout<<"Fail to start Matlab!"<<endl;  
  496.         exit(0);  
  497.     }  
  498.     engSetVisible(ep, false);        /*hide the main window*/  
  499.     /*create maxtrix as a variable*/  
  500.     if(!(paForXOrder = mxCreateDoubleMatrix(1, n_Length, mxREAL)) ||  
  501.         !(paForYOrder = mxCreateDoubleMatrix(1, n_Length, mxREAL)))  
  502.     {  
  503.         cout<<"Fail to create matrix!"<<endl;  
  504.         exit(0);  
  505.     }  
  506.     for(size_t index = 0; index != n_Length; index++)  
  507.         InitForXOrder[index] = index;  
  508.     /*copy InitForXOrder to mxArrary*/  
  509.     memmove(mxGetPr(paForXOrder), InitForXOrder, ArrLength*sizeof(double));  
  510.     /*write mxArray into matlab workerspace*/  
  511.     engPutVariable(ep, "paForXOrder", paForXOrder);  
  512.     delete InitForXOrder;  
  513. }  
  514.   
  515. /*RowsOfMatrix from [0,8)*/  
  516. void MatDraw::mdPlot(double **pd, const size_t &RowsOfMatrix)  
  517. {  
  518.     char temp[1024];  
  519.     string EvalStr("title('collision statistics');"), colStr("rgbykmc");  
  520.     sprintf(temp, "xlabel('x=[0 : %d]');", ArrLength);  
  521.     EvalStr += string(temp);  
  522.     EvalStr += string("ylabel('the count ofs collision');");  
  523.     EvalStr += string("hold on;");  
  524.     for(size_t index = 0; index != RowsOfMatrix; index++)  
  525.     {  
  526.         memmove(mxGetPr(paForYOrder), pd[index],   
  527.             ArrLength*sizeof(double));  
  528.         engPutVariable(ep, "paForYOrder", paForYOrder);  
  529.         EvalStr += string("plot(paForXOrder,paForYOrder,\'")  
  530.             +colStr[index]+string("*-\');");  
  531.         engEvalString(ep, EvalStr.c_str());  
  532.         EvalStr.clear();  
  533.     }  
  534. }  

上述測試不再生成matlab腳本,而是直接在vc中調用其函數庫,因此需要進行如下設置(VS2010):

  1. 右擊解決方案 --> 打開屬性窗口 --> 點擊"VC++目錄"選項卡。
  2. 在“包含目錄”選項中添加頭文件目錄,如“d:\Program Files\MATLAB\R2009a\extern\include;”。
  3. 在“庫目錄”中添加庫文件目錄,如”d:\Program Files\MATLAB\R2009a\extern\lib\win32\microsoft;“。
  4. 最後選中”鏈接器“ --> "輸入"選項卡,在”附加依賴項“中添加添加如下庫"libeng.lib;libmx.lib;libmex.lib;"。

上述最後一步也可以換成在源代碼中添加預處理指令,格式如#pragma comment(lib,"libeng.lib"),將上述三個庫顯式命令鏈接器加載。

與測試用例相關的配置介紹完了,我們最後簡單分析執行所得結果


在我們的測試用例中,測試函數散列狀況的方法是將同一個待散列集合中的每一個元素根據某個散列函數得到與其對應的槽,並將所有元素以鏈表的形式鏈接在該槽中,最終統計各個槽中存儲的元素個數用來表示碰撞的次數。可以發現,集合被除餘散列,乘法散列以及全域散列作用之後的散列狀況大致相同,這是因爲在我們的測試中,使用的待散列元素爲隨機生成數。這裏不再精確計算每個散列函數下的碰撞次數的方差,因爲如果重新生成新的隨機散列集合,方差也將隨之改變,因此在隨機生成的集合下判定到底哪個函數的散列性能較好沒有意義,一個重要的原則是首先分析待散列的集合具有什麼樣的特點,之後再具體選擇有針對性的散列函數。

上述執行結果爲開放尋址下的三種函數的散列性能,測試所用的方法是爲每個槽設置一個計數值,並且元素在探查到某個空槽之前將探查序列中的每個槽所對應的計數值均自增1(包括該空槽),最終統計槽被該散列集合中的元素碰撞的總次數,以此表示該散列函數性能,之後多次重新生成隨機數的集合進行測試。我們開放尋址下的三種函數中,線性試探法的散列性能最差,而二次試探與雙重散列的性能相當,這是因爲線性試探法中存在嚴重的羣集現象

後記
散列表這種數據結構僅僅只是散列函數一個較爲簡單的應用。試想一下散列函數將輸入映射爲某個輸出,既然可以將該輸出解釋爲所要尋址的槽,那麼自然也可以具有其他的解釋,一個具有重大意義的應用領域即爲密碼學。這裏簡單提一下王小云教授MD5所採用的破解方式,準確來說她所採用的方法不叫破解,而是構造了一種能夠快速產生碰撞的方法,即給出一個p1,通過該方法可以很快算出一個不等於p1的p2使得 MD5(p1)=MD5(p2),根據這一點就足以把MD5槍斃掉了。這種方法並不意味着能根據MD5的Hash函數反算出明文來,亦即這是一種單向加密函數——不存在逆函數可以將密文重新變爲明文。最後要提的一點是,現有的加密方式也就那麼幾種,並且數據/字符所組合而成的輸入也是有限的,那麼自然可以通過窮舉所有輸入,並且根據現有的散列函數預先計算出所有的輸出用以加速破解,詳細見彩虹表

轉自 http://blog.csdn.net/jn1158359135/article/details/7205688

發佈了0 篇原創文章 · 獲贊 2 · 訪問量 5982
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章