一种常见的方法(至少对我来说很常见)是将您的位串分成几个块,并在这些块上查询精确匹配作为预过滤步骤。如果您使用文件,您可以创建与块一样多的文件(例如,此处为 4 个),每个块在前面排列,然后对文件进行排序。您可以使用二分搜索,甚至可以在匹配块的上方和下方扩展搜索以获得奖励。
然后,您可以对返回的结果执行按位汉明距离计算,该结果应该只是整个数据集的一小部分。这可以使用数据文件或 SQL 表来完成。
回顾一下:假设您在数据库或文件中有一堆 32 位字符串,并且您希望找到在 3 位汉明距离或小于“查询”位字符串的每个哈希值:
创建一个包含四列的表:每列将包含 32 位哈希的 8 位(作为字符串或整数)切片,即 1 到 4 切片。或者如果您使用文件,则创建四个文件,每个文件为在每个“行”的前面有一个“islice”的切片的排列
以相同的方式在 qslice 1 到 4 中对查询位字符串进行切片。
查询此表,以便 qslice1=islice1 or qslice2=islice2 or qslice3=islice3 or qslice4=islice4 中的任何一个。这将为您提供查询字符串 7 位 (8 - 1) 内的每个字符串。如果使用文件,请在四个排列的文件中的每一个中执行二进制搜索以获得相同的结果。
对于每个返回的位字符串,使用查询位字符串成对计算准确的汉明距离(从数据库或置换文件的四个切片中重建索引端位字符串)
第 4 步中的操作数应该比整个表的完整成对汉明计算少得多,并且在实践中非常有效。
此外,根据需要使用并行性提高速度,可以轻松地将文件分片为较小的文件。
当然,在您的情况下,您正在寻找一种自联接,即所有值都在彼此之间的一定距离内。恕我直言,相同的方法仍然有效,尽管您必须从一个起点向上和向下扩展以共享起始块的排列(使用文件或列表)并计算生成的集群的汉明距离。
如果在内存而不是文件中运行,您的 100M 32 位字符串数据集将在 4 GB 范围内。因此,四个置换列表可能需要大约 16GB+ 的 RAM。虽然我使用内存映射文件获得了出色的结果,并且对于类似大小的数据集必须更少的 RAM。
有可用的开源实现。该领域中最好的是恕我直言,它是为Simhash by Moz、C++ 而设计的,但设计用于 64 位字符串而不是 32 位。
Moses Charikar 在其“simhash”开创性paper 和相应的谷歌patent 中首次描述了这种有界重叠距离方法:
- 汉明空间中的近似最近邻搜索
[...]
给定由 d 位组成的位向量,我们选择
N = O(n 1/(1+ ) ) 位的随机排列。对于每个
随机排列 σ,我们维持一个有序的 O σ
位向量,按置换位的字典顺序
由 σ。给定一个查询位向量 q,我们找到近似的
通过执行以下操作来最近的邻居:
对于每个排列 σ,我们对 O σ 执行二分查找以定位
最接近 q 的两个位向量(按照由 σ 置换的位获得的字典顺序)。我们现在搜索每个
排序的顺序 O σ 检查上方和下方的元素
二分查找返回的位置
匹配 q 的最长前缀的长度。
Monika Henziger 在她的论文"Finding near-duplicate web pages: a large-scale evaluation of algorithms" 中对此进行了扩展:
3.3 算法C的结果
我们将每一页的位串分成 12 个非
重叠 4 字节片段,创建 20B 片段,并计算所有页面的 C 相似度,其中至少有一个
一块共同点。这种方法保证能找到所有
差异高达 11 的页面对,即 C 相似度 373,
但可能会因为较大的差异而遗漏一些。
Gurmeet Singh Manku、Arvind Jain 和 Anish Das Sarma 的论文 Detecting Near-Duplicates for Web Crawling 也解释了这一点:
- 汉明距离问题
定义:给定一组 f 位指纹和一个
查询指纹F,识别是否已有指纹
与 F 的差异最多为 k 位。 (在批处理模式版本中
对于上述问题,我们有一组查询指纹
而不是单个查询指纹)
[...]
直觉:考虑一个 2 d f 位真正随机指纹的排序表。只关注最重要的 d 位
在表中。这些 d 位数字的列表相当于
“几乎是一个计数器”,因为 (a) 相当多的 2 d 位-
存在组合,并且 (b) 很少有 d 位组合
重复。另一方面,最不显着的 f - d
位“几乎是随机的”。
现在选择 d 使得 |d − d|是一个小整数。自从
对表进行排序,单个探针足以识别在 d 个最高有效位位置中与 F 匹配的所有指纹。
由于|d - d|小,这样的比赛次数也少
预计很小。对于每个匹配的指纹,我们可以
很容易弄清楚它是否在最多 k 个位位置上与 F 不同
与否(这些差异自然会限于
f - d 个最低有效位位置)。
上述过程可以帮助我们找到现有的
在 k 位位置上与 F 不同的指纹,所有这些
被限制在最低有效 f - d 位之间
F. 这处理了相当多的案例。覆盖所有
在这种情况下,构建少量额外的就足够了
排序表,如下一节中正式概述的那样。
注意:我在related DB-only question 上发布了类似的答案