哈希表與應用

1、哈希表算法

       哈希表是一種數據結構,它須要配合哈希函數使用,用於創建索引,便於快速查找。數據結構

       哈希表的實現通常來講就是一個定長的存儲空間,每一個位置存儲一個對象。若是咱們假設定長爲N,則能夠將這N個存儲位置分別編號爲0,1,...,N-1。那麼如今的問題就是,如何決定一個元素應該放到哪一個位置?如何查找一個元素在哪一個位置?最常採用的策略以下:函數

       插入:使用哈希函數計算待插入對象的哈希值,若是哈希值是H,則插入編號爲H mod N的位置。spa

       查找:使用哈希函數計算待查找對象的哈希值,若是哈希值是H,則檢查編號爲H mod N的位置。設計

       下面是一個例子,假設咱們有4個字符串須要創建索引,這4個字符串及其對應的哈希值以下表所示:對象

字符串索引

哈希值圖片

algorithmci

1圖片處理

mathematics

5

notes

9

book

15

 

       若是哈希表的長度爲10,那麼咱們一般將其描述爲下圖。

       其中,mathematics和book兩個字符串的哈希值模10的餘數相同,所以放入同一位置。圖中所採用的是所謂的鏈地址法,即用鏈表將衝突的多個對象存儲在同一位置,這也是在哈希表中解決衝突時最經常使用的方法。

       當咱們須要查找某個字符串是否在哈希表中時,只需計算其哈希值,而後到哈希表的對應位置檢查,從而達到快速查找的目的。很容易看出,若是每一個字符串都放在哈希表中的不一樣位置,則查找速度會更快。哈希函數衝突少的特色使得不一樣的字符串一般具備不一樣的哈希值。因此,通常來講,只要增大哈希表的長度,就能夠避免將兩個字符串放入同一位置的狀況。

       肯定哈希表的長度是一個重要的難題。一方面,長度過小可能會致使衝突多,查詢速度慢;另外一方面,長度太長可能會白白浪費存儲空間。若是事先知道須要索引的對象數目,則能夠設定比較合適的哈希表大小,但許多時候並不能事先獲得這個消息。一個實用的技巧是動態調整哈希表的長度;在開始時,首先選用一個較小的哈希表,當發現其使用率,即存儲的對象數目與哈希表大小的比值達到某個閾值時,就創建一個更大的哈希表並將以前的哈希表中的對象遷移過來。遷移哈希表的過程每每是很是費時的,所以,能夠同時保留新舊兩個哈希表,逐步遷移,分散計算量。在進行查找和刪除操做時,同時檢查新舊兩個哈希表。在進行插入操做時,只針對新的哈希表。每進行一次插入操做時,咱們同時將r個對象從舊哈希表遷移到新哈希表,其中,r是一個任意指定的常數。這個策略至關於每次從舊哈希表中刪除r個對象,就同時在新哈希表中增長r+1個對象。所以,只要新哈希表的長度不小於舊哈希表長度的(r+1)/r倍,咱們就能夠在徹底刪除舊哈希表的時候,保證新哈希表的使用率沒有超過舊哈希表。

