基数估计的黑魔法:HyperLogLog 原理与实现

news/2026/1/25 16:20:03/文章来源:https://www.cnblogs.com/kaiux/p/19530040

1. 引言:从一道经典的面试题说起

在海量数据处理领域,有一个几乎被问烂了的问题:

“给你 10 亿个 IP 地址,内存限制只有1MB,如何计算其中有多少个唯一的 IP(基数统计,Cardinality Estimation)?”

如果使用 std::setHashSet,光是存储这 10 亿个 IP 可能就会耗尽内存。即便使用 Bitmap,在数据稀疏的情况下依然不够节省。

今天我们聊聊 HyperLogLog (HLL)。它是一种概率算法,仅需几KB内存,就能以标准误差小于 2% 的精度估算 10 亿级数据的基数。

2. 直觉:抛硬币的“黑魔法”

想象你在玩一个简单的抛硬币游戏:反面记为 0,正面记为 1。规则很简单:你不断抛硬币,直到出现 第一个正面(1) 时,本轮游戏结束。

根据概率论,我们可以观察到一组有趣的现象:

  • 第 1 次就抛出正面的概率是 \(\frac{1}{2}\)
  • 连续 2 次反面后才出现正面的概率是 \(\frac{1}{8}\)
  • 连续 \(n\)反面后才出现正面的概率是 \(\frac{1}{2^{n+1}}\)

这里隐藏着一个核心逻辑: 如果你观测到了一个极其罕见的序列,比如连续出现了 20 个反面才看到正面(概率约为百万分之一),那么直觉会告诉你:为了碰上这么一次“狗屎运”,你肯定已经在大后方默默尝试了上百万次。

我们将这个直觉转化为推断公式:

如果我们在实验中观测到的“连续反面”的最大长度为 \(n\),那么我们可以大胆推测,总共大约进行了 \(2^{n+1}\) 次实验。

注意: 这只是一个“合理的推测”而非精确值。正如你不可能通过一次好运就断定自己是亿万富翁一样,HyperLogLog 也是通过这种概率分布来做近似估算

3. 从抛硬币到比特流:HyperLogLog 的数学底座

在计算机的世界里,万物皆比特(0 和 1)。我们可以巧妙地将数据对应的二进制串,看作一系列“抛硬币”的结果:0 代表反面,1 代表正面。

我们规定:从右向左扫描(从低位向高位),直到遇见第一个 1(正面)时,本轮“游戏”结束。

  • 序列 ...01:第一次就抛出正面,游戏结束。抛掷次数 = 1。
  • 序列 ...010:第一次反面,第二次正面。抛掷次数 = 2。(注意:第一个 1 之后的所有位,在概率推断中均可忽略,因为“游戏”已经结束了)。
  • 序列 ...1000:前三次均为反面,第四次才看到正面。抛掷次数 = 4。
  • 序列 ...1 后面跟着 \(n\)0:说明连续出现了 \(n\) 次反面。

现在我们来做一道概率题: 在随机分布的比特流中,连续出现 \(n\)0 才结束游戏的概率是多少?答案很简单:\(\frac{1}{2^{n+1}}\)

逆向思维: 假设我手里有一堆二进制串,其中我观察到的最长连续零长度\(n\)。根据前面的结论,我们可以反推:为了撞见这个“奇迹”,我大约已经看过了 \(2^{n+1}\) 个不同的二进制串。

这就是 HyperLogLog 的核心原理:

  1. 观察极端值:不记录数据本身,只记录所有数据中观测到的最大连续零的长度(记为 \(\rho\))。
  2. 基数推测:利用这个最大长度反推数据的唯一数量(基数)。

一句话总结: 看到连续的 \(n\)\(0\),就说明大概率已经有 \(2^{n+1}\) 种不同的数据来过这里。

4. 落地中的挑战:从理论走向工程

4.1 问题一:打破规律性(为什么需要 Hash)

刚才的数学推导建立在一个 “强假设” 之上:比特流中出现 01 的概率必须严格满足 1:1,且位与位之间相互独立。换句话说,这必须是一枚 “绝对公平的硬币”

