Hash介绍及几种实现

摘自网络:http://blog.minidx.com/2008/01/27/446.html,添加了些注释。

哈希算法将任意长度的二进制值映射为固定长度的较小二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的输入,在概率上是极其微小的,所以数据的哈希值可以检验数据的完整性。

 

链表查找的时间效率为O(N),二分法为log2NB+Treelog2N,但Hash链表查找的时间效率为O(1)

设计高效算法往往需要使用Hash链表,常数级的查找速度是任何别的算法无法比拟的,Hash链表的构造和冲突的不同实现方法对效率当然也会有一定的影响,然而Hash函数是Hash链表最核心的部分,下面是几款经典软件中使用到的字符串Hash函数实现,通过阅读这些代码,我们可以在Hash算法的执行效率、离散性、空间利用率等方面有比较深刻的了解。

 

下面分别介绍几个经典软件中出现的字符串Hash函数。

 
//   推荐
//   PHP 中出现的字符串Hash函数
static unsigned  long hashpjw( char *arKey, unsigned  int nKeyLength)
{
     //   check
     if ((arKey == NULL) || (nKeyLength <=  0))   return  0;

    unsigned  long h =  0;
    
    unsigned  long g =  0;
     char *arEnd = arKey+nKeyLength; 
    
     while (arKey < arEnd) 
    {
        h = (h <<  4) + *arKey++;
         if ((g = (h &  0xF0000000)))   //   处理溢出值
        {
            h = h ^ (g >>  24);
            h = h ^ g;
        }
    }

     return h;
}

//   推荐
//   OpenSSL 中出现的字符串Hash函数
unsigned  long lh_strhash( char *str)
{
     //   check
     if (str == NULL)   return  0;

    unsigned  long ret =  0;

     int i, len;
    unsigned  short *sTemp; 

    len = (strlen(str)+ 1)/ 2;
    sTemp = (unsigned  short *)str; 

     for (i= 0; i<len; i++)
        ret ^= (sTemp[i]<<(i& 0x0f));   //   1:异或^,没有溢出;最大左移16位,也没有溢出;

     return ret;


/*  The following hash seems to work very well on normal text strings 
* no collisions on /usr/dict/words and it distributes on %2^n quite 
* well, not as good as MD5, but still good. 
*/
unsigned  long lh_strhash( const  char *s)
{
     //   check
     if ((s == NULL) || (*s ==  ' \0 '))   return  0;

    unsigned  long ret =  0;

    unsigned  long v;
     int r; 

     long n =  0x100;
     while (*s)
    {
        v = n|(*s);
        r = ( int)((v>> 2)^v)& 0x0f;
        ret = (ret( 32-r));   //   vc6上编译不能通过
        ret &=  0xFFFFFFFFL;
        ret ^= v*v;

        n +=  0x100;
        s ++;
    } 
    
     return ((ret>> 16)^ret);
}

//   MySql中出现的字符串Hash函数
//   Mysql中对字符串Hash函数还区分了大小写
#ifndef NEW_HASH_FUNCTION 

/*  Calc hashvalue for a key  */
static unsigned  int calc_hashnr( const  byte *key, unsigned  int length)
{
    register unsigned  int nr= 1, nr2= 4
    
     while (length--)
    {
        nr ^= (((nr& 63)+nr2) * ((unsigned  int)(unsigned  char)*key++)) + (nr <<  8);   //   没看明白。当字符串很长时,nr2值较大,乘法可能溢出
        nr2 +=  3;
    } 
    
     return ((unsigned  int)nr);


/*  Calc hashvalue for a key, case indepenently  */
static unsigned  int calc_hashnr_caseup( const  byte *key,unsigned  int length)
{
    register unsigned  int nr= 1, nr2= 4
    
     while (length--)
    {
        nr ^= (((nr& 63)+nr2) * ((unsigned  int)(unsigned  char)toupper(*key++))) + (nr <<  8);
        nr2 +=  3;
    } 
    
     return((unsigned  int) nr);
}
#else

/*  
* Fowler/Noll/Vo hash 

* The basis of the hash algorithm was taken from an idea sent by email to the 
* IEEE Posix P1003.2 mailing list from Phong Vo ([email protected]) and 
* Glenn Fowler ([email protected]). Landon Curt Noll ([email protected]
* later improved on their algorithm. 

* The magic is in the interesting relationship between the special prime 
* 16777619 (2^24 + 403) and 2^32 and 2^8. 

* This hash produces the fewest collisions of any function that we've seen so 
* far, and works well on both numbers and strings. 
*/
unsigned  int calc_hashnr( const  byte *key, unsigned  int len)
{

    unsigned  int hash =  0
    
     const  byte *end = key+len;
     for (hash =  0; key < end; key++)
    {
        hash *=  16777619;   //   magic number   //   会有溢出
        hash ^= (unsigned  int)*(unsigned  char*)key;
    } 
    
     return (hash);


unsigned  int calc_hashnr_caseup( const  byte *key, unsigned  int len)
{
    unsigned  int hash =  0

     const  byte *end = key+len;
     for (hash =  0; key < end; key++)
    {
        hash *=  16777619;   //   magic number   //   会有溢出
        hash ^= (unsigned  int)(unsigned  char)toupper(*key);
    } 
    
     return (hash);
}
#endif

//
//   另一个经典字符串Hash函数
unsigned  int hash( char *str)
{
    register unsigned  int h= 0;

    register unsigned  char *p = (unsigned  char *)str; 
     for(h= 0; *p; p++)
        h =  31 * h + *p;   //   未考虑溢出的影响
    
     return h;
}

  

 

 

你可能感兴趣的:(hash)