理解哈希码

哈希函数在实现哈希表时很重要。 我知道在java Object中有它的哈希码,它可能是从弱哈希函数生成的。

以下是一个“补充哈希函数”的片段

static int hash(Object x) { int h = x.hashCode(); h += ~(h <>> 14); h += (h <>> 10); return h; } 

任何人都可以帮助解释哈希算法的基本思想是什么? 生成非重复整数? 如果是这样,这些按位操作如何实现呢?

哈希函数是任何定义良好的过程或数学函数,它将大的,可能大小可变的数据量转换为小数据,通常是可以作为数组索引的单个整数。 哈希函数返回的值称为哈希值,哈希码,哈希值,校验和或简单哈希值。 ( 维基百科 )

使用更多“人类”语言对象散列是基于对象属性的简短紧凑值。 也就是说,如果您有两个以某种方式变化的对象 – 您可以预期它们的哈希值会有所不同。 好的哈希算法为不同的对象产生不同的值。

您通常尝试使用哈希算法将大搜索键转换为小的非负数,这样您就可以在某个表中查找相关记录,并且比M log2 N更快(其中M是“比较”的成本和N是二进制搜索(或树搜索)中典型的“表”中的项目数。

如果你足够幸运拥有一个完美的哈希,你知道你的(已知!)键集的任何元素都将被散列为一个唯一的,不同的值。 完美的哈希主要用于需要查找语言关键字的编译器等。

在现实世界中,你有不完美的哈希值,其中几个键都散列到相同的值。 没关系:你现在只需将密钥与一小组候选匹配(散列到该值的匹配)进行比较,而不是大集(全表)。 小集合传统上称为“桶”。 您使用哈希算法选择存储桶,然后为存储桶本身使用其他可搜索的数据结构。 (如果存储桶中的元素数量已知或安全地预期为非常小,则线性搜索并非不合理。二进制搜索树也是合理的。)

您的示例中的按位运算看起来很像签名分析移位寄存器,它尝试将长的唯一位模式压缩为一个简短但仍然唯一的模式。

基本上,您尝试使用散列函数实现的事情是,在给定要散列的特定项目的情况下,给散列码中的所有位大约50%的机会关闭或打开。 这样,你的哈希表有多少“桶”(或换句话说,为了确定桶号你需要多少底部位)并不重要 – 如果每个位都尽可能随机,然后一个项目将始终分配给一个基本上随机的桶。

现在,在现实生活中,许多人使用不那么好的哈希函数。 它们在某些位中有一些随机性,但不是全部。 例如,想象一下如果你有一个哈希函数,其位6-7有偏差 – 让我们说在一个对象的典型哈希码中,它们有75%的机会被设置。 在这个组成的例子中,如果我们的哈希表有256个桶(即桶号来自哈希码的0-7位),那么我们就丢掉了位8-31中存在的随机性,并且更小桶的一部分将倾向于被填充(即那些数字具有第6和第7位设置的那些)。

补充散列函数基本上试图在较大数量的比特上扩散散列码中的任何随机性。 因此,在我们的假设示例中,想法是来自位8-31的一些随机性将与低位混合,并稀释位6-7的偏差。 它仍然不会是完美的,但比以前更好。

如果您正在生成哈希表,那么在编写哈希函数时您想要了解的主要内容是确保一致性,而不一定要创建完全唯一的值。

例如,如果您有一个大小为10的哈希表,则不希望哈希函数一次又一次地返回哈希值3。 否则,该特定桶将强制搜索时间为O(n)。 你想要一个哈希函数,它会返回,例如:1,9,4,6,8 …并确保你的桶没有比其他桶重得多。

对于您的项目,我建议您使用众所周知的哈希算法,如MD5甚至更好的SHA,并使用您需要的前k位并丢弃其余的。 这些是经过时间考验的function,作为程序员,您可以聪明地使用它们。

该代码试图通过捣碎周围的位来改善散列值的质量。

总体效果是对于给定的x.hashCode(),您希望在整个整数范围内更好地分配哈希值。 如果您从糟糕的哈希码实现开始,然后以这种方式改进哈希码,某些算法的性能将得到改善。

例如,Java中一个简陋的Integer的hashCode()只返回整数值。 虽然这可以用于许多目的,但在某些情况下,您需要更好的哈希代码,因此通过这种函数放置hashCode会显着改善它。

它可以是你想要的任何东西,只要你遵守文档中描述的一般合同 ,用我自己的话来说就是:

  • 如果在对象上调用100(N)次hashCode,则所有时间必须返回相同的值,至少在该程序执行期间(后续程序执行可能会返回另一个)
  • 如果o1.equals(o2)为true,那么o1.hashCode() == o2.hashCode()必须为true
  • 如果o1.equals(o2)为false,那么o1.hashCode() == o2.hashCode()可能为true,但它有帮助。

就是这样。

根据类的性质,hashCode()e可能非常复杂或非常简单。 例如,可能具有数百万个实例的String类需要非常goo hashCode实现,并使用素数来减少冲突的可能性。

如果你的class级有一个连续的数字是有意义的,那也没关系,没有理由你每次都应该复杂化它。