2、哈希的應用

       1  類似性搜索

       哈希主要用於快速查找,而查找的對象須要是徹底相同的。也就是說,通常只要求當兩個對象徹底相同時有相同的哈希值,而兩個類似的對象的哈希值不須要有任何關係。但若是哈希函數設計得足夠巧妙,也可讓類似的對象有相同或類似的哈希值,這樣咱們就能夠藉助哈希來進行類似性搜索

       局部敏感哈希(Local-Sensitive Hashing,LSH)就定義了一類能夠衡量類似性的哈希函數,它要求類似對象的哈希值在某種意義下類似,而且有機率保證。用數學語言來描述,局部敏感哈希的定義爲知足如下兩個特性的哈希函數。

  • 若是d(x,y)<=d1,則P(hash(x)=hash(y))>=p1。也就是說,當兩個對象的距離不大於d1時,它們的哈希值相同的機率要不小於p1。
  • 若是d(x,y)>=d2,則P(hash(x)=hash(y))<=p2。也就是說,當兩個對象的距離不小於d2時,它們的哈希值相同的機率要不大於p2。

       其中,d(x,y)表示兩個對象x和y之間的距離,hash(.)是哈希函數,P(.)是機率,d一、d二、p一、p2都是常數。對於不一樣的場景,對距離的定義多是不一樣的,所以,哈希函數的設計也會很不同。

       下面再來看一個用於文檔類似性搜索的哈希函數:Simhash。Simhash能夠將文檔哈希到一個64位二進制數,使得類似的文檔具備類似的二進制數。對於一個文檔,咱們能夠把文中的每一個詞(也能夠是詞組等)做爲一個特徵,統計各個特徵的出現頻率。例如,咱們統計文檔「To be or not to be is a tough question」,其特徵與相應的頻率爲:(to,0.2)、(be,0.2)、(or,0.1)、(not,0.1)、(is,0.1)、(a,0.1)、(tough,0.1)、(question,0.1)。而後,咱們能夠利用某個傳統的哈希函數將特徵映射到64位二進制。爲了描述方便,咱們只統計到6位二進制數,並假設映射結果是to:001100,be:100100,or:001010,not:101000,is:011100,a:100101,tough:100111,question:011011。根據二進制數的各個二進制位,咱們對每一個特徵構造一個向量。若是一個特徵映射到的二進制數的某一位是1,則其向量對應位置上的份量爲該特徵的頻率,不然爲頻率的相反數。因而,各個特徵對應的向量爲

to:(-0.2,-0.2,0.2,0.2,-0.2,-0.2),

be:(0.2,-0.2,-0.2,0.2,-0.2,-0.2),

or:(-0.1,-0.1,0.1,-0.1,0.1,-0.1),

not:(0.1,-0.1,0.1,-0.1,-0.1,-0.1),

is:(-0.1,0.1,0.1,0.1,-0.1,-0.1),

a:(0.1,-0.1,-0.1,0.1,-0.1,-0.1),

tough:(0.1,-0.1,-0.1,0.1,0.1,0.1),

question:(-0.1,0.1,0.1,-0.1,0.1,0.1).

       將這些向量相加,就獲得(0,-0.6,0.2,0.4,-0.4,-0.4)。對於這一貫量的每一個向量,若是大於0就取1,不然就取0,這樣獲得的二進制數就是Simhash的最終哈希值,即001100。能夠想象,出現頻率高的特徵,其相應的向量份量的絕對值更大,對最終向量相加的結果的影響也更大。所以,若是兩個文檔類似,那麼它們出現頻率高的特徵應該比較接近,最終獲得的哈希值也就會有不少二進制位都是相同的。在查找類似文檔的時候,咱們只須要找到哈希值的各個二進制位區別很小的文檔。通常來講,咱們要求至多有3個二進制位不一樣。

       還有一類主要用於圖片類似性搜索的哈希函數是感知哈希(Perceptual Hashing)。它是一類普遍使用的圖片搜索算法,能夠作到以圖搜圖。其思想主要是經過調整大小、灰度化和二值化三個步驟,將圖片映射到一個二進制數,使得類似圖片具備相近的二進制數。咱們以圖片處理中的經典圖片Lenna爲例進行說明。

       假設咱們但願哈希值是64位二進制數,則咱們首先將圖片大小經過簡單的縮放調整爲8像素X8像素,這樣就恰好有64個像素了。在調整圖片大小獲得的新圖中,每一個像素上的RGB值實質上是原圖相應位置上的RGB平均值。而後,咱們將原圖灰度化。灰度化最多見的方式就是將每一個像素上的灰度值取爲RGB三個份量的平均值。

      將灰度化後的圖像個各個像素點的灰度值能夠寫爲矩陣

155   130   131   133   123   121   127   136 
106   102   108   100   100   86    69    60 
129   121   127   112   72    96    91    70 
138   162   146   85    120   88    77    108 
127   183   165   163   149   142   151   145 
145   123   184   125   95    79    137   178 
159   123   111   97    127   147   138   123 
89    85    148   155   166   200   134   90 

       最後一步是二值化。咱們首先計算出64個像素點的灰度平均值爲77.090,而後將小於平均值的像素點都取爲0,其他像素點都取爲1。將全部64個0或1連在一塊兒,就獲得了一個64位二進制串,也就是最終的哈希值:

1111111111111100111101101111110111111111111111111111111111111111

       這樣咱們只需對兩個圖片的哈希值進行逐位比較,就能夠斷定他們是否類似了。

相關文章
相關標籤/搜索