1 前言
前几天和一个大佬交流了几个问题,其中一个关于ID生成的问题推展到了哈希冲突和一个与之相关的一个数学趣题生日悖论。
当时对于两个事情的理解不够深刻,周末花时间仔细研究了一下,发现很有趣,于是觉得写一篇文章来和大家分享,今天的主题就是哈希冲突和生日悖论。
通过本文你将了解到以下内容:
- 哈希的映射压缩和冲突
- 生日悖论
- CRC32的冲突分析
2. 哈希映射压缩和冲突
哈希的本质就是数学,简单来说哈希函数实现了各种长度和形式的输入经过公开的哈希函数的运算生成一个固定长度的串,并且这个过程是单向不可逆的,也就是无法从哈希生成的串逆转为最初的输入。
听起来确实很神奇且有用,像一个万能胶囊,在一个小的范围内装了很多不一样的东西,原来有10MB的文件或者1GB的文件经过哈希运算后都会被映射到一个固定长度的串,可见压缩程度之大。
但是又不得不思考另外一个问题:输入是无穷尽的,生成的哈希串长度是固定的,那么必然面临着多个不一样的输入被映射压缩为一个相同的哈希串,就是无限集合映射有限集合导致的哈希碰撞或者叫哈希冲突。
举个栗子:
假如哈希串固定长度是二进制10bit,那么这个10bit空间可容纳的最大数量为2^10=1024,先不说无限输入集合,假如现在有2000个输入,在哈希函数均匀的前提下最少会有2000-1024=976个冲突。
看到这里,肯定有人会说那不用哈希了,但是哈希的压缩映射和不可逆性带来的便利确实有很大的吸引力,所以知难而退并不是个好主意。
我们知道幂次爆炸,所以如果把二进制位数扩展到32bit->64bit->128bit->256bit呢,2^32约为42亿,2^64约为1800多亿亿,128和256就更大了,貌似到了这里开始柳暗花明了,我们将长度增加空间就幂次增加,有效降低了冲突的概率,这也是当前主流的思路和方向。
小结:
哈希函数本身有很多种,底层的实现都有非常复杂的数学逻辑,均匀分布是哈希函数的一个重要特征,笔者才疏学浅并没有对哈希函数的数学原理做过多研究。
无限输入集合向有限集合的压缩映射是必然会出现碰撞的,解决碰撞的一个有效方法是增加映射空间长度,理解这一点就足够进行后面的阅读了,相信聪明的读者一定Get到笔者的意思了。
3.神奇的生日悖论
前面讨论了哈希冲突的必然性,那么我们不禁要思考:那我该选择多少bit的哈希函数才能避免碰撞呢?
其实面对这个问题的时候,我最开始是这么想的:使用32bit的哈希函数这样还有42亿个空间呢,那么产生哈希碰撞岂不是42亿分之一,貌似可以高枕无忧了,先看一个有趣的问题:
图片来自网络
生日悖论是指在不少于 23 个人中至少有两人生日相同的概率大于 50%。例如在一个 30 人的小学班级中,存在两人生日相同的概率为 70%。对于 60 人的大班,这种概率要大于 99%。从引起逻辑矛盾的角度来说,生日悖论并不是一种 “悖论”。但这个数学事实十分反直觉,故称之为一个悖论。
确实有一些违背直觉,一年365天怎么会有那么多人生日相同呢?然而这却是个事实。
来实际算一下这个问题:
假设有n个人在同一班级内,在不考虑特殊因素的前提下,例如闰年、双胞胎,假设一年365日出生概率是平均分布的,计算至少有两个人在同一日出生的概率是多少?
计算分析过程:
我们从其对立事件进行求解,找到n不断变化时对应的概率分布,p(n)表示n个人中每个人的生日都不同的概率,先考虑边界条件当n>365根据鸽巢原理其概率为0,n ≤ 365,则概率为:
在维基百科也有通用计算公式,一起看下: