Hash函数及其应用

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Hash 函数的常用算法
1.Hash 介绍
Hash 这个在实现某些功能的经常会用到的数据结构,在 java 和 c++ 里面都有相 应的封装好的数据结构:C++ STL Map java 有 HashMap TreeMap。
计算理论中,没有 Hash 函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂 的定义,大家可以去看计算理论或者密码学方面的数据。用“人 类”的语言描述单向函数 就是:如果某个函数在给定输入的时候,很容易计算出其结果来;而当给定结果的时候,很 难计算出输入来,这就是单项函数。各种加密函数都可以被认为是单向函数的逼近。Hash 函数(或者成为散列函数)也可以看成是单向函数的一个逼近。即它接近于满足单向函数的 定义。
final int p = 16777619; int hash = (int)2166136261L; for(int i=0;i
hash = (hash ^ data.charAt(i)) * p; hash += hash << 13; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; return hash; } 复制代码 除了乘以一个固定的数,常见的还有乘以一个不断改变的数,比如:
直接定址法:地址集合 和 关键字集合大小相同
数字分析法:根据需要 hash 的关键字的特点选择合适 hash 算法,尽量寻找每个关键字的不 同点
ຫໍສະໝຸດ Baidu
平方取中法:取关键字平方之后的中间极为作为哈希地址,一个数平方之后中间几位数字与 数的每一位都相关,取得位数由表长决定。比如:表长为 512,=2^9,可以取平方之后中间 9 位二进制数作为哈希地址。 折叠法:关键字位数很多,而且关键字中每一位上的数字分布大致均匀的时候,可以采用折 叠法得到哈希地址, 除留取余法除 P 取余,可以选 P 为质数,或者不含有小于 20 的质因子的合数 随机数法:通常关键字不等的时候采用此法构造哈希函数较恰当。 实际工作中需要视不同的情况采用不同的 hash 函数: 考虑因素:计算哈希函数所需要的时间,硬件指令等因素。 关键字长度 哈希表大小 关键字分布情况 记录查找的频率。(huffeman 树) 处理冲突的方法: 开放地址法:现行探测再散列只要哈希表为填满,总能找到一个不冲突的地址,二次探测再 散列表长为素数时才可能保证总能找到一个不冲突的地址,随机探测再散列取决于伪随机数 列 再哈希法:不易发生聚集,但是增加了计算的时间 链地址法;Chord 协议中,一致性 hash 有应用。
一 加法 Hash
所谓的加法 Hash 就是把输入元素一个一个的加起来构成最后的结果。标准的加法 Hash 的构 造如下: static int additiveHash(String key, int prime) {
int hash, i; for (hash = key.length(), i = 0; i < key.length(); i++)
}
return (hash & 0x7FFFFFFF);
}
复制代码
虽然 Adler32 算法的应用没有 CRC32 广泛,不过,它可能是乘法 Hash 里面最有名的一个了。 关于它的介绍,大家可以去看 RFC 1950 规范。
四 除法 Hash
除法和乘法一样,同样具有表面上看起来的不相关性。不过,因为除法太慢,这种方式几乎 找不到真正的应用。需要注意的是,我们在前面看到的 hash 的 结果除以一个 prime 的目的 只是为了保证结果的范围。如果你不需要它限制一个范围的话,可以使用如下的代码替 代”hash%prime”: hash = hash ^ (hash>>10) ^ (hash>>20)。
应用于加密的 Hash 函数已经探讨过太多了,在作者的博客里面有更详细的介绍。所以,本 文只探讨用于查找的 Hash 函数。 Hash 函数应用的主要对象是数组(比如,字符串),而其目标一般是一个 int 类型。以下 我们都按照这种方式来说明。
2. 常用的 Hash 算法
有时候 hash 函数是一个压缩映像,因此不可避免会发生冲突,因此在建造 hash’函数的时 候不仅要设定一个好的 hash 函数,还要设定一种处理冲突的方法,哈希造表,散列表。
public int FNVHash(byte[] data) {
int hash = (int)2166136261L; for(byte b : data)
hash = (hash * 16777619) ^ b;
if (M_SHIFT == 0) return hash;
return (hash ^ (hash >> M_SHIFT)) & M_MASK; } 复制代码 以及改进的 FNV 算法: public static int FNVHash1(String data) {
Hash 函数还有另外的含义。实际中的 Hash 函数是指把一个大范围映射到一个小范围。把大 范围映射到一个小范围的目的往往是为了节省空间,使得数据容易保存。除此以外,Hash 函数往往应用于查找上。所以,在考虑使用 Hash 函数之前,需要明白它的几个限制:
1. Hash 的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范 围相当或者比它更小。不然冲突就会很多。 2. 由于 Hash 逼近单向函数;所以,你可以用它来对数据进行加密。 3. 不同的应用对 Hash 函数有着不同的要求;比如,用于加密的 Hash 函数主要考虑它和单 项函数的差距,而用于查找的 Hash 函数主要考虑它映射到小范围的冲突率。
} else
{ hash ^= ~((hash<<11>>5));
} hash += (hash<<5> hash = key.charAt(i) + (hash<<6>>16) ? hash; hash ^= ((hash<<5>>2));
复制代码 三 乘法 Hash
这种类型的 Hash 函数利用了乘法的不相关性(乘法的这种性质,最有名的莫过于平方取头 尾的随机数生成算法,虽然这种算法效果并不好)。比如, static int bernstein(String key) {
int hash = 0; int i; for (i=0; i return hash; } 复制代码 jdk5.0 里面的 String 类的 hashCode()方法也使用乘法 Hash。不过,它使用的乘数是 31。 推荐的乘数还有:131, 1313, 13131, 131313 等等。 使用这种方式的著名 Hash 函数还有: // 32 位 FNV 算法 int M_SHIFT = 0;
int hash, i;
for (hash=key.length(), i=0; i hash = (hash<<4>>28)^key.charAt(i);
return (hash % prime); } 复制代码 先移位,然后再进行各种位运算是这种类型 Hash 函数的主要特点。比如,以上的那段计算 hash 的代码还可以有如下几种变形: hash = (hash<<5>>27)^key.charAt(i); hash += key.charAt(i); hash += (hash << 10); hash ^= (hash >> 6); if((i&1) == 0) { hash ^= (hash<<7>>3);
hash += key.charAt(i); return (hash % prime); } 复制代码 这里的 prime 是任意的质数,看得出,结果的值域为[0,prime-1]。
二 位运算 Hash
这类型 Hash 函数通过利用各种位运算(常见的是移位和异或)来充分的混合输入元素。比 如,标准的旋转 Hash 的构造如下: static int rotatingHash(String key, int prime) {
对不同 hash 函数以及处理冲突的方法,计算了查找成功的平均长度以及不成功的平均长 度。这些结果主要取决于装填因子,装填因子,可以将平均查找长度限定在一个范围内。
一般的说,Hash 函数可以简单的划分为如下几类:
1. 加法 Hash; 2. 位运算 Hash;
3. 乘法 Hash; 4. 除法 Hash; 5. 查表 Hash; 6. 混合 Hash; 下面详细的介绍以上各种方式在实际中的运用。
static int RSHash(String str)
{
int b
= 378551;
int a
= 63689;
int hash = 0;
for(int i = 0; i < str.length(); i++)
{
hash = hash * a + str.charAt(i);
a
= a * b;
相关文档
最新文档