Hash Compared & ELFHash 详解

常用HASH算法 代码 & 比较

  • 常用的字符串Hash函数还有ELFHash,APHash等等,都是十分简单有效的方法。这些函数使用位运算使得每一个字符都对最后的函数值产生影响。另外还有以MD5和SHA1为代表的杂凑函数,这些函数几乎不可能找到碰撞。
  • 常用字符串哈希函数有BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,PJWHash,ELFHash等等。对于以上几种哈希函数,我对其进行了一个小小的评测。
Hash函数  数据1 数据2 数据3    数据4  数据1得分   数据2得分   数据3得分   数据4得分   平均分
BKDRHash    2   0     4774    481       96.55   100         90.95       82.05       92.64
APHash      2   3     4754    493       96.55   88.46       100         51.28       86.28
DJBHash     2   2     4975    474       96.55   92.31       0           100         83.43
JSHash      1   4     4761    506       100     84.62       96.83       17.95       81.94
RSHash      1   0     4861    505       100     100         51.58       20.51       75.96
SDBMHash    3   2     4849    504       93.1    92.31       57.01       23.08       72.41
PJWHash     30  26    4878    513       0       0           43.89       0           21.95
ELFHash     30  26    4878    513       0       0           43.89       0           21.95
  • 其中数据1为100000个字母和数字组成的随机串哈希冲突个数。数据2为100000个有意义的英文句子哈希冲突个数。数据3为数据1的哈希值与1000003(大素数)求模后存储到线性表中冲突的个数。数据4为数据1的哈希值与10000019(更大素数)求模后存储到线性表中冲突的个数。
  • 经过比较,得出以上平均得分。平均数为平方平均数。可以发现,BKDRHash无论是在实际效果还是编码实现中,效果都是最突出的。APHash也是较为优秀的算法。DJBHash,JSHash,RSHash与SDBMHash各有千秋。PJWHash与ELFHash效果最差,但得分相似,其算法本质是相似的。
  • 在信息修竞赛中,要本着易于编码调试的原则,个人认为BKDRHash是最适合记忆和使用的。
  • BYVoid原创,欢迎建议、交流、批评和指正。
  • 附:各种哈希函数的C语言程序代码
SDBM Hash
  • SDBM Hash
unsigned int SDBMHash(char *str)
{
    unsigned int hash = 0;
    while (*str)
    {
        // equivalent to: hash = 65599*hash + (*str++);
        hash = (*str++) + (hash << 6) + (hash << 16) - hash;
    }
    return (hash & 0x7FFFFFFF);
}
RS Hash Function
  • RS Hash Function
// RS Hash Function
unsigned int RSHash(char *str)
{
    unsigned int b = 378551;
    unsigned int a = 63689;
    unsigned int hash = 0;
    while (*str)
    {
        hash = hash * a + (*str++);
        a *= b;
    }
    return (hash & 0x7FFFFFFF);
}
JS Hash Function
  • JS Hash Function
// JS Hash Function
unsigned int JSHash(char *str)
{
    unsigned int hash = 1315423911;
    while (*str)
    {
        hash ^= ((hash << 5) + (*str++) + (hash >> 2));
    }
    return (hash & 0x7FFFFFFF);
}
P. J. Weinberger Hash Function
  • P. J. Weinberger Hash Function
// P. J. Weinberger Hash Function
unsigned int PJWHash(char *str)
{
    unsigned int BitsInUnignedInt = (unsigned int)(sizeof(unsigned int) * 8);
    unsigned int ThreeQuarters    = (unsigned int)((BitsInUnignedInt  * 3) / 4);
    unsigned int OneEighth        = (unsigned int)(BitsInUnignedInt / 8);
    unsigned int HighBits         = (unsigned int)(0xFFFFFFFF) << (BitsInUnignedInt - OneEighth);
    unsigned int hash             = 0;
    unsigned int test             = 0;
    while (*str)
    {
        hash = (hash << OneEighth) + (*str++);
        if ((test = hash & HighBits) != 0)
        {
            hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
        }
    }
    return (hash & 0x7FFFFFFF);
}
ELF Hash Function
  • ELF Hash Function
// ELF Hash Function
unsigned int ELFHash(char *str)
{
    unsigned int hash = 0;
    unsigned int x    = 0;
    while (*str)
    {
        hash = (hash << 4) + (*str++);
        if ((x = hash & 0xF0000000L) != 0)
        {
            hash ^= (x >> 24);
            hash &= ~x;
        }
    }
    return (hash & 0x7FFFFFFF);
}
BKDR Hash Function
  • BKDR Hash Function
// BKDR Hash Function
unsigned int BKDRHash(char *str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
    unsigned int hash = 0;
    while (*str)
    {
        hash = hash * seed + (*str++);
    }
    return (hash & 0x7FFFFFFF);
}
// DJB Hash Function
unsigned int DJBHash(char *str)
{
    unsigned int hash = 5381;
    while (*str)
    {
        hash += (hash << 5) + (*str++);
    }
    return (hash & 0x7FFFFFFF);
}
AP Hash Function
  • AP Hash Function
// AP Hash Function
unsigned int APHash(char *str)
{
    unsigned int hash = 0;
    int i;
    for (i=0; *str; i++)
    {
        if ((i & 1) == 0)
        {
            hash ^= ((hash << 7) ^ (*str++) ^ (hash >> 3));
        }
        else
        {
            hash ^= (~((hash << 11) ^ (*str++) ^ (hash >> 5)));
        }
    }
    return (hash & 0x7FFFFFFF);
}

ELFHash详细分析

  • ELFHash
// ELF Hash Function
unsigned int ELFHash(char *str)
{
    unsigned int hash = 0;
    unsigned int x = 0;
    while (*str)
    {
        hash = (hash << 4) + (*str++);//hash左移4位,当前字符ASCII存入hash
        if ((x = hash & 0xF0000000L) != 0)
        {//如果最高的四位不为0,则说明字符多余7个,如果不处理,再加第九个字符时,第一个字符会被移出,因此要有如下处理。
            //该处理,如果对于字符串(a-z 或者A-Z)就会仅仅影响5-8位,否则会影响5-31位,因为C语言使用的算数移位
            hash ^= (x >> 24);
            //清空28-31位。上面其实就是把即将删除的高四位和低5-8位运算一次,和 hash = (hash << 4) + (*str++); 效果相同
            hash &= ~x;
        }
    }
    //返回一个符号位为0的数,即丢弃最高位,以免函数外产生影响。(我们可以考虑,如果只有字符,符号位不可能为负)
    return (hash & 0×7FFFFFFF);
}
  • 解释
ELFhash函数在UNIX系统V 版本4中的“可执行链接格式”( Executable and Linking Format,即ELF )中会用到,ELF文件格式用于存储可执行文件与目标文件。ELFhash函数是对字符串的散列。它对于长字符串和短字符串都很有效,字符串中每个字符都有同样的作用,它巧妙地对字符的ASCII编码值进行计算,ELFhash函数对于能够比较均匀地把字符串分布在散列表中。
说明:unsigned int hash = 0; unsigned int x = 0;
定义无符号整数,在进行位运算时无需考虑符号位的影响,左移和右移均补位0
int 为32位 ,即  00000000  00000000   00000000   00000000
hash = (hash << 4) + (*str++);//hash左移4位,当前字符ASCII存入hash
例,如果hash2时,(hash << 4)操作后,放大1624次方)倍;然后加上(*str++),(*str++)为8位的字符,所以对4-7为有影响,其后四位添到hash左移空出的四位。
if ((x = hash & 0xF0000000L) != 0)
0xF0000000L表示28-31位这4位是1,后28为均为0的长整型(L),该操作的结果为x保存hash 的高4位
& 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
hash ^= (x >> 24);
首先x的拷贝进行右移23位的操作,然后与hash进行异或操作。
右移后X的值为 00000000 00000000 00000000  ****0000  ;****为hash的高四位
^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1
hash &= ~x;
有 if ((x = hash & 0xF0000000L) != 0),x保存着hash的高四位,虽然进行右移操作,但不会改变x的值,而是对副本进行操作。经过hash &= ~x;  hash的高四位被清空。
//返回一个符号位为0的数,即丢弃最高位,以免函数外产生影响。(我们可以考虑,如果只有字符,符号位不可能为负)
return (hash & 0×7FFFFFFF);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值