整数哈希介绍

为什么要整数哈希

http://www.cnblogs.com/napoleon_liu/archive/2010/12/29/1920839.html

  很多时候,可以直接用整数作为键,比如QQ号码,手机号码,但这些号码的分布性不是均匀的(比如高位的变化更少,低位变化更多)。 
  分布均匀指的是每位为0或1的概率都是一样的。

理论基础

整数哈希的目标

    1. 函数要是可逆的(1对1的映射) 
    2. 雪崩效应(输入中1bit的变化 影响 输出中1/4 到 1/2的bits变化)

可逆操作

    key + const_value 加法是可逆 
    key - const_value 减法是可逆 
    key ^ const_value 异或是可逆的 
    ~key 取反也是可逆的 
    key * const_value 乘以一个奇数也是可逆的

复杂操作的可逆性分析

    a = (a+0xfd7046c5) + (a<<3);  // <<和+ 是可逆 
    a = (a+0xfd7046c5) + (a>>3);  // >>和+ 不保证是可逆的 
    a = (a^0xb55a4f09) ^ (a<<16); // ^ 和<< 是可逆 
    a = (a^0xb55a4f09) ^ (a>>16); // ^ 和>> 是可逆

 

雪崩操作

+ bit is 1 导致左边临近的所有1和最后一个0被反转( 0111 + 1 = 1000) 
- bit is 1 导致左边临近的所有0和最后一个1被反转( 1000 - 1 = 0111) 
所以加法和减法可以从低向高扩散变化

~ 可以把过多的0变1,把过多的1变成零,最后结合原始值,就可以保持0和1的平衡,以便 +或-可以有效扩散变化。

<< 让低位转到高位。 
>> 让高位转到低位。

^ 会把某些位反转, 异或可以把两个变量的变化组合一起来,这经常跟移位一起来实现1个bit的变化扩散到2个bit上。 

乘法的本质是 多个<< 和 +,也可以引起雪崩。 除法也可以影响雪崩(特别是素数)。 
不用乘法和除法的原因的是,整数的乘法和除法太耗CPU了,整数除法是最慢的操作,比浮点数数除法还慢,在一些RISC机器上可能是上百倍的差距。

 

具体哈希算法介绍

乘法

《计算机编程艺术》中讲过: 乘以一个黄金分割数:2654435761(2^32) 可以得到 HASH. 
    hash = key*2654435761; 
 

乘法的问题在于,低位可以影响高位,但高位不能影响低位。 分布是不均匀的。

 

但不是说乘法hash就没有用,在大量脚本语言中,对象的hash是通过对象的地址值来算hash的(因为对象本身会变化), 
对象分配的特点决定了,地址的高位是不变化或变化极少。

uint32 address_hash(char* addr) 

  register uint32 key; 
  key = (uint32) addr; 
  return (key >> 3) * 2654435761; //这个3是对齐边界为8 
}

这时候MASK, 应该选择高位更好(0xFFFF0000)

 

除法

  

   hash = key%prime


    除法的问题在于, 除数需要是素数,而且整数除法比较耗时。

    如果除数不是素数,就要求hash值是均匀分布。

    其实除法hash多是用在哈希表中求桶的位置,如果不能保证键的哈希函数是均匀的,那么建议使用素数来取模。 
      
    如果可以保证键值的哈希函数的质量,那么使用 MASK( &MASK)操作取代取模,效率会更高。

 

其它位运算

Tomas Wang


uint32_t hash32shift(uint32_t key) 

  key = ~key + (key << 15); // key = (key << 15) - key - 1; 
  key = key ^ (key >> 12); 
  key = key + (key << 2); 
  key = key ^ (key >> 4); 
  key = key * 2057; // key = (key + (key << 3)) + (key << 11); 
  key = key ^ (key >> 16); 
  return key; 
}

Bob Jenkins' 32 bit integer hash function


uint32_t hash( uint32_t a) 

   a = (a+0x7ed55d16) + (a<<12); 
   a = (a^0xc761c23c) ^ (a>>19); 
   a = (a+0x165667b1) + (a<<5); 
   a = (a+0xd3a2646c) ^ (a<<9); 
   a = (a+0xfd7046c5) + (a<<3); // <<和 +的组合是可逆的 
   a = (a^0xb55a4f09) ^ (a>>16);  
   return a; 
}

这六个数是随机数, 通过设置合理的6个数,你可以找到对应的perfect hash.

 

64 bit Mix Functions


uint64_t hash64shift(uint64_t key) 

  key = (~key) + (key << 21); // key = (key << 21) - key - 1; 
  key = key ^ (key >> 24); 
  key = (key + (key << 3)) + (key << 8); // key * 265 
  key = key ^ (key >> 14); 
  key = (key + (key << 2)) + (key << 4); // key * 21 
  key = key ^ (key >> 28); 
  key = key + (key << 31); 
  return key; 
}

64 bit to 32 bit Mix Functions


uint32_t hash64_32shift(uint64_t key) 

  key = (~key) + (key << 18); // key = (key << 18) - key - 1; 
  key = key ^ (key >> 31); 
  key = key * 21; // key = (key + (key << 2)) + (key << 4); 
  key = key ^ (key >> 11); 
  key = key + (key << 6); 
  key = key ^ (key >> 22); 
  return (int) key; 
}

Bob Jenkins' 96 bit Mix Function


uint32_t mix(uint32_t a, uint32_t b, uint32_t c) 

  a=a-b;  a=a-c;  a=a^(c >> 13); 
  b=b-c;  b=b-a;  b=b^(a << 8); 
  c=c-a;  c=c-b;  c=c^(b >> 13); 
  a=a-b;  a=a-c;  a=a^(c >> 12); 
  b=b-c;  b=b-a;  b=b^(a << 16); 
  c=c-a;  c=c-b;  c=c^(b >> 5); 
  a=a-b;  a=a-c;  a=a^(c >> 3); 
  b=b-c;  b=b-a;  b=b^(a << 10); 
  c=c-a;  c=c-b;  c=c^(b >> 15); 
  return c; 
}

参考

http://www.concentric.net/~ttwang/tech/inthash.htm

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值