字符串的哈希函数

Posted

技术标签:

【中文标题】字符串的哈希函数【英文标题】:Hash function for a string 【发布时间】:2012-01-09 04:34:59 【问题描述】:

我们目前正在课堂上处理哈希函数。我们的讲师要求我们在互联网上使用一个哈希函数来与我们在代码中使用的两个函数进行比较。

第一个:

int HashTable::hash (string word)   
// POST: the index of entry is returned
       int sum = 0;
        for (int k = 0; k < word.length(); k++)
            sum = sum + int(word[k]);
        return  sum % SIZE; 

第二:

int HashTable::hash (string word)

   int seed = 131; 
   unsigned long hash = 0;
   for(int i = 0; i < word.length(); i++)
   
      hash = (hash * seed) + word[i];
   
   return hash % SIZE;

其中 SIZE 为 501(哈希表的大小),输入来自 20,000 多个单词的文本文件。

我看到this 的问题有几个代码示例,但不确定要在哈希函数中寻找什么。如果我理解正确,在我的情况下,哈希接受一个输入(字符串)并进行数学计算,为字符串分配一个数字并将其插入表中。这样做是为了提高搜索列表的速度吗?

如果我的逻辑是合理的,是否有人有一个很好的例子或资源显示涉及字符串的不同哈希函数?甚至是我自己编写高效哈希函数的过程。

【问题讨论】:

您刚刚为您的问题提供了 2 个答案。 你的导师没有教你任何关于哈希表/函数的知识,他怎么能要求你分析两个哈希函数? "有人有好的例子或资源吗?" Yes. 另见softwareengineering.stackexchange.com/questions/49550/… 【参考方案1】:

首先,在实践中通常没有那么重要。大多数哈希函数都“足够好”。

但如果你真的在乎,你应该知道它本身就是一个研究课题。关于这方面的论文有数千篇。通过学习和设计哈希算法,您今天仍然可以获得博士学位。

您的第二个哈希函数可能会稍微好一些,因为它可能应该将字符串 "ab" 与字符串 "ba" 分开。另一方面,它可能不如第一个哈希函数快。它可能与您的应用相关,也可能不相关。

我猜想用于基因组字符串的散列函数与用于在电话数据库中散列姓氏的散列函数完全不同。甚至一些字符串哈希函数可能更适合德语,而不是英语或法语单词。

许多软件库都为您提供了足够好的哈希函数,例如Qt有qhash,C++11有std::hash在&lt;functional&gt;,Glib有几个hash functions在C,POCO有一些hash函数。

我经常使用涉及素数(参见Bézout's identity)和异或的散列函数,例如

#define A 54059 /* a prime */
#define B 76963 /* another prime */
#define C 86969 /* yet another prime */
#define FIRSTH 37 /* also prime */
unsigned hash_str(const char* s)

   unsigned h = FIRSTH;
   while (*s) 
     h = (h * A) ^ (s[0] * B);
     s++;
   
   return h; // or return h % C;

但我并不声称自己是哈希专家。当然,ABCFIRSTH 的值最好是素数,但你也可以选择其他素数。

查看一些 MD5 实现,以了解散列函数可以是什么。

大多数关于算法的好书至少有一整章专门介绍哈希。从hash function 和hash table 上的维基页面开始。

【讨论】:

非常好的答案。 +1 ... :)【参考方案2】:

-- 这些天要走的路--

使用SipHash。为了您自己的保护。

-- 古老而危险的--

unsigned int RSHash(const std::string& str)

    unsigned int b    = 378551;
    unsigned int a    = 63689;
    unsigned int hash = 0;

    for(std::size_t i = 0; i < str.length(); i++)
    
        hash = hash * a + str[i];
        a    = a * b;
    

    return (hash & 0x7FFFFFFF);
 

 unsigned int JSHash(const std::string& str)
 
      unsigned int hash = 1315423911;

      for(std::size_t i = 0; i < str.length(); i++)
      
          hash ^= ((hash << 5) + str[i] + (hash >> 2));
      

      return (hash & 0x7FFFFFFF);
 

向谷歌询问“通用哈希函数”

【讨论】:

【参考方案3】:

用于算法的散列函数通常有两个目标,首先它们必须快速,其次它们必须将值均匀分布在可能的数字上。哈希函数还需要为相同的输入值提供相同的数字。

如果您的值是字符串,以下是一些不良哈希函数的示例:

    string[0] - ASCII 字符 a-Z 比其他字符更常见 string.lengh() - 最可能的值是 1

良好的哈希函数会尝试使用输入的每一位,同时保持计算时间最短。如果您只需要一些哈希码,请尝试将字节与质数相乘,然后将它们相加。

【讨论】:

【参考方案4】:

使用boost::hash

#include <boost\functional\hash.hpp>

...

std::string a = "ABCDE";
size_t b = boost::hash_value(a);

【讨论】:

在 Linux 上,#include 指令中的反斜杠不太可能起作用,因此您的代码可能是特定于 Windows 的(或者您应该将反斜杠更改为斜杠) 这是一个关于哈希概念的学术问题,所以没有用。【参考方案5】:

Java 的String implements hashCode like this:

public int hashCode()

Returns a hash code for this string. The hash code for a String object is computed as

     s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.) 

所以是这样的:

int HashTable::hash (string word) 
    int result = 0;
    for(size_t i = 0; i < word.length(); ++i) 
        result += word[i] * pow(31, i);
    
    return result;

【讨论】:

我认为 java 使用 clevel 移位来计算该值,而不是直接计算表达式。 31 = 32 - 1,所以 31^k = (32 - 1)^k = (-1)^k + 2*32*(-1)^(k-1) ... 32^k;因为 32 = 2^5, 32^7 > sizeof(int),所以你只需要计算总和的前 6 个,甚至可以通过移位来完成。它比使用 pow() 更快,所以除非你愿意优化一些计算,否则不要这样做。【参考方案6】:

C++ 已经为std::string 实现了哈希:

std::hash<:string>

#include <iostream> // not actually required for the hash
#include <string>

auto main() ->int

    const std::string input = "Hello World!";
    const std::hash<std::string> hasher;
    const auto hashResult = hasher(input);
    
    std::cout << "Hash for the input is: " << hashResult << std::endl;

在此处运行此代码:https://onlinegdb.com/33KLb91ku

【讨论】:

以上是关于字符串的哈希函数的主要内容,如果未能解决你的问题,请参考以下文章

字符串的哈希函数

128 哈希函数

构造哈希表/哈希函数

lintcode 128哈希函数

哈希碰撞——密码哈希函数

哈希函数和组合多个键