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;
    }

      

  • 相关阅读:
    机器学习中的规则化
    TF-IDF 文本相似度分析
    数据分析实践:遇到的问题及感想
    记录工作中用到的linux命令
    回归:预测数值型数据
    FP
    Application
    Mac安装sqlite3
    查看百度CUID方法
    leetcode算法题整理
  • 原文地址:https://www.cnblogs.com/ant-wjf/p/3452661.html
Copyright © 2011-2022 走看看