一种常见的方法(至少对我来说是常见的)是将您的位串分成几个块,并在这些块上查询精确匹配作为预过滤步骤。如果您使用文件,您可以创建与块一样多的文件(例如,此处为 4 个),每个块在前面排列,然后对文件进行排序。您可以使用二分搜索,甚至可以在匹配块的上方和下方扩展搜索以获得奖励。
然后,您可以对返回的结果执行按位汉明距离计算,这应该只是整个数据集的一小部分。这可以使用数据文件或 SQL 表来完成。
回顾一下:假设您在数据库或文件中有一堆 32 位字符串,并且您希望找到在 3 位汉明距离或更短的“查询”位字符串内的每个哈希:
创建一个有四列的表:每列将包含 32 位散列的 8 位(作为字符串或 int)切片,即 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。
有可用的开源实现。该领域最好的是恕我直言,Moz 为 Simhash设计的 C++,但设计用于 64 位字符串而不是 32 位。
Moses Charikar在其“simhash”开创性论文和相应的谷歌专利中首次描述了这种有界的重叠距离方法:
- 汉明空间中的近似最近邻搜索
[...]
给定每个由 d 位组成的位向量,我们选择 N = O(n 1/(1+ ) ) 个位的随机排列。对于每个随机排列 σ,我们维护位向量的排序顺序 O σ,按照由 σ 排列的位的字典顺序。给定一个查询位向量 q,我们通过执行以下操作找到近似最近邻:
对于每个置换 σ,我们对 O σ 执行二分搜索以定位最接近 q 的两个位向量(按照由 σ 置换的位获得的字典顺序)。我们现在按照与 q 匹配的最长前缀的长度的顺序在每个排序顺序 O σ 中搜索由二分搜索返回的位置上方和下方的元素。
Monika Henziger在她的论文“寻找近乎重复的网页:算法的大规模评估”中对此进行了扩展:
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 df 位真正随机指纹的排序表。只关注表中最重要的 d 位。这些 d 位数字的列表相当于“几乎一个计数器”,因为 (a) 存在相当多的 2 d 位组合,并且 (b) 很少有 d 位组合是重复的。另一方面,最低有效的 f-d 位是“几乎随机的”。
现在选择 d 使得 |d − d| 是一个小整数。由于该表已排序,因此单个探针足以识别在 d 个最高有效位位置中与 F 匹配的所有指纹。由于|d - d| 小,这样的比赛的数量预计也很少。对于每个匹配的指纹,我们可以很容易地确定它是否与 F 最多 k 位位置不同(这些差异自然会限制在 f - d 个最低有效位位置)。
上面描述的过程帮助我们在 k 位位置上找到与 F 不同的现有指纹,所有这些都被限制在 F 的最低有效 f - d 位之间。这处理了相当多的情况。为了涵盖所有情况,只需构建少量额外的排序表就足够了,如下一节中正式概述的那样。
注意:我对相关的仅限 DB 问题发布了类似的答案