Day-25 Hash Function(杂凑函数), 乘法杂凑法, 除法杂凑法

Hash function

一个好的杂凑函数,可以把https://chart.googleapis.com/chart?cht=tx&chl=key均匀的分布在杂凑表的每一个slot中,也就是尽量满足简单均匀杂凑的假设,而且https://chart.googleapis.com/chart?cht=tx&chl=key分布的均匀性,不会受到元素的影响,也就是说,一个好的杂凑函数,我们希望它所产生的杂凑值,可以独立於输入的元素。

Division method(除法杂凑法)

在杂凑函数中,除法杂凑法是很常见的杂凑函数,令https://chart.googleapis.com/chart?cht=tx&chl=keyhttps://chart.googleapis.com/chart?cht=tx&chl=k,杂凑表https://chart.googleapis.com/chart?cht=tx&chl=Thttps://chart.googleapis.com/chart?cht=tx&chl=m个槽,我们通过取https://chart.googleapis.com/chart?cht=tx&chl=khttps://chart.googleapis.com/chart?cht=tx&chl=m的余数,将https://chart.googleapis.com/chart?cht=tx&chl=k映射到https://chart.googleapis.com/chart?cht=tx&chl=T的其中一个槽中,可以用以下的式子表示这个杂凑函数

https://chart.googleapis.com/chart?cht=tx&chl=h(k)%20%3D%20k%5C%20mod%5C%20m

使用这个杂凑函数时,我们需要避免一些情况的发生。

  1. 杂凑表可用的槽需要够多,假设https://chart.googleapis.com/chart?cht=tx&chl=m%3D2,则产生出的杂凑表分布的将不够平均,只有两条链结串列,导致效率低落。
  2. 如果我们给定的https://chart.googleapis.com/chart?cht=tx&chl=keyhttps://chart.googleapis.com/chart?cht=tx&chl=m都恰好为偶数,那麽我们只能使用https://chart.googleapis.com/chart?cht=tx&chl=T一半的槽,不仅浪费许多空间,且违反的我们对於杂凑函数的期望,也就是https://chart.googleapis.com/chart?cht=tx&chl=key分布的均匀性不受到输入元素的影响。
  3. 如果我们给定https://chart.googleapis.com/chart?cht=tx&chl=m%3D2%5Ep,则https://chart.googleapis.com/chart?cht=tx&chl=h(k)就是https://chart.googleapis.com/chart?cht=tx&chl=khttps://chart.googleapis.com/chart?cht=tx&chl=p个最低位数字,而这样的情况下,除非https://chart.googleapis.com/chart?cht=tx&chl=p个最低位数字他们是均匀分布的,也是以二进位观点,0和1都是均等出现的,那麽才能保证杂凑表有较好的效率,否则一般设计时,我们都会优先考虑到元素的所有位数。
0010101011001, m=2^6,那麽我们会使用到的数字为

011001=h(k),而在二进位中,1和0的出现通常都具有一定的规律性,这会导致我们产生出的杂凑情况不够均匀

一般情况下,我们在取https://chart.googleapis.com/chart?cht=tx&chl=m的时候,会选择一个不接近2的整数幕的质数,而这样取https://chart.googleapis.com/chart?cht=tx&chl=m的情况下,往往会产生出比较好的结果。像是https://chart.googleapis.com/chart?cht=tx&chl=m%3D701

除法杂凑法很常在程序中见到,因为他的程序码量不多,且效果不会太差,但相较於其他基於加法和乘法的杂凑函数,除法在电脑中需要花费更多的计算步骤,造成更多时间的消耗。

Multiplication method(乘法杂凑法)

在实际处理资料时,我们常常无法事先得知https://chart.googleapis.com/chart?cht=tx&chl=key的范围,或是https://chart.googleapis.com/chart?cht=tx&chl=key具有的特性,像是全部都是偶数之类的,这时候,乘法杂凑表就是一个不错的方法可以使用。

假设https://chart.googleapis.com/chart?cht=tx&chl=m%3D2%5Erhttps://chart.googleapis.com/chart?cht=tx&chl=m表示hash table的槽数,且在一部电脑中,每一个bytes含有https://chart.googleapis.com/chart?cht=tx&chl=w bits,则杂凑函数为

https://chart.googleapis.com/chart?cht=tx&chl=h(k)%3D(A*k%5C%20mod%5C%202%5Ew)%20%3E%3E%20(w-r)

https://chart.googleapis.com/chart?cht=tx&chl=%3E%3E表示向右偏移(right shift)的意思,就是二进制的偏移, A是一个奇数,范围介於https://chart.googleapis.com/chart?cht=tx&chl=2%5E%7Bw-1%7D%3CA%3C2%5Ew, https://chart.googleapis.com/chart?cht=tx&chl=k表示https://chart.googleapis.com/chart?cht=tx&chl=key

这个方法的关键在於https://chart.googleapis.com/chart?cht=tx&chl=A的选取,在选取https://chart.googleapis.com/chart?cht=tx&chl=A时我们必须避免选取一些特定的数值,像是选择https://chart.googleapis.com/chart?cht=tx&chl=A时要尽量避免以2为底的数字,像是2,4,8,16等数字。

基本上这个方法是一个相当快速的方法,相乘後再取余数会比起直接除法要来的更快,且二进制的偏移运算本身就十分快速,尤其是在我们进行杂凑运算之前。我们就已经得知需要偏移的位元数的情况下,又来的更加的快速。

  • Example 1. 假设我们有一个hash table,有8个槽,https://chart.googleapis.com/chart?cht=tx&chl=m%3D8%3D2%5E3https://chart.googleapis.com/chart?cht=tx&chl=w%3D7
这个杂凑函数的关键就在於A的选取,假设A = 1011001(Bin) = 89(dec),2^w-1 = 64, 2^w = 128。
然後选取一个key来乘上A,k = 1101011,接着计算A * k

        1011001 = A
*       1101011 = k
 __________________
 10010100110011
 
 得到A和k的乘积後,接着对2^7取余数,也就是取出前7位数字
 
 1001010__(0110011), 接着向右偏移(w-r)位,也就是(7-3) 
 
 0110011 >> (7-3) = 0000011,也就是取出最高位的前3位数字,而这个值就是h(k)了

而为什麽乘法杂凑法是一个很不错的演算法,我们引用下面图形进行说明,在说明之前,我们先进行一些假设

       1011001 = A
*      1101011 = k
 __________________
 10010100110011
 
 假设A是一个小於1的数,则我们可以在A的最高位天加上一个小数点,如下面所示
 
       .1011001 = A
 *      1101011 = k
___________________
1001010.0110011

而取余数的计算就是忽略掉整数部分,保留小数部分,以这个例子来说,A的小数部分以10进制来说大约是5.5左右。
而假设k = 1,2,3,4,5....
 

有了上面的假设,我们可以以一个环来表示一个规律

当k = 1时,A*k取小数部分大约为https://chart.googleapis.com/chart?cht=tx&chl=5.5%2F8

当k = 2时,A*k取小数部分大概是https://chart.googleapis.com/chart?cht=tx&chl=3%2F8

而乘法杂凑法就是运用这样的想法,让https://chart.googleapis.com/chart?cht=tx&chl=key平均的分布在hash table中每一个槽里。而这也就是为什麽这是一个不错的杂凑方法,既可以保证计算的速度,又可以保证https://chart.googleapis.com/chart?cht=tx&chl=key分布的平均性。

而先前提到的除法杂凑法,与乘法凑法对比在处理二进为资料时,由於取余数的部分还多出了hash table的槽位,和一个bytes的位元数等因子参与,因此增加了随机性,从而使乘法杂凑法成为了更好的策略可以使用。

而上面的范例是以7 bit进行示范,如果我们以64 bit表示,然後增加key的范围,那麽我们可以使最後产生出的杂凑值结果更加的随机,也就是对应到更多的槽位,使得碰撞的机率下降。

前面提到说,这个方法的好坏取决於我们如何选取https://chart.googleapis.com/chart?cht=tx&chl=A,对於某一些https://chart.googleapis.com/chart?cht=tx&chl=A值,这个方法的效果特别的好,根据Knuth认为,如果https://chart.googleapis.com/chart?cht=tx&chl=A趋近於黄金比例常数的共轭根
https://chart.googleapis.com/chart?cht=tx&chl=A%20%5Capprox%20(%5Csqrt%205%20-%201)%20%2F%202%20%3D%200.618....
产生出的结果会较为理想。

参考资料:Introduction to algorithms 3rd


<<:  Day 23 [Python ML、资料视觉化] 直方图、密度图

>>:  Day - 19 分开页面

Day11 远端共同协作 - 使用 GitHub

大家好我是乌木白,今天来向大家介绍GitHub,我自己很喜欢的一个可以做很多功能的网站!! 什麽是...

30天打造品牌特色电商网站 Day.22 图片排版实作

分享完关於图片的排版设计,今天我们就来进行排版的实作啦! 之前有介绍过的<float>&...

Day 22 活泼是为了不让人倦怠

一个人应该:活泼而守纪律,天真而不幼稚,勇敢而不鲁莽,倔强而有原则,热情而不冲动,乐观而不盲目。 《...

D19 - 今晚我想来点 唯独派 getter 唯写派 setter

前言 JavaScript 内的物件都有内建的两个属性,可以实现对物件的存取,称为: getter ...

第4砍 - 蓄势待发

今晚我想来点... 麻而不辣的 linker script [叮咚] 您的外送餐点到瞜, 已经依照您...