在一个Hash表中,一个好的Hash函数决定了这个Hash表的工作效率,今天就来研究一下Hash函数。
Hash函数可以简单的划分为如下几类:
1、 加法Hash; 2、 位运算Hash; 3、 乘法Hash; 4、 除法Hash; 5、全域哈希法;
1、加法Hash
加法Hash就是把输入元素一个一个的加起来构成最后的结果。标准的加法Hash的构造如下:
int additiveHash(string key, int prime)
{
int hash = key.length();
for (int i = 0; i < key.length(); i++)
{
hash += key[i];
}
return (hash % prime);
}
2、位运算Hash
位运算Hash通过利用各种位运算(常见的是移位和异或)来充分的混合输入元素,先移位,然后再进行各种位运算是这种类型Hash函数的主要特点。标准的位运算Hash的构造如下:
int rotatingHash(string key, int prime) {
int hash = key.length();
for (int i = 0; i < key.length(); i++)
{
hash = (hash<<4>>28)^key[i];
}
return (hash % prime);
}
3、乘法Hash
乘法Hash利用了乘法的不相关性(乘法的这种性质,最有名的莫过于平方取头尾的随机数生成算法,虽然这种算法效果并不好)。标准的乘法Hash的构造如下:
int multiHash(string key, int prime) {
double hash = 0;
for (int i = 0; i < key.length(); i++)
{
hash += key[i];
}
int temp = floor((2<<14)*(hash*(sqrt(5)-1)/2 - floor(hash*(sqrt(5)-1)/2)));
return (temp % prime);
}
4、除法Hash
除法Hash就是把元素k对一个整数m取余,将k映射到对应的一个地址,标准的除法Hash如下
int divisionHash(int key)
{
int hash = key%23;
return hash;
}
5、 全域哈希法
在向哈希表中插入元素时,如果所有的元素全部被哈希到同一个桶中,此时数据的存储实际上就是一个链表,那么平均的查找时间为 Θ(n) 。而实际上,任何一个特定的哈希函数都有可能出现这种最坏情况,唯一有效的改进方法就是随机地选择哈希函数,使之独立于要存储的元素。这种方法称作全域哈希(Universal Hashing)
为什么是prime:
在上面的许多例子里都看到了一个东西 prime (素数),这个素数在代码里面的作用都是将预算内宿限制在prime这个范围内,相信读者都明白。但是为什么是prime?
证明:设 key%m = key - xm,即key减掉m的某个倍数x,剩下比m小的部分就是key除以m的余数。显然,x等于key/m的整数部分,以floor(key/m)表示。假设key和m有公约数g,即key=ag, m=bg, 则 key - xm = key - floor(key/m)m = key - floor(a/b)m。由于0 <= a/b <= a,所以floor(a/b)只有a+1中取值可能,从而推导出key%m也只有a+1中取值可能。a+1个球放在m个盒子里面,显然不可能做到均匀。
下图为出自《算法导论的解释》
具体的例子详见https://blog.csdn.net/Ilozk/article/details/90762541
最小完美哈希函数
在满足完美哈希(不会产生冲突(单射))的前提下,key值数量(假设为n)和哈希表中槽的数量(假设为m)相等,即 m = n,此种哈希函数被称为最小完美哈希函数(其实相当于数学中双射的概念)
设计完美哈希的基本思想是利用两级的哈希策略,而每一级上都使用全域哈希(Univeral Hashing)。
第一级与使用链接技术(chaining)的哈希表基本上是一样的,利用从某一全域哈希函数族中随机选择的一个函数 h ,将 n 个关键字哈希到 m 个槽中。
而此时,不像链接技术中对槽使用链表结构,而是采用一个较小的二次哈希表 Sj ,与其相关的哈希函数为 hj 。通过随机的选取哈希函数 hj ,可以确保在第二级上不出现哈希冲突。
如果利用从一个全域哈希函数族中随机选择的哈希函数 h,将 n 个关键字存储在一个大小为 m = n2 的哈希表中,那么出现碰撞的概率小于 1/2 。
为了确保第二级上不出现哈希冲突,需要让哈希表 Sj 的大小 mj 为哈希到槽 j 中的关键字数 nj 的平方。mj 对 nj 的这种二次依赖关系看上去可能使得总体存储需求很大,但通过适当地选择第一次哈希函数,预期使用的的总存储空间仍为 O(n)。
如果关键字的数量 n 等于槽的数量 m ,则该哈希函数称为最小完美哈希函数(Minimal Perfect Hash Function)。
下面是几篇关于完美Hash的博客,改日继续研究。困zzzZZZ
https://www.cnblogs.com/eve-walle/archive/2012/09/17/2688914.html
http://blog.chinaunix.net/uid-14789604-id-84624.html