跳转到主内容
极星编程网:以代码为星,赴技术山海!

C++如何进行字符串的SimHash计算?(海量文本去重基础)

SimHash是一种语义敏感的指纹算法,通过分词、加权、向量合并、降维生成64位整数,使相似文本汉明距离小;而std::hash输入敏感,无法用于去重。 SimHash 是什么,为什么不能直接用
std::hash
SimHash 不是普通哈希,它生成的是「语义敏感」的指纹:相似文本产出的 64 位整数汉明距离很小。而
std::hash<:string>
对输入极其敏感,"abc" 和 "abd" 哈希值完全无关,没法用于去重。SimHash 的核心在于分词 → 加权 → 向量合并 → 降维,C++ 标准库不提供现成实现。 手动实现 SimHash 的关键四步(C++17 可用) 不需要第三方库,但得自己写清楚每一步的取舍。重点不是“跑通”,而是控制精度和性能平衡: 分词建议用空格 + 标点切割,别上
std::regex
——太慢;简单用
std::stringstream
或
std::string_view::find_first_of
切更稳 每个词用
std::hash<:string_view>
得到 64 位哈希值,再按词频或 TF-IDF 权重影响向量维度(初版可全设为 1) 维护一个长度为 64 的整型数组
weight[64]
,对每个词哈希的每一位(bit 0 到 bit 63),若该位为 1 就
+= weight
,否则
-= weight
最后遍历
weight
数组:正数则对应位设为 1,负数/零设为 0,组合成
uint64_t
返回 示例片段(省略分词):
uint64_t simhash(const std::vector& words) { int64_t weight[64] = {}; for (const auto& w : words) { uint64_t h = std::hash{}(w); for (int i = 0; i < 64; ++i) { bool bit = (h >> i) & 1; weight[i] += bit ? 1 : -1; } } uint64_t res = 0; for (int i = 0; i < 64; ++i) if (weight[i] > 0) res |= (1ULL << i); return res; }
汉明距离计算必须手写,别调
__builtin_popcountll
就完事
__builtin_popcountll(a ^ b)
确实快,但要注意:GCC/Clang 支持,MSVC 要换
std::popcount
(C++20)或查表法。更重要的是——别在循环里反复算距离。实际去重时,应先用
std::unordered_set
快速过滤汉明距离 > 3 的候选,再对小集合逐个计算精确距离。否则 O(n²) 一上来就崩。 立即学习 “ C++免费学习笔记(深入) ”; 常见翻车点:分词太粗、权重没归一、64 位移位写错 最容易被忽略的是位操作细节: 用
1 << i
计算掩码?i ≥ 31 就溢出 —— 必须写
1ULL << i
分词没去停用词、没转小写,导致 "The" 和 "the" 被当不同词,simhash 差距拉大 权重没做归一化(比如词频直接当权重),长文本天然压倒短文本,结果偏向长度而非语义 没考虑 Unicode:UTF-8 字符串直接切可能截断多字节,简单场景可先转
std::wstring
或用 ICU,但多数日志/标题场景用 ASCII 分词已够用 SimHash 看似只是个哈希,但每个环节都在悄悄吃掉召回率。真正上线前,拿几十对人工标好的相似/不相似文本跑一遍,看汉明距离分布是否符合预期——这才是最不可跳过的验证。

相关文章