我在采访中遇到过需要对整数或字符串使用哈希函数的情况。在这种情况下我们应该选择哪一种?我在这些情况下错了,因为我最终选择了产生大量碰撞的那些,但是哈希函数往往是数学的,你不能在面试中回忆起它们。是否有任何一般性建议,以至于面试官对整数或字符串输入的方法感到满意?在“面试情境”中,哪些功能对于两种输入都是足够的
答案 0 :(得分:8)
以下是来自Effective java page 33的简单食谱:
答案 1 :(得分:5)
你应该问面试官哈希函数的用途 - 这个问题的答案将决定哪种哈希函数是合适的。
如果它用于散列数据结构之类的哈希映射,您希望它尽可能简单(快速执行)并避免冲突(最常见的值映射到不同的哈希值) )。一个很好的例子是对同一个整数进行整数散列 - 这是java.lang.Integer中的标准hashCode()实现
如果是出于安全目的,您需要使用cryptographic hash function。这些主要是为了很难反转哈希函数或发现冲突。
如果您想要快速伪随机哈希值(例如,用于模拟),那么您通常可以修改伪随机数生成器来创建这些值。我个人最喜欢的是:
public static final int hash(int a) { a ^= (a << 13); a ^= (a >>> 17); a ^= (a << 5); return a; }
如果要为某种形式的复合结构计算哈希值(例如,包含多个字符的字符串,或数组,或具有多个字段的对象),则可以使用各种技术来创建组合哈希函数。我建议对组成部分的旋转散列值进行异或,例如:
public static <T> int hashCode(T[] data) {
int result=0;
for(int i=0; i<data.length; i++) {
result^=data[i].hashCode();
result=Integer.rotateRight(result, 1);
}
return result;
}
请注意,上述内容不具有加密安全性,但可用于大多数其他目的。您显然会遇到冲突,但是当将大型结构散列为整数时,这是不可避免的: - )
答案 2 :(得分:2)
对于整数,我通常使用k%p,其中p =哈希表的大小并且是素数,对于字符串,我从String类中选择哈希码。这足以接受一家大型科技公司的采访吗? - 凤凰2天前
也许不是。需要向哈希表提供哈希函数并不罕见,哈希表的实现对您来说是未知的。此外,如果您以一种依赖于使用素数桶的实现的方式进行散列,那么如果实现由于新的库,编译器,OS端口等而发生更改,则性能可能会降低。
就我个人而言,我认为在访谈中重要的是清楚地理解通用哈希算法的理想特征,这基本上是对于任何两个输入键,其值的变化小到一位,每一个输出中的位有大约50/50的翻转几率。我发现这很反直觉,因为我第一次看到的很多散列函数都使用了位移和XOR,而翻转的输入位通常会翻转一个输出位(通常在另一个位位置,因此1输入位影响很多) -output-bits是一个小小的启示时刻,当我在Knuth的一本书中读到它时。凭借这些知识,你至少能够测试和评估具体的实现,而不管它们是如何实现的。
我会提到一种方法,因为它实现了这种理想并且易于记忆,尽管内存使用可能比数学方法慢(也可能更快,取决于硬件),只需使用输入中的每个字节查找随机整数表。例如,给定24位RGB值和int table[3][256]
,table[0][r] ^ table[1][g] ^ table[2][b]
是一个很好的sizeof int
哈希值 - 如果输入通过int
值随机分散,则确实“完美” (而不是说递增 - 见下文)。这种方法对于长或任意长度的密钥并不理想,但您可以开始重新访问表并对值进行位移等。
所有这些说明,对于您了解输入键中的模式和/或所涉及的桶数(例如,您)的特定情况,您可以有时比这种随机方法做得更好可能知道输入键从1到100是连续的,并且有128个桶,因此您可以在没有任何冲突的情况下传递密钥。但是,如果输入不再符合您的期望,那么您可能会遇到可怕的碰撞问题,而“随机化”方法永远不会比负载(size()/ buckets)暗示的更糟糕。另一个有趣的见解是,当你想要一个快速和平庸的哈希时,你不必在生成哈希时包含所有输入数据:例如上次我看了Visual C ++的字符串哈希代码时,它选择了沿文本均匀分布的十个字母作为输入....