把一个较大集合P映射到一个较小集合Q中,其中映射算法位H,即Q=H(P),每一个p对应一个q,一个q可能对应多个p,这就是Hash编码的初步理解。其中散列表,可以认为是一种特殊的数据结构,有|Q|个所谓的槽,存储相应的Q值,其中P中的元素出现,就在Q中相对应的结果中进行记录,对于不同p1,p2对应到同一个槽时,即h(p1)=h(p2)=q1则,q1的槽对应一个list,存储p1、p2的值。一个好的散列函数应该尽可能的将|P|个元素尽可能评价的分为|Q|个槽中。几个较为经典散列算法如下:
除法散列法
其实就是取余,h(p)=p mod m,其中m小于|Q|的值,但不一定是|Q|的值。使用该方法时,m尽量取一个质数,因为如果m取2或者10的幂,相当于p中的最后几位起作用,之前的几位都荒废了,而m取质数时,所有的元素,都起作用,得到结果可能更为分散。
乘法散列法
h(p)=⌊m(pAmod1)⌋
,其中A的取值是约等于
(5√−1)/2
=0.618……,而mod 1 就是取小数部分,距离说明p=123456,而|P|是0-
232
之间的数,而|Q|是0-
214
之间的数,m即为
214
,将A取成与
(5√−1)/2
最为接近的如
s/232
的数,p*s=
76300∗232+17612864
,其中小数部分为
17612864/232
,该数乘以m,即为
17612864/232−14
,然后取整编码为67。
全域散列
如果散列算法确定,对于某一个hash值q可能对应多个p,即集合 Pq , 如果总是使用集合 P_{q}中的p来测试系统,则Hash表起作用的只有q对应的那一个链表,从而起不到作用。而如果设计一个散列函数簇,在运行时,随机选择一个散列函数,这样对应一个p,不同的算法,得到不同的q,因此就对于该系统就无法确定一个集合 Pq ,来攻击系统。但是当系统实际运行时,一单随机选择了散列算法,则就不会改变,一直使用该算法去初始化Hash表,去检索系统。
说白了,就是如果算法固定,就可以得到集合 Pq ;而全域散列的算法中存在随机数,这样会导致,无法得到 Pq ,但是系统运行时,一旦随机选择了散列函数,就一直使用该算法运行。而我们无法知道该系统选择的函数。使用《算法导论》中的例子说明,即为:
ha,b(p)=((a∗p+b)modt)modm
其中,t是一个预先设定的数,其中对于P中的所有元素,都落在[0,t-1]之间,m是编码集合Q的个数, zp 表示集合{0,1,……,t-1}, z∗p 表示集合{1,……,t-1}。a和b是分别属于 zp 和 z∗p 的随机数,这样不同的a,b选择就构成一个函数簇。