常用的哈希函数

本文介绍了八种经典的字符串哈希算法,包括RS、JS、PJW、ELF、BKDR、SDBM、DJB和AP等算法,并提供了详细的Java实现代码。这些算法在散列性能和分布均匀性方面各有特色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



通用的哈希函数库有下面这些混合了加法和一位操作的字符串哈希算法。下面的这些算法在用法和功能方面各有不同,但是都可以作为学习哈希算法的实现的例子。(其他版本代码实现见下载

1.RS 
从Robert  Sedgwicks的  Algorithms in C一书中得到了。 原文作者已经 添加了一些 简单的优化 算法,以 加快 散列 过程。
[java]  view plain copy
  1. public long RSHash(String str)  
  2.    {  
  3.       int b     = 378551;  
  4.       int a     = 63689;  
  5.       long hash = 0;  
  6.       for(int i = 0; i < str.length(); i++)  
  7.       {  
  8.          hash = hash * a + str.charAt(i);  
  9.          a    = a * b;  
  10.       }  
  11.       return hash;  
  12.    }  
 
2.JS
Justin Sobel写的一个位操作的哈希函数。
[c-sharp]  view plain copy
  1. public long JSHash(String str)  
  2.    {  
  3.       long hash = 1315423911;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash ^= ((hash << 5) + str.charAt(i) + (hash >> 2));  
  7.       }  
  8.       return hash;  
  9.    }  
 
3.PJW 
该散列 算法是基于贝尔实验室的 彼得J 温伯格 的的研究 。在Compilers一书中 (原则, 技术 和工具) 建议 采用这个算法的 散列 函数 的哈希 方法。
[java]  view plain copy
  1. public long PJWHash(String str)  
  2.    {  
  3.       long BitsInUnsignedInt = (long)(4 * 8);  
  4.       long ThreeQuarters     = (long)((BitsInUnsignedInt  * 3) / 4);  
  5.       long OneEighth         = (long)(BitsInUnsignedInt / 8);  
  6.       long HighBits          = (long)(0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);  
  7.       long hash              = 0;  
  8.       long test              = 0;  
  9.       for(int i = 0; i < str.length(); i++)  
  10.       {  
  11.          hash = (hash << OneEighth) + str.charAt(i);  
  12.          if((test = hash & HighBits)  != 0)  
  13.          {  
  14.             hash = (( hash ^ (test >> ThreeQuarters)) & (~HighBits));  
  15.          }  
  16.       }  
  17.       return hash;  
  18.    }  
 
4.ELF 
和PJW很相似,在Unix系统中使用的较多。
[java]  view plain copy
  1. public long ELFHash(String str)  
  2.    {  
  3.       long hash = 0;  
  4.       long x    = 0;  
  5.       for(int i = 0; i < str.length(); i++)  
  6.       {  
  7.          hash = (hash << 4) + str.charAt(i);  
  8.          if((x = hash & 0xF0000000L) != 0)  
  9.          {  
  10.             hash ^= (x >> 24);  
  11.          }  
  12.          hash &= ~x;  
  13.       }  
  14.       return hash;  
  15.    }  
 
5.BKDR
这个算法来自Brian Kernighan 和 Dennis Ritchie的 The C Programming Language。这是一个很简单的哈希算法,使用了一系列奇怪的数字,形式如31,3131,31...31,看上去和DJB算法很相似。(参照我之前一篇博客,这个就是Java的字符串哈希函数)
[java]  view plain copy
  1. public long BKDRHash(String str)  
  2.    {  
  3.       long seed = 131// 31 131 1313 13131 131313 etc..  
  4.       long hash = 0;  
  5.       for(int i = 0; i < str.length(); i++)  
  6.       {  
  7.          hash = (hash * seed) + str.charAt(i);  
  8.       }  
  9.       return hash;  
  10.    }  
 
6.SDBM
这个算法在开源的SDBM中使用,似乎对很多不同类型的数据都能得到不错的分布。
[java]  view plain copy
  1. public long SDBMHash(String str)  
  2.    {  
  3.       long hash = 0;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash = str.charAt(i) + (hash << 6) + (hash << 16) - hash;  
  7.       }  
  8.       return hash;  
  9.    }  
 
7.DJB
这个算法是Daniel J.Bernstein 教授发明的,是目前公布的最有效的哈希函数。
[java]  view plain copy
  1. public long DJBHash(String str)  
  2.    {  
  3.       long hash = 5381;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash = ((hash << 5) + hash) + str.charAt(i);  
  7.       }  
  8.       return hash;  
  9.    }  
 
8.DEK
由伟大的Knuth在《编程的艺术 第三卷》的第六章排序和搜索中给出。
[java]  view plain copy
  1. public long DEKHash(String str)  
  2.    {  
  3.       long hash = str.length();  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash = ((hash << 5) ^ (hash >> 27)) ^ str.charAt(i);  
  7.       }  
  8.       return hash;  
  9.    }  
 
9.AP
这是本文作者Arash Partow贡献的一个哈希函数,继承了上面以旋转以为和加操作。代数描述: AP

[java]  view plain copy
  1. public long APHash(String str)  
  2.    {  
  3.       long hash = 0xAAAAAAAA;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          if ((i & 1) == 0)  
  7.          {  
  8.             hash ^= ((hash << 7) ^ str.charAt(i) * (hash >> 3));  
  9.          }  
  10.          else  
  11.          {  
  12.             hash ^= (~((hash << 11) + str.charAt(i) ^ (hash >> 5)));  
  13.          }  
  14.       }  
  15.       return hash;  
  16.    }  
 
### 常用哈希函数及其在 `unordered_set` 模板中的使用和实现 #### 1. 哈希函数简介 哈希函数是一种将任意长度的数据映射为固定长度值的算法。对于 C++ 中的 `unordered_set` 和其他基于哈希表的容器来说,良好的哈希函数能够显著提高其性能[^1]。 C++ 标准库提供了默认的通用哈希函数模板 `std::hash<T>`,适用于大多数内置类型(如整数、浮点数、字符串等)。然而,当键类型是非标准类型时,可能需要用户提供自定义的哈希函数。 --- #### 2. 默认哈希函数的应用 对于常见的内置类型,可以直接利用 `std::hash` 提供的功能。例如: ```cpp #include <iostream> #include <unordered_set> #include <string> int main() { std::unordered_set<int> intSet; intSet.insert(42); std::unordered_set<std::string> stringSet; stringSet.insert("hello"); if (intSet.find(42) != intSet.end()) { std::cout << "Integer found." << std::endl; } if (stringSet.find("hello") != stringSet.end()) { std::cout << "String found." << std::endl; } } ``` 上述代码展示了如何使用默认的 `std::hash` 处理基本数据类型和字符串类型的键值[^2]。 --- #### 3. 自定义哈希函数的实现 当键类型是用户定义的结构体或其他复杂类型时,需提供自定义的哈希函数。以下是一个完整的例子: ##### 示例:为自定义结构体定义哈希函数 假设有一个表示二维坐标的结构体 `Point`,我们需要为其创建一个哈希函数以便将其作为 `unordered_set` 的键。 ```cpp #include <iostream> #include <unordered_set> #include <functional> struct Point { int x, y; }; // 自定义哈希函数 namespace std { template <> struct hash<Point> { size_t operator()(const Point& p) const { // 结合 x 和 y 的哈希值生成唯一的哈希码 return hash<int>()(p.x) ^ (hash<int>()(p.y) << 1); } }; } int main() { std::unordered_set<Point> pointSet; pointSet.emplace(Point{1, 2}); pointSet.emplace(Point{3, 4}); if (pointSet.find(Point{1, 2}) != pointSet.end()) { std::cout << "Point (1, 2) exists in the set." << std::endl; } } ``` 在此示例中,我们通过特化 `std::hash` 来支持 `Point` 类型的哈希计算[^4]。 --- #### 4. 特殊情况处理 有些情况下,键类型无法直接转换为整数值,或者需要更复杂的逻辑来生成唯一标识符。例如: - **字符串类型**:虽然 `std::hash<std::string>` 已经存在,但如果希望优化性能或满足特定需求,可以编写自己的哈希函数。 - **指针类型**:由于指针本身已经是地址形式,因此可简单地返回其值作为哈希码。 ##### 示例:为字符串类型提供简化版哈希函数 ```cpp #include <iostream> #include <unordered_set> #include <string> struct CustomHash { size_t operator()(const std::string& str) const { return static_cast<size_t>(str[0]); // 取第一个字符作为哈希值 } }; int main() { std::unordered_set<std::string, CustomHash> customHashSet; customHashSet.insert("apple"); customHashSet.insert("banana"); if (customHashSet.find("apple") != customHashSet.end()) { std::cout << "Custom hash function works for 'apple'." << std::endl; } } ``` 注意,这种方法可能导致较高的碰撞率,仅适合简单的应用场景[^4]。 --- #### 5. 性能考量与调试工具 为了评估哈希表的实际表现,可以借助一些成员函数进行分析。例如: - `bucket_count()` 返回当前桶的数量; - `bucket_size(n)` 获取第 n 个桶的有效元素数量; - `bucket(key)` 查找某个键所属的具体桶编号。 这些工具可以帮助开发者诊断潜在问题并调整哈希策略[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值