❖加密形式保存密码
❖仅保存密码的散列值,用户输入密码后,计算散列值并比对;
❖无需保存密码的明文即可判断用户是否输入了正确的密码。
(通过值计算散列值很简单,但反过来就不可以)
- 散列函数设计:折叠法
❖ 折叠法设计散列函数的基本步骤是
将数据项按照位数分为若干段,
再将几段数字相加,
最后对散列表大小求余,得到散列值
❖ 例如,对电话号码62767255
可以两位两位分为4段(62、 76、 72、 55)
相加(62+76+72+55=265)
散列表包括11个槽,那么就是265%11=1
所以h(62767255)=1
❖ 有时候折叠法还会包括一个隔数反转的步骤
比如(62、 76、 72、 55)隔数反转为(62、 67、 72、 55)
再累加(62+67+72+55=256)
对11求余(256%11=3),所以h’(62767255)=3
❖ 虽然隔数反转从理论上看来毫无必要,但这个步骤确实为折叠法得到散列函数提供了一种微调手段,以便更好符合散列特性
- 散列函数设计:平方取中法
❖ 平方取中法,首先将数据项做平方运算,然后取平方数的中间两位,再对散列表的大小求余
❖ 例如,对44进行散列
首先44*44=1936
然后取中间的93
对散列表大小11求余, 93%11=5
- 散列函数设计:非数项
“”"
❖ 我们也可以对非数字的数据项进行散列,把字符串中的每个字符看作ASCII码即可
如cat, ord(‘c’)==99, ord(‘a’)==96,ord(‘t’)==116
❖ 再将这些整数累加,对散列表大小求余
“”"
def hash(astring,tablesize):
sum = 0
for pos in range(len(astring)):
sum = sum + ord(astring[pos])
return sum%tablesize
ord函数可以将字符转化为你所需要的ASCII码
当然,这样的散列函数对所有的异序词都返回相同的散列值
为了防止这一点,可以将字符串所在的位置作为权重因子,乘以ord值
- 散列函数设计
❖ 我们还可以设计出更多的散列函数方法,但要坚持的一个基本出发点是,散列函数不能成为存储过程和搜索过程的计算负担
❖ 如果散列函数设计太过复杂,去花费大量的计算资源计算槽号,失去了散列本身的意义
可能还不如简单地进行顺序搜索或者二分搜索
- 冲突解决方案
❖ 如果两个数据项被散列映射到同一个槽,需要一个系统化的方法在散列表中保存第二个数据项,这个过程称为“解决冲突”
❖ 前面提到,如果说散列函数是完美的,那就不会有散列冲突,但完美散列函数常常是不现实的
❖ 解决散列冲突成为散列方法中很重要的一部分。
❖ 解决散列的一种方法就是为冲突的数据项再找一个开放的空槽来保存
最简单的就是从冲突的槽开始往后扫描,直到碰到一个空槽
如果到散列表尾部还未找到,则从首部接着扫描
❖ 这种寻找空槽的技术称为“ 开放定址open addressing”
❖向后逐个槽寻找的方法则是开放定址技术中的“线性探测linear probing”
- 冲突解决方案:线性探测Linear Probing
我们把44、 55、 20逐个插入到散列表中
h(44)=0,但发现0#槽已被77占据,向后找到第一个空槽1#,保存
h(55)=0,同样0#槽已经被占据,向后找到第一个空槽2#,保存
h(20)=9,发现9#槽已经被31占据了,向后,再从头开始找到3#槽保存
采用线性探测方法来解决散列冲突的话,则散列表的搜索也遵循同样的规则
如果在散列位置没有找到搜索项的话,就必须向后做顺序搜索
直到找到搜索项,或者碰到空槽(搜索失败)。
- 冲突解决方案:线性探测的改进
❖ 线性探测法的一个缺点是有聚集(clustering)的趋势
❖ 即如果同一个槽冲突的数据项较多的话,这些数据项就会在槽附近聚集起来
❖ 从而连锁式影响其它数据项的插入
❖ 避免聚集的一种方法就是将线性探测扩展,从逐个探测改为跳跃式探测
下图是“ +3” 探测插入44、 55、 20
- 冲突解决方案:再散列rehashing
❖ 重新寻找空槽的过程可以用一个更为通用的“再散列rehashing” 来概括
newhashvalue = rehash(oldhashvalue)
对于线性探测来说, rehash(pos)= (pos+ 1)%sizeoftable
“ +3” 的跳跃式探测则是: rehash(pos)=(pos+ 3)% sizeoftable
跳跃式探测的再散列通式是: rehash(pos)=(pos+skip)% sizeoftable
❖ 跳跃式探测中,需要注意的是skip的取值,不能被散列表大小整除,否则会产生周期,造成很多空槽永远无法探测到
一个技巧是,把散列表的大小设为素数,如例子的11
❖ 还可以将线性探测变为“ 二次探测 quadratic probing”
❖ 不再固定skip的值,而是逐步增加skip值,如1、 3、 5、 7、 9
❖ 这样槽号就会是原散列值以平方数增加:h, h+1, h+4, h+9, h+16…