哈希表与应用

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

       这样咱们只需对两个图片的哈希值进行逐位比较,就能够断定他们是否类似了。

相关文章
相关标签/搜索