一、解决的问题

统计海量数据的基数,例如 UV(独立访客数)。

  • 传统做法HashSet 存所有 ID。
  • 痛点:数据量大时内存爆炸(亿级用户需数百 MB)。
  • HLL 方案:极低内存(约 12KB)估算基数,误差约 1%。

二、核心思想

不存数据本身,只记录“随机性特征”反推数量。

三、算法步骤

1. 哈希映射

将数据(如 user_id)映射为固定长度的二进制串。

user_id -> hash -> 001010101...

2. 寻找特征(前导零)

统计二进制串开头连续 0 的个数。

  • 1xxxx 0 个 0
  • 01xxx 1 个 0
  • 001xx 2 个 0

3. 极值估算

只记录观察到的最大前导零个数

  • 原理:出现 k 个前导零的概率为
  • 结论:最大前导零越多,说明尝试次数(数据量)越多。

四、抛硬币类比

  • 连续抛 10 次正面(对应 10 个前导零)极罕见。
  • 若观察到此现象,推测肯定抛了无数次硬币(数据量大)。

五、精度优化:分桶

单次观测误差大,采用“分桶平均”法。

  1. 将数据分到 个桶(如 1024 个)。
  2. 每个桶独立记录最大前导零数。
  3. 最终通过调和平均数计算整体估算值。

六、内存优势

  • 存储内容:仅需存储每个桶的“最大前导零计数值”。
  • 空间占用:每个桶仅需几 bit,总共仅需 KB 级内存。

七、一句话总结

利用哈希值的“前导零”概率分布,以 KB 级内存估算亿级基数。 用前导0,是因为它: 1️⃣ 概率简单(1 / 2^n) 2️⃣ 容易计算(从左往右数) 3️⃣ 可逐步提高难度(n 越大越难) 4️⃣ 易验证(别人一眼就能检查)

哈希二进制 前面开头是一个0 1/2 两个0 1/4 … 这是一个指数分布