但在现实工程中,原始数据往往是极其“不公平”的。试想我们要统计一组连续的 User ID:1000, 1001, 1002... 它们的二进制表示在长段前缀上几乎完全相同:

  • 1000 -> ...001111101000
  • 1001 -> ...001111101001

如果这些 ID 本身恰好带有大量的连续零(比如低位 ID),HLL 就会产生严重的幻觉,误以为观测到了概率极其微小的“稀有事件”,从而给出一个天文数字般的错误估算。

为了修补这个漏洞,我们必须引入 Hash 函数

虽然本文不深入探讨各种哈希算法的实现,但请务必记住它的核心使命:将规律的数据“打散”,转化为均匀分布、随机扰动的比特流。 在 HyperLogLog 的世界里,哈希不是为了加密,而是为了确保我们抛出的每一枚硬币都是“公平”的。

为什么需要hash

4.2. 问题二:如何消除“暴发户”偏差

即便硬币是公平的,概率依然存在极端情况。万一你运气爆棚,第一轮就抛出了 30 个连续的 0 呢?

算法会天真地认为你已经拥有了 10 亿条数据,而实际上你可能只插入了一条。这种单点极端值带来的偏差,在统计学上是灾难性的。

为了让结果回归理性,HyperLogLog 引入了两大“降噪”利器:分桶(Bucketing)调和平均(Harmonic Mean)

4.2.1 分桶:不把鸡蛋放在一个篮子里

既然一个人的“运气”不可靠,那我们就观察一群人的平均水平。

我们将 64 位的哈希值(或原始二进制流)切分为两部分:

  • \(b\) 位(桶索引):用来决定这个数据该去哪个“房间”。如果 \(b=10\),我们就拥有 \(m = 2^{10} = 1024\) 个独立的桶(Registers)。
  • 剩余位(抛硬币):在对应的桶里记录它所看到的最大连续零的长度。

这样一来,原本的一个“超级大猜测”,被拆分成了 1024 个“独立小猜测”。即使某个桶因为运气爆棚记录了一个异常大的值,它也只能影响 1024 分之一的决策权重,无法左右大局。

分桶

4.2.2 调和平均:对“暴发户”的降维打击

汇总这 1024 个桶的结果时,如果使用简单的算术平均,效果会非常糟糕。

举个例子:

假设 1023 个桶的值都是 1,只有一个桶因为极端运气变成了 100

  • 算术平均\((1 \times 1023 + 100) / 1024 \approx 1.1\)。看起来波动不大?别忘了,HLL 的估算是指数级的!原本是 \(2^1\),现在变成了 \(2^{1.1}\),误差会被瞬间放大。

为此,HLL 采用了调和平均数(Harmonic Mean)

\[H = \frac{m}{\sum_{i=1}^m \frac{1}{R_i}} \]

调和平均数有一个非常重要的特性:它对极大的异常值(暴发户)非常不敏感,而对较小的值更敏感。

调和平均

通过这种方式,那些因为偶然“运气好”而产生的极端大值会被有效地平滑掉,从而让估算结果稳稳地落在真实基数附近。

4.2.3 修正参数:消除算法的“自带偏差”

即便有了分桶和调和平均,数学家们在实验中发现,HLL 的原始估算公式(直接用调和平均反推)依然存在一个系统性的固有偏差

简单来说,就是算法在逻辑上会习惯性地“高估”或“低估”真实基数,且这种偏差与桶的数量 \(m\) 有关。

为了让估算值尽可能逼近真实值,HLL 引入了一个修正常数 \(\alpha_m\)。在不同的分桶精度下,这个常数有所不同:

  • \(m=16\) 时,\(\alpha_m = 0.673\)
  • \(m=32\) 时,\(\alpha_m = 0.697\)
  • \(m \ge 128\) 时,\(\alpha_m\) 趋于稳定值: $$\alpha_m \approx \frac{0.7213}{1 + 1.079/m}$$
    你在代码中看到的 0.7213 / (1 + 1.079 / m) 这一串数字,就像是给这台精密的天平做最后的 “调零”。如果没有它,HLL 的误差曲线会整体偏移。

