如何为大字符串创建一个好的哈希函数?

时间:2011-07-15 16:04:43

标签: java hashtable hashcode

这是Strings的哈希函数

public class GoodHashFunctor implements HashFunctor {

    @Override
    public int hash(String item) {

        String binaryRepString = "";

        for(int i = 0; i < item.length(); i++){
            // Add the String version of the binary version of the integer  version of each character in item
            binaryRepString += Integer.toBinaryString((int)(item.charAt(i)));
        }


        long longVersion = Long.parseLong(binaryRepString, 2) % Integer.MAX_VALUE;


        return (int) longVersion;

    }

}

然而,当我尝试散列大字符串(大约10-15个字符)时,我遇到错误,因为当它试图解析Long时,它会因为数字太大而死亡。

你们认为我应该怎么做?我的教授说我们不能使用Java的hashCode()

我看到一篇类似的帖子,其中最好的答案是这样散列:

int hash=7;

for (int i=0; i < strlen; i++) {
    hash = hash*31+charAt(i);
}

但我不会遇到同样的问题吗?我想这可能需要更长的时间才能以这种新方式打破它。我不知道我很困惑......

2 个答案:

答案 0 :(得分:0)

为什么在将每个字符转换为long之前,需要将每个字符转换为字符串(也是二进制形式)?为什么不只是添加long的{​​{1}}值?

这是家庭作业,所以我不会发布代码。您还可以查看任何好的算法手册或在网上搜索有关散列的更多信息。

编辑: 我知道你不想只是总结它们,因为anagrams都会有相同的哈希值。但我认为你已经知道如何避免这种情况。请注意,通过连接位,您基本上是在将值移位到某个位置后将值添加到值中。即“10101”+“10001”与1010100000 + 10001 - 21 <&lt; 5 + 17相同。

通过将每个字符移动与其在字符串中的位置成比例的量,添加到散列的值取决于字符的值和位置。此外,通过简单地乘以而不是缩放来观察相同的效果。

需要注意的另一件事是char只有64位。在开始溢出之前,你只能将很多long包装进去。因此,大多数实用的散列函数都采用模数值。当然,这意味着对于无限数量的输入字符串,只有有限数量的可能哈希值。碰撞是不可避免的,但为您的班次/乘数和模型选择的值可以最大限度地减少碰撞次数。

答案 1 :(得分:0)

什么是好的哈希函数在很大程度上取决于你的意思。我知道这听起来很陈词滥调但是它是如此真实。要确定哪个哈希函数最适合您的特定问题域,您必须指定:

  • 输入的时间

  • 输入包含的字母(某个字母表中的字母,或者只是遗传序列中的4个字母,如果你想要一个非常好的散列函数,你甚至需要指定每个字母的预期概率)< / p>

  • 您希望区分字符串的方式(您对MAK答案的评论表明您希望哈希值对于相同字符串的排列不同。所以您的+=不是候选者,但请参阅以下链接用于满足此要求的某些功能)

这3个注意事项的组合允许您选择一个好的哈希函数,但首先必须指定这3个点。

作为附注:很明显,你的+=成为一个只适用于短字符串。但即使使用不同的哈希函数,也不会为每个可以放入64位长(Java)的字符串获取唯一的哈希值:即使使用完美哈希,也只能区分2 ^ 64个字符串功能。通常,如果您有一个映射aKey-&gt; anObject的哈希表,您仍然存储原始密钥(而不仅仅是该存储桶所代表的哈希值),因此您可以将其与请求的密钥字符串进行比较。

根据您的要求,您可能需要查看加密哈希函数主题,以确定那些是否符合您的要求。然而,首先看看非常好的维基百科条目,它列出了一些好的哈希函数,更重要的是它们是好的情况: http://en.wikipedia.org/wiki/Hash_function