Hash学习(2)-Hash函数

来源:互联网 发布:dijkstra算法证明 编辑:程序博客网 时间:2024/06/09 08:00

            一个好的hash函数一般具有以下两个特点:第一,速度快,第二,能够将散列键均匀的分布在整个表中,保证不会产生聚集。通常,hash函数具有如下形式:

hash-key = calculated-key % tablesize

上一节主要讨论了一下tablesize,为了提高散列键的离散程度,tablesize通常取素数。一般而言,没有绝对好的hash函数,hash函数的好坏很大程度上依赖于输入键的结构,人们讨论的最多的一般都是输入键为普通字符串的情况。这里也以字符串为例讨论如何一步步优化hash函数。

         当键是字符串时,一种选择策略是简单的将字符串中每个字符的ASCII码加起来,代码如下:   

[cpp] view plaincopy
  1. unsigned int hash(const char *key, unsigned int tableSize)  
  2. {  
  3.     unsigned int hashVal;  
  4.   
  5.     while(*key != '\0')  
  6.         hashVal += *key++;  
  7.   
  8.     return (hashVal % tableSize);  
  9. }  
         上面的hash函数实现简单而且能够很快的算出答案,不过,如果表很大,则函数就不能很好的分配键,例如,设tableSize=10949,并设所有的键至多8个字符长,由于ASCII字符的值最多是127,因此hash函数只能在0~1016之间取值,其中1016为127*8,显然这不是一种均匀的分配。

         下面的hash函数对针对上面的缺点进行了改进。

[cpp] view plaincopy
  1. unsigned int hash(const char *key, unsigned int tableSize)  
  2. {  
  3.     return (key[0] + 27*key[1] + 729*key[2]) % tableSize;  
  4. }  
         这个hash函数假设key至少有3个字符。值27表示英文字母表的字母个数外加一个空格,而729是27的平方。虽然该函数只考察了前三个字符,但是,假如字符出现的概率是随机的,而表的大小还是10949,那么我们就会得到一个合理的均衡分布。可是,英文不是随机的。虽然3个字符有26*26*26=17567种可能的组合,但查验词汇量足够大的联机词典却揭示出:3个字母的不同组合数实际上只有2851种。即使这些组合没有冲突,也不过只有表的28%被真正散列到。因此,虽然容易计算,但是当hash表足够大的时候,这个函数还是不合适。

         针对以上缺点,进一步改进:

[cpp] view plaincopy
  1. unsigned int hash(const char *key,unsigned int tableSize)  
  2. {  
  3.     unsigned int hashVal;  
  4.   
  5.     while(*key != '\0')  
  6.         hashVal = (hashVal << 5) + *key++;  
  7.   
  8.     return (hashVal % tableSize);  
  9. }  
         这个hash函数涉及键中的所有字符,并且一般可以分布的很好,它计算了字符串的如下值:


在计算该值时,利用了Horner法则,例如计算 hash=a+32b+32*32c 的另一种方式是借助公式:hash=((c)*32+b)*32+a。Horner法则将其扩展到用于n次多项式。该算法通过将乘法运算转换为位运算保证了hash函数快速的特点。

       下面给出实际字符串hash应用中使用的很广的一个hash函数:ELFhash

[cpp] view plaincopy
  1. unsigned long ELfHash(const unsigned char * key)  
  2. {  
  3.     unsigned long h = 0, g;  
  4.   
  5.     while(*key)  
  6.     {  
  7.         h = (h << 4) + *key++;//把h左移4位加上该字符赋给h  
  8.         if(g = h & 0xF0000000)//取h的高四位赋给g  
  9.             h ^= g >> 24;//如果g不为0,让h和g的高八位异或再赋给h  
  10.   
  11.         h &= ~g;//对g取反并与h相与赋给h  
  12.     }  
  13.     return h;  
  14. }  

http://blog.csdn.net/qll125596718/article/details/7005369

0 0