(c)散列:散列函数
看清哈希从原理到冲突处理的全部代价,掌握工程取舍依据
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
(c)散列:散列函数
看清哈希从原理到冲突处理的全部代价,掌握工程取舍依据
冲突难免
上页讲散列函数把任意输入压缩到固定长度输出。一个硬事实紧随其后:输出位数有限、输入空间无限,撞车不可避免。这页说清楚冲突为何必然、三类抗性的差别,以及工程上如何与其相处。
抽屉数小于物品数,必有一格装 ≥2 个;冲突是数学必然而非算法失败
何谓优劣
冲突既然避免不了,问题就变成了——什么样的散列函数才算「好」?这就需要一把尺子来衡量。
均匀散=好函数;总把同姓氏塞进同一班=坏函数
整除留余
散列函数要把关键字变成槽号,整除留余是最朴素的一招:key 除以 m 取余。水全在「表长 m 怎么选」里——选错,键里的规律会原样传染到槽位分布上。
编号÷列数取余得列号;列数是合数时,偶数编号全挤到偶数列
以蝉为师
北美的蝉在地下蛰伏13年或17年后集体出洞。这两个数字都是质数——错峰出土能最大程度避开天敌与竞争。散列函数的设计,竟和蝉的生存智慧异曲同工。
蝉用13/17错开出土高峰;散列函数用质数作模数,让键均匀落桶
M+A+D
上页讲到蝉启示的乘法散列、整除留余——这只是冰山一角。几乎所有散列函数的构造都逃不开三类基本运算:M+A+D。掌握这三板斧,就能拆解绝大多数设计。
乘法=抻面改变长宽比;加法=折叠压成多层;除法=切段取份
平方取中
前面我们看了除留余数和乘法哈希,都需要表长。平方取中反其道而行——把 key 平方后直接从结果中间挖一段,完全不依赖表长。但这种『自由』是有代价的:分布可能严重退化。
平方等于把数字『拉长』成位数更多的数,从拉长的数中间切下 r 位
折叠汇总
从「整除留余」一路走到「平方取中」,我们手里攒了四种构造法。它们各自能解决什么问题,又在哪里失灵?这一页折叠汇总。
取件码=关键码,格口=桶位,系统查码开格=散列映射
伪随机数
回想上一页的 MAD:X_{n+1}=(aX_n+c) mod m。这串递推正是最经典的伪随机数生成器 LCG——散列与伪随机本就同源。
手法固定→每次表演抽出同一张牌;观众觉得神奇,魔术师对每步心知肚明
多项式
前面学过的方法都把键当成整数处理,可'姓名'、'学号'、'网址'是字符串,怎么变成数字?多项式给出一条优雅路径:把整串字符当作一个多项式来求值。
1200 与 1024 差 176,量级由首位决定;a₀·xⁿ⁻¹ 贡献远大于末位
Vorldmort
前面讲了除法、乘法、折叠等构造法。Vorldmort 另辟蹊径——它不追求算得快,而是追求「让人看不出规律」。
原料差一点,出来的酒味道天差地别;尝一口推不出配方
DSA@THU
前面我们盘点了整除留余、数字分析、平方取中、折叠汇总、伪随机数、多项式这些构造方法。现在回到根本:散列函数到底是什么?评判它好坏的尺度是什么?它在工程里又有哪几片用武之地?
把书名/作者这种长串缩成一个短码——中图法号直接告诉你去哪个书架,散列号直接告诉你去哪个桶
本节要点
- ✓冲突是必然:鸽巢原理决定 N > M 时必撞
- ✓好散列函数看均匀性与确定性两件事
- ✓整数键三法宝:除留余数、MAD、多项式
- ✓M 选素数且远离 2 的幂,分布才均匀
- ✓字符串用多项式滚动,要防溢出与顺序错位
课后思考
先自己想 30 秒,再对照参考答案——开放题没有标准答案,思路对就行。
参考答案M 若为 2 的幂,h(k) 只看 k 的低几位,高位被整段丢弃,分布严重不均;所以经典教材才反复强调「尽量用素数」。
参考答案不能。需要先把字符串「翻译」成整数——例如按字符编码累加、或做多项式求值——再喂给整数散列函数。
参考答案不绝对。0.618… 的连分数展开各商最小,是「最无理数」,低阶散落少、分布均匀;其他小数要看小数展开是否同样「无规律」。