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 == ''))  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 (kpv@research.att.com) and 
* Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com) 
* 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;
}

  

原文地址:https://www.cnblogs.com/ant-wjf/p/3452661.html