我对此不是 100% 确定,但这可能是不同作者在不同情况下做出不同假设的产物。
Knuth 在 TAoCP 中的工作是在任何其他书籍或哈希函数被开发之前完成的。当时,Knuth 在某种意义上开创了如何分析和思考不同算法和数据结构的道路。当时,“使用某种方案将东西分配到桶中”的想法是众所周知的,但没有人认真考虑过如何最好地选择该方案。他的方法在数学上简单而优雅,并且在当代(1970 年代)硬件上运行迅速。总的主题是“如果你要根据某个函数进行分配,这里有一个非常好用且简单的函数,并且背后有一个很好的理论。”
Knuth 的第一篇分析数据结构或算法的论文 IIRC 是关于哈希表的。他在哈希码是均匀随机的假设下进行了分析,并表明在这些假设下哈希表表现良好。
不过,正如您所提到的,很明显,如果您选择任何固定的哈希函数,您将得到退化的输入案例。一群人开始思考如何处理这个问题,许多人尝试了从可用哈希函数池中随机选择一个哈希函数的想法。在 1970 年代后期,Wegman 发表了一篇题为 Universal Classes of Hash Functions 的论文,其中概述了一个正式的数学定义,即散列函数家族成为可供选择的一类好的散列函数意味着什么。这篇论文包含了一个证明,证明通用哈希函数族的预期冲突数量很少,这使得它们非常适合链式哈希表。
第一版 CLRS 于 1990 年出版,结合了 Knuth 对线性探测的分析(假设是真正的随机哈希码)和使用通用哈希对链式哈希表的分析。换句话说,它承认你在选择散列函数时必须小心(没有一个固定函数总是有效的,所以看看通用散列),但也会在假设你有一个“足够好的”散列函数的情况下做一些数学运算。
(后来的理论发展包括具有里程碑意义的论文“为什么简单散列函数起作用”,解释了为什么弱散列函数与输入分布中的少量熵相结合本质上就像真正的随机函数一样,后来的一些工作表明 5-您只需要独立的哈希函数就可以在线性探测表中获得非常好的性能。)
上述所有工作都在 Theoryland 进行,其目标是建立良好的数学框架来分析数据结构,并为实践中的方法提出具体建议,以获得良好的分布和效率。
然后是真实世界,从业者并不总能得到数学,而且数学往往落后于从业者所做的事情。
如果您查看大多数关于散列函数的工作,许多散列函数假定您正在处理可以以有意义的方式轻松分解为整数单元的数据。但真实数据并不总是以这种方式很好地分解。或者你可能有一种像 C++、Java、Python 等这样的语言,其中每个对象都有“一个”哈希码,它是与之关联的 哈希码,而不是理论人们推荐的拥有可用的哈希函数。
在这样的情况下,尝试构建一个哈希函数并不是不合理的,它 (1) 评估速度非常快,(2) 可以跨同一程序或多台机器的不同运行,以及 (3)在实践中工作“足够好”,人们不会抱怨。这就是你得到像 MurmurHash 之类的哈希函数的地方——它们非常非常好地满足了这个需求。假设您没有针对对抗性选择的输入工作,那么这些类型的哈希函数就可以了。
有趣的是,我们现在看到了 Knuth 的复兴
乘法哈希函数。允许您将不同哈希函数组合在一起的库,例如 Boost 的 hash_combine,使用该技术在给定多个现有哈希作为输入的情况下提供确定性但广泛传播的哈希码。
总结一下: