认识哈希

1 哈希性质

  1. 输入无穷,输出有穷
  2. 无随机机制,一个输入有且只有唯一的输出
  3. 不同的输入输出可能相同(哈希碰撞),保证尽量不同
  4. 散列尽量均匀
  5. 具有不可逆性 -> 单向加密

2 资源限制

举例:40 亿整数文件,整数 4 个字节,只有1GB内存,返回次数最多的数

一个数最多出现 40 亿次,如果每个都出现一次,也就最多 40 条记录

所以使用哈希表则最少需要 4 B 的 key 和 4 B 的 value,其他都不算的情况下,最少也得需要 (4 + 4)* 40 亿 = 320 亿 也就是 32G,所以这个方案不可行

这时我们可以使用哈希函数的特性

  1. 一个数经过哈希函数再模 40 丢到一个文件里 -> 相同的数一定会在同个文件(性质 2)
  2. 经过哈希函数后,散列尽量是均匀的 -> 每个文件就会大概是 1 亿上下,所以内存足够

3 哈希表

经典哈希表设计

  1. 通过哈希函数获得一个值,直接定位到位置,所以 CRUD 为 O(1)
  2. 当发生哈希碰撞的时候,一般采用拉链法
  3. 一个链表太长,扩容桶大小,重新哈希 -> 因为散列近似均匀,所以当一个链表太长时,就可以断定为其他链表也都很长了
  4. 桶扩容成本为 O(N),均摊下来哈希表的时间复杂度为 O(1)
  5. 要尽量避免桶扩容的出现 -> 离线扩容、挂有序表 等改进

Java 改进

  1. 挂的链表太长时,转换为红黑树
  2. 红黑树操作成本是 logN,同样是 8 次操作,红黑树可以检索 256 个,也就是同样操作次数下,可允许哈希碰撞 256 次
  3. 通过这种方式几乎在实际应用上扩容不可能出现

4 布隆过滤器

位图:通过标记二进制位上是否为 1,来确认是否加入黑名单(同理,你也可以构建白名单)

  1. int[] arr = new int[100] -> 只需要 100 个字节就能标志 3200 个记录
  2. 找到 453 个记录的状态,status = (arr[453/32] >> (453%32)) & 1
  3. 一维数组不够,可扩展为二维数组,同理可无限扩展
  4. 一定会把黑名单的内的拦截,但是会造成误判,不在黑名单内却被误认为在黑名单内 -> 因为存在哈希碰撞
  5. 位图不能太小,因为将标志位填满,不管什么数对应的标志都是 1,所以会提高误判率

布隆过滤器:上述只经过一次哈希函数,所以一位相同就会产生误判

  1. 通过多个哈希函数,获取多个位标志,这几个位标志同时为 1 时,才确认在黑名单内,大大降低误判率
  2. 因为同时标志多个位,所以当哈希函数太多,也很快就会把整个比特序列标志满,从而导致误判率上升。所以 哈希函数个数也不能太多

从而产生以下问题:位图要选多大?哈希函数又要多少个?首先要知道数据量 n 和预期失误率 p(实际证明略,实际只需要公式)

  1. 位图大小: m = − n × ln ⁡ p ( ln ⁡ 2 ) 2 \mathrm{m}=-\frac{n \times \ln p}{(\ln 2)^{2}} m=(ln2)2n×lnp
  2. 哈希个数: k = ln ⁡ 2 × m n \mathrm{k}=\ln 2 \times \frac{m}{n} k=ln2×nm
  3. 真实失误率: ( 1 − e − n k m ) k \left(1-e^{-\frac{n k}{m}}\right)^{k} (1emnk)k

ln2 可取 0.7

相互独立的哈希函数,只需要两个使用两个独立的哈希函数线性组合

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值