5. 具体实现

在实现 HyperLogLog 时,我们需要重点关注两个工程细节:高效的位运算小范围修正

核心实现要点:

  1. 硬件加速:我们使用了 GCC 内置的 __builtin_ctzll 指令。它能直接调用 CPU 级别的指令(如 x86 上的 BSFTZCNT)来统计末尾零的个数,这比手写循环判断位要快得多。
  2. 内存效率:每个寄存器(Register)仅占用 8 bits(uint8_t),因为 64 位整数的连续零长度不可能超过 64。在 1024 个桶的配置下,整个数据结构仅占用 1 KB 内存。
  3. Linear Counting 修正:概率算法在数据量极小时(桶还没被填满)误差较大。因此,当估算值较小时,我们切换到 Linear Counting 算法,利用空桶的比例来计算基数,从而保证全量程的准确性。
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <string.h>// 精度参数 k (桶数为 2^10 = 1024)
#define HLL_PRECISION 10
#define HLL_REGISTERS (1 << HLL_PRECISION)
#define HLL_MASK (HLL_REGISTERS - 1)typedef struct {uint8_t registers[HLL_REGISTERS];
} HyperLogLog;/*** 简单的 64 位哈希函数 (MurmurHash3 混合部分)* 在生产环境中,建议直接使用完整的 MurmurHash3 或 CityHash*/
uint64_t hash_64(const void *key, int len) {uint64_t h = 0x12345678abcdefLL;const uint8_t *data = (const uint8_t *)key;for (int i = 0; i < len; i++) {h ^= data[i];h *= 0xbf58476d1ce4e5b9LL;h ^= h >> 33;}return h;
}/*** 获取第一个 1 出现的位置 (ρ 值)* 剔除掉用于分桶的位,统计剩余位中末尾 0 的个数*/
uint8_t get_rho(uint64_t hash) {uint64_t v = hash >> HLL_PRECISION;if (v == 0) return 64 - HLL_PRECISION;// 使用内置函数统计末尾 0 的个数,+1 表示第一个 1 出现的位置return (uint8_t)__builtin_ctzll(v) + 1;
}// 初始化:将所有寄存器清零
void hll_init(HyperLogLog *hll) {memset(hll->registers, 0, HLL_REGISTERS);
}// 添加元素:更新对应桶的最大 ρ 值
void hll_add(HyperLogLog *hll, const void *data, int len) {uint64_t hash = hash_64(data, len);int index = hash & HLL_MASK; // 提取低位作为桶索引uint8_t rho = get_rho(hash);if (rho > hll->registers[index]) {hll->registers[index] = rho;}
}// 估算基数:调和平均数汇总 + 偏差修正
double hll_count(HyperLogLog *hll) {double m = HLL_REGISTERS;// αm 修正系数double alpha_m = 0.7213 / (1 + 1.079 / m);double sum = 0.0;for (int i = 0; i < HLL_REGISTERS; i++) {// 计算 1 / 2^R[i]sum += 1.0 / (1ULL << hll->registers[i]);}double estimate = alpha_m * m * m / sum;// 小范围修正 (Linear Counting):// 如果估算值小于 2.5 * 桶数,利用空桶率进行修正if (estimate <= 2.5 * m) {int zeros = 0;for (int i = 0; i < HLL_REGISTERS; i++) {if (hll->registers[i] == 0) zeros++;}if (zeros > 0) {estimate = m * log(m / (double)zeros);}}return estimate;
}int main() {HyperLogLog hll;hll_init(&hll);printf("开始模拟插入 100,000 个唯一元素...\n");for (int i = 0; i < 100000; i++) {char buf[32];sprintf(buf, "element_%d", i);hll_add(&hll, buf, strlen(buf));}printf("实际元素: 100000\n");printf("估算基数: %.2f\n", hll_count(&hll));printf("误差率:   %.2f%%\n", fabs(hll_count(&hll) - 100000) / 100000 * 100);return 0;
}

运行结果:

hll运行结果

6. 结语:数学、工程与 AI 的浪漫交响

HyperLogLog 这个略显怪诞的名字,实则镌刻着算法进化的三部曲,如同一场宏大的交响乐章:

  • Log(对数): 核心律动。算法捕捉哈希值中连续零的位长 \(\rho\),将宏大的基数映射为优雅的对数比例。
  • LogLog(双重对数): 承前启后。2003 年,Philippe Flajolet 证明了仅需极小空间,便能窥见海量数据的轮廓。
  • Hyper(超越): 最终华章。2007 年,调和平均数与偏差修正的引入,将精度推向了“超越”极限的高度。

解决工程瓶颈的钥匙,往往并非单纯的“暴力算力”,而是深邃的“数学模型”。 这种“以虚御实”的哲学,在当今 AI 的参数压缩与高效检索中依然大放异彩。

从一枚硬币的随机博弈,到十亿级数据的基数估计,HyperLogLog 完美诠释了计算机科学的终极浪漫:以方寸内存,容纳寰宇信息;以概率之“朦胧”,换取性能之“巅峰”。

一位优秀的工程师能让代码无误;而一位卓越的工程师,则能借数学之美,令程序触碰物理极限。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1215102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IO模型有哪几种

Java / 后端开发中核心的 IO 模型分类,是网络编程、高并发开发的基础知识点。主流的 IO 模型分为5 类(从最基础的阻塞 IO 到高性能的异步 IO),本文用通俗易懂的语言从浅到深讲解,结合场景和比喻快速理解。一、IO …

01-移植NXP官方的U-Boot

01-移植NXP官方的U-Boot 前言 为了后续 Linux 驱动和应用开发能顺利展开,得先把地基打牢——也就是把 U-Boot、Linux 内核、根文件系统这三件套搬到板子上并跑通。一句话速记它们各自的角色: U-Boot:上电后的“搬运…

让opencode+GLM-4.7+SKILL一起服务

让opencodeGLM-4.7SKILL一起服务 缘起 随着克劳德的限制越来越严&#xff0c;追寻一个替代品&#xff0c;也迫上眉睫。最近opencode冒出来了&#xff0c;GLM-4.7好像也风评不错&#xff0c;而关于prompt,也慢慢进化出了skill&#xff0c;这个周末&#xff0c;刚好来试试。 过…

CSS-选择器

View PostCSS-选择器一、全局选择器(注释单行快捷键) *二、标签选择器(只要是标签都可以成为标签选择器 无论多深都能被选择上,但一旦选中控制的为所有此标签)三、类(class)选择器 核心规则:class 首字符只能是…

整理2026年淮南艺体高考培训学院排名,合肥东辰职业学校性价比高

在职业教育多元化发展的今天,艺体高考作为学子升学的重要赛道,选择一所信誉好的艺体高考培训院校,直接关系到学子的成长与未来。面对市场上良莠不齐的培训学校,如何挑选出真正的艺体高考培训学院?以下结合安徽地区…

讲讲高质量铸造钢球特点,山东金池重工产品有哪些功能亮点?

一、基础认知篇 问题1:什么是高质量铸造钢球?和普通铸造钢球有何核心区别? 高质量铸造钢球是指通过严格材质配比、精密铸造工艺与科学热处理技术生产,在硬度、耐磨性、冲击韧性等核心性能上达到行业高标准的钢球产…

2026年十大靠谱的PVC塑胶地板供应商排名,新凯琳实力入围

在医疗康养、教育机构与商业空间的地面材料选择中,PVC塑胶地板凭借环保、耐磨、易维护的特性成为高频选择,但市场上产品质量参差不齐、供应商服务水平差异显著,让采购方陷入选品难、选商更难的困境。以下结合产品工…

武汉德语培训公司哪家口碑好

问题1:选择德语培训公司时,实力具体体现在哪些方面?如何判断一家德语培训公司是否具备真正的实力? 德语培训的实力并非单一维度的指标,而是需要从课程体系、师资储备、教研成果、教学技术等多方面综合评估。真正有…

2026沐浴露专业品牌推荐,恋香花语精准护理产品值得拥有

