问题:普通hash会导致对于特定的输入,其时间复杂度可以达到O(n)。为了解决这个问题,同快速排序的随机化一样,使用随机化算法来实现,从而可以使得复杂度不会依赖于输入,且性能能达到平均。
1. 全域hash(universal hashing)
全域hash:在执行的开始,随机地从一族hash函数中选择一个函数来执行hash。从而避免对特定输入导致的最差性能。
H为有限的一组散列函数,将关键字域U映射为{0,1,…,m-1}。这样的个函数组称为全域的。
如果对任何一对不同的关键字k1,k2,H中使得h(k1)=h(k2)的函数的个数最多为|H|/m。
即,随机的从H中选择一个函数h,当关键字k1与k2不等时,发生冲突的概率不大于1/m。
这也是从集合{0,1,….,m-1}中随机、独立的选择时h(k1),和h(k2)发生冲突的概率。
如果h为H中的一个函数,h将n个关键字映射到一个m个槽且用链接法表示的hash表中。如果关键字k不在表中,则k被散列到的链表的长度期望至多为a(装载因子);如果k在表中,则k被散列到的链表的长度期望至多为1+a。
1.1. 全域hash的一种设计
设m为质数。将k表示为m进制的数k<k0,k1,k2,…,kr>,最大关键字为r+1位的m进制数。随机的选择一个a也表示为r位的m进制数a<a0,a1,a2,…,ar>。每一个ai都是随机选择的。由m进制的表示可知:ki,ai为0,m-1内的数。r是由最大关键字来决定的,m为hash表的槽数。
定义Ha(k) = (k0*a0+k1*a1+…+kr*ar)mode m,从而对所有的a,H中函数的个数为,m^(r+1)。
H为一族全域hash。
证明的思路:任取两个不同的键x,y;则其m进制表示中,至少有一位不同。如果碰撞,即h(x)=h(y)(mod m),由m为质数,因此这一位可以由其他的r位决定。从而使x,y碰撞的Ha的函数为m^r个(即|H|/m),因此为全域的。假设地0位为不同的,x0不等于y0,证明中并不要求对应的a0为0,因为x0-y0不为0,因此可以取倒数。从而a0可以由a1,…,ar决定。
1.2. 全域hash的另一种设计
选择足够大的质数p,使得p比任何k都大。Hash表的槽数m小于p。定义Zp为{0,1,…,p-1};Zp*为{1,..,p-1}。
对任何的a属于Zp*,b属于Zp,定义hash函数为
Ha,b(k)=((a*k+b) modp) mod m
所有的这些函数Hp,m将Zp映射到Zm。m不再要求为质数,可以为任意数。
H中函数的个数为p*(p-1),由a,b的选择决定。
H为一族全域hash。
证明的思路:对不同的关键字k,l由k,l不同,则mod p阶段的值也不同。不会有冲突。
r=(a*k+b) mod p
s=(a*l+b) mod p
Hash函数的取值对(a,b)有p*(p-1)种方式。对于每一对(a,b),都会产生不同的结果对(r,s),且r不等于s,因为给定r,s后,可以反解a,b。且(r,s),r不等于s的对的个数也为p*(p-1),因此(a,b)对于(r,s)对之间有1-1映射。
从而,r,s为随机选择的对时,不同k,l碰撞时r=s mod m。而对于给定的r,s可能取值为剩下的p-1种。其中满足s不等于r,且s=r mod m的s个数最多为:
[p/m] -1 <=((p+m-1)/m) -1 = (p-1)/m,从而在mod m的情况下,r与s冲突的概率小于
((p-1)/m)/(p-1)=1/m。
从而k与l冲突的概率<=1/m。从而H为全域的。
2. 完全hash。(perfect hashing)
Hash的好处在于有常数的期望性能。
如果关键字的集合为静态的时,hash可以得到最差情况下的常数性能。
2.1. 静态是指:关键字集合一旦确定,则不再变化,不再有插入或删除。比如程序语言的关键字。
2.2. 完全hash:一种hash方法,在查找(不再有插入、删除)时,最坏的时间性能为O(1)的。槽数m与元素个数n同阶,即m=O(n)。
2.3. 方法:
使用二级散列,每一级都使用全域hash。
第一层和普通hash链类似,不同在于,不是对每个槽位冲突的键建立链表,而是对每个槽位冲突的键建立另一个hash表(二级hash表),而第二层上的hash函数是经过挑选的,因此不会再有冲突。
第一层中可能会冲突,但是第二层后则不会有冲突。
第一层中,如果第i个槽有ni个键冲突,则第二层的hash表对应的链长mi=ni^2。
第二层会非常稀疏。
虽然第二层的大小为冲突数的平方级别,但是因为第一层为全域hash,因此总的存储空间也是O(n)级别的。
证明使用的引理:
Ø 全域hash中随机选择的hash函数,将n个关键字存储到槽数为n^2的hash表中,碰撞的概率小于1/2。
一对特定的键冲突的概率为1/m=1/(n^2),键冲突的组合有C(n,2)种情况(组合数),因此有键冲突的期望次数为C(n,2)/(n^2),小于1/2。
使用马尔可夫不等式即可得有键冲突的概率小于1/2。
从而对于第二层的hash函数,可以从全域hash中尝试选择一个特定的hash函数进行测试,使得不会有冲突发生。因为大部分都不会冲突,因此这种尝试不会太多。
当然如果只用一层hash来做实现不碰撞也可以,不过O(n^2)的hash表则比较大,浪费空间。所以才用二级散列的方法,在第二层使用这个技术。
Ø 空间为O(n)
第一层的槽数m=n。
对第二层的槽数使用期望(类似桶排序)。
第二层的期望空间为2*n.
利用马尔可夫不等式还可得,第二层空间超过4*n的概率小于1/2。
2.4. 应用:
已知最常见的1000个词集合。任意给一个词,查询这个词是否为常见词。
K全域hash与安全认证。