哈希函数
在计算机中,函数是一个有输入输出的黑匣子,而哈希函数是其中一类函数。我们通常会接触两类哈希函数。
- 用于哈希表的哈希函数。比如布隆过滤里的哈希函数,
HashMap
的哈希函数。 - 用于加密和签名的哈希函数。比如,MD5,SHA-256。
哈希函数通常具有以下特征。
- 长度固定。任意的输入一定得到相同的输出长度。
- 确定性。相同的输入一定得到相同的输出。
- 单向性。通过输入得到输出,但是不能通过输出反推输入。
哈希函数质量
哈希函数作用是将一堆数据信息映射到一个简短数据,这个简短数据代表了整个数据信息。比如身份证号。
如何衡量一个哈希函数质量,主要从考量以下方面
- 哈希值是否分布均匀,呈现出随机性,有利于哈希表空间利用率提升,增加哈希的破解难度;
- 哈希碰撞的概率很低,碰撞概率应该控制在一定范围;
- 是否计算得更快,一个哈希函数计算时间越短效率越高。
碰撞概率
什么是碰撞?
当同一个哈希值映射了不同数据时,即产生了碰撞。
碰撞不可避免,只能尽可能减小碰撞概率,而碰撞概率由哈希长度和算法决定。
碰撞概率如何评估。概率学中有个经典问题生日问题,数学规律揭示,23人中存在两人生日相同的概率会大于50%,100人中存在两人生日相同的概率超过99%。这违反直觉经验,所以也叫生日悖论。
生日问题是碰撞概率的理论指导。密码学中,攻击者根据此理论只需要 2^n/2 次就能找哈希函数碰撞。
下面是不同位哈希的碰撞参考表:
另外根据维基上的推导,我们还可以得到以下公式。
指定已有哈希值数量
指定碰撞概率
指定碰撞概率
|
|
|
|
|
|
根据上面公式,我们评估一下String.hashCode()
,Java里面 hashCode
() 返回 int
,所以哈希范围是 String.hashCode()
在1000万UUID下的表现。
1000万UUID,理论上的碰撞数量为11632.50
|
|
使用下面代码进行测试
|
|
碰撞总数11713非常接近理论值。
|
|
注意,上面测试不足以得出string.hashCode()性能结论,字符串情况很多,无法逐一覆盖。
对于JDK中的hashCode
算法的优劣决定了它在哈希表的分布,我们可以通过估算理论值和实测值来不断优化算法。
对于一些有名的哈希算法,比如FNV-1,Murmur2 网上有个帖子专门对比了它们的碰撞概率,分布情况。
小结
哈希函数是将长信息映射为长度固定的短数据,判断一个哈希函数的好坏考量它的碰撞概率和哈希值的分布情况。