zoukankan      html  css  js  c++  java
  • 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;
    }

      

  • 相关阅读:
    FZU 2112 并查集、欧拉通路
    HDU 5686 斐波那契数列、Java求大数
    Codeforces 675C Money Transfers 思维题
    HDU 5687 字典树插入查找删除
    HDU 1532 最大流模板题
    HDU 5384 字典树、AC自动机
    山科第三届校赛总结
    HDU 2222 AC自动机模板题
    HDU 3911 线段树区间合并、异或取反操作
    CodeForces 615B Longtail Hedgehog
  • 原文地址:https://www.cnblogs.com/ant-wjf/p/3452661.html
Copyright © 2011-2022 走看看