在消费升级与个护需求精细化的浪潮下,一款契合自身需求的沐浴露不仅是清洁工具,更是日常仪式感与肌肤健康的守护者。面对市场上繁杂的沐浴露品牌与产品,如何避开香精堆砌清洁力与温和性失衡等雷区,选择真正专业可靠…

【第1章·第12节】MATLAB/C语言混合编程应用2——通过PSO粒子群算法实现网络节点最大覆盖率优化

目录 1.引言 2.PSO粒子群优化算法实现网络节点最大覆盖率原理 3.PSO粒子群优化的matlab编程实现 4.PSO粒子群优化的mex+C编程实现 本文提出了一种基于PSO粒子群优化算法的网络节点最大覆盖率求解方法,通过MATLAB/C混合编程实现。首先介绍了PSO算法原理,包括粒子速…

2025年国内知名的花灯加工厂排行榜单,庙会花灯/演绎花灯/马年花灯/大型花灯/传统花灯/春节国潮花灯,花灯定制厂家推荐

随着文旅夜游经济的蓬勃发展,十二生肖花灯作为传统节日与现代文旅场景的核心载体,正经历从“照明工具”向“文化IP+科技交互”的转型。据行业统计,2024年国内花灯市场规模突破85亿元,其中智能互动花灯占比超40%,消…

2026.1.23 闲话:TopTree 维护仙人掌

被电脑做局了,现在才发。贡献一发 TopTree 维护仙人掌的代码。不保证绝对正确,要是被 hack 了记得告诉我,我马上改 qwq。记录即将完工两次被机房电脑自动关机做局。引入 TopTree 维护树就不多提了,默认大家都会了。…

C++趣味找错误,请找出这个C++程序到底有多少错误!

C++趣味找错误,请找出这个C++程序到底有多少错误!当你正在学习C++入门的时候,通过查错,可以温故而知新,这里有一份有很多错误的C++代码,你能找出到底有多少个错误吗?//让小火箭重复4次画一个正方形, #inclu…

市场评价高的流化床干燥机厂家,定制服务解析,干燥设备/干燥机/闪蒸干燥机/喷雾干燥机/废液干燥系统,干燥机批发厂家排行榜

在化工、制药、食品等工业领域,干燥设备是保障产品品质与生产效率的核心环节。其中,流化床干燥机凭借高效节能、干燥均匀、操作灵活等优势,成为高湿度物料处理的首选设备。然而,不同行业对干燥温度、速度、物料适应…

聊聊口碑好的电火花加工,汉霸数控为何受众多知名企业信赖?

随着精密加工行业对设备精度、效率与稳定性的要求持续提升,口碑好的电火花加工、电火花品牌、精密电火花哪家更靠谱已成为众多制造企业采购决策的核心疑问。本文围绕这些高频问题,结合上海汉霸数控的技术实力与行业实…

2025年成都火锅品牌排行,这10家回头客最多!美食/烧菜火锅/火锅/特色美食/社区火锅成都火锅品牌推荐榜单

引言:口碑为王的成都火锅市场新格局 在美食之都成都,火锅不仅是餐饮品类,更是一种深入城市肌理的文化符号。随着市场竞争日趋激烈与消费者口味的不断进化,单纯依靠营销已难以长久立足。能够持续吸引回头客的品牌,…

心灵的三重疆域:弗洛伊德意识三层结构理论解析

心灵的三重疆域&#xff1a;弗洛伊德意识三层结构理论解析 在精神分析理论的构建中&#xff0c;西格蒙德弗洛伊德&#xff08;Sigmund Freud&#xff09;提出的“意识三层结构”理论&#xff0c;是颠覆人类对自身心灵认知的革命性创见。这一理论打破了“心灵即意识”的传统认知…

靠谱的安全阀在线检测仪加工厂技术对比,江西选哪家好?

随着特种设备安全监管要求的日益严格,安全阀作为保障设备运行安全的核心附件,其在线检测的精准性与便捷性成为众多企业关注的焦点。而选择专业、靠谱的安全阀在线检测仪加工厂,更是确保检测工作合规高效的关键。本文…