用于查找具有相似位值的附近键的数据结构

时间:2009-06-10 18:27:53

标签: database algorithm language-agnostic math hash

我有一些数据,最多可达一百万到十亿条记录,每条记录由一个位域表示,每个键大约64位。这些位是独立的,您可以将它们想象成基本上随机的位。

如果我有一个测试密钥,并且我想使用相同的密钥查找数据中的所有值,则哈希表将非常容易地在O(1)中吐出这些值。

哪种算法/数据结构可以有效地查找与查询键最相似的所有记录?这里类似意味着大多数位是相同的,但允许最小数量是错误的。这通常由Hamming distance.来衡量,它只计算不匹配位的数量。

可以通过两种方式进行此查询,一种可能是通过指定不匹配率,例如“给我一个列表,其中包含少于6位且与我的查询不同的所有现有密钥”,或者仅通过最佳匹配,例如“给我一个10,000个密钥的列表,这些密钥与我的查询具有最少的不同位数。“

你可能很想跑到k-nearest-neighbor algorithms,但在这里我们讨论的是独立位,所以像四叉树这样的结构似乎不太有用。

这个问题可以通过简单的强力测试哈希表来解决少量不同的比特。例如,如果我们想要查找与查询相差一位的所有键,我们可以枚举所有64个可能的键并对它们进行全部测试。但是这很快爆发,如果我们想要允许两位差异,那么我们必须探测64 * 63 = 4032次。对于更高的位数,它会呈指数级变差。

还有另一种数据结构或策略可以使这种查询更有效吗? 数据库/结构可以根据需要进行预处理,这是应该优化的查询速度。

13 个答案:

答案 0 :(得分:5)

你想要的是BK-Tree。它是一个非常适合索引度量空间的树(您的问题是一个),并支持最近邻和距离查询。我刚才写了an article

BK-Trees通常是参考文本并使用levenshtein距离来构建树来描述的,但是根据二进制字符串和汉明距离来编写一个很简单。

答案 1 :(得分:3)

这听起来非常适合S-Tree,它就像一个分层的倒置文件。关于该主题的良好资源包括以下论文:

Hierarchical Bitmap Index: An Efficient and Scalable Indexing Technique for Set-Valued Attributes.

Improved Methods for Signature-Tree Construction (2000)

从第一个引用:

  

分层位图索引有效支持dif-   不同类型的查询,包括子集,超集和相似性查询。   我们的实验表明,分层位图索引优于   其他集索引技术显着。

这些论文包括对您可能认为有用的其他研究的参考,例如M-Trees

答案 2 :(得分:3)

以下列方式创建表示起始集中每个键的二叉树(特别是trie):根节点是空字,向下移动树向左追加0并向下移动右边附加一个1.树只有你的起始集合中有多少个叶子,所以大小应该保持可管理。

现在你可以对这棵树进行递归遍历,在每个递归执行行中允许与查询键最多n个“偏差”,直到你发现起始集中的所有节点都在该数量的范围内。偏差。

答案 3 :(得分:1)

我会选择inverted index,就像搜索引擎一样。你基本上有一个64字的固定词汇。然后通过汉明距离来测量相似度,而不是搜索引擎想要使用的余弦相似度。构建索引的速度很慢,但您应该能够以正常的搜索引擎速度查询它。

本书Introduction to Information Retrieval涵盖了倒排索引的有效构造,存储,压缩和查询。

答案 4 :(得分:1)

从2008年开始,{p> "Near-optimal hashing algorithms for approximate nearest neighbor in high dimensions"似乎是最好的结果。自从我在一年前阅读它以来,我不会总结它,它很毛茸茸。这是来自locality-sensitive hashing上的页面,以及该方案的早期版本的实现。有关更多一般性的指示,请阅读nearest neighbor search

之前曾问过这类问题:Fastest way to find most similar string to an input?

答案 5 :(得分:1)

  

数据库/结构可以   预处理为您喜欢

嗯... IF 这是真的。那么你所需要的只是汉明距离的相似矩阵。通过修剪很远的距离使矩阵稀疏。它没有得到任何更快的速度,也没有那么多的记忆力。

答案 6 :(得分:0)

好吧,您可以插入所有邻居密钥和原始密钥。这意味着您存储(64选择k)倍数据,对于k个不同的位,并且它将要求您事先确定k。虽然你总是可以通过强力查询邻居来扩展k,这将自动查询你插入的邻居的邻居。这也为您提供了时空权衡:例如,如果您接受64 x数据爆炸并且速度慢64倍,则可以获得两位距离。

答案 7 :(得分:0)

我还没有完全考虑过这一点,但我知道我要从哪里开始。

您可以将搜索空间划分为多个存储桶,其中每个存储桶都有一个存储桶密钥,存储桶中的密钥是更类似的密钥这个桶密钥比任何其他桶密钥。要创建存储桶密钥,您可以随机生成64位密钥并丢弃任何过于接近任何先前创建的存储区密钥的密钥,或者您可以计算出一些生成完全不同的密钥的算法。要查找最接近测试密钥的密钥,首先找到最接近的存储区密钥,然后测试存储区中的每个密钥。 (实际上,有可能,但不太可能,最近的钥匙在另一个桶中 - 您是否需要找到最近的钥匙,或者非常接近的钥匙是否足够好?)

答案 8 :(得分:0)

如果您对随机算法(在本例中为蒙特卡罗)没问题,可以使用minhash

答案 9 :(得分:0)

答案 10 :(得分:0)

假设您必须访问每一行以测试其值(或者如果您在位域上编入索引,那么每个索引条目),那么您可以使用

非常有效地编写实际测试

A xor B

要查找差异位,然后使用this等技术对结果进行位计数。

这有效地为你提供了汉明距离。

由于每次测试可以编译成数十条指令,因此运行速度非常快。

答案 11 :(得分:0)

如果您可以按概率进行操作,我认为有一种解决问题2的好方法。我假设您有2 ^ 30个数据和cutoff,并且您希望找到{{1}内的所有点距cutoff的距离。

One_Try()
    1. Generate randomly a 20-bit subset S of 64 bits
    2. Ask for a list of elements that agree with test on S (about 2^10 elements)
    3. Sort that list by Hamming distance from test 
    4. Discard the part of list after cutoff

在合并列表时,您可以根据需要重复test。你尝试的次数越多,找到的点就越多。例如,如果One_Try在5位以内,您将在一次尝试中找到它,大约(2/3)^ 5 = 13%概率。因此,如果你重复100次尝试,你会发现大约10 ^ { - 6}个x。总时间:x

这样做的主要优点是,您可以在继续操作时输出问题2的答案,因为在前几次尝试之后,您肯定会发现距离不超过3位的所有内容等。

如果您有许多计算机,您可以为它们分别进行多次尝试,因为它们可以完全并行化:每台计算机都会提前保存一些哈希表。

答案 12 :(得分:-1)

如果数据不是那么稀疏,那么用键作为顶点和边连接'相邻'(汉明距离= 1)节点的图表可能在时间上非常有效。虽然这个空间非常大,但在你的情况下,我认为这不值得进行权衡。