搜索数百万个模糊哈希值的最佳方法

时间:2015-06-01 00:29:11

标签: lucene levenshtein-distance fuzzy-search fuzzy-comparison

我在数据库表中有大约一千万个文件的spamsum复合哈希值,我想找到彼此相似的文件。 Spamsum哈希由两个最大64字节的CTPH哈希组成,它们看起来像这样:

384:w2mhnFnJF47jDnunEk3SlbJJ+SGfOypAYJwsn3gdqymefD4kkAGxqCfOTPi0ND:wemfOGxqCfOTPi0ND

它们可以分为三个部分(在冒号上分割字符串):

  1. 块大小:上面哈希中的384
  2. 首次签名:w2mhnFnJF47jDnunEk3SlbJJ+SGfOypAYJwsn3gdqymefD4kkAGxqCfOTPi0ND
  3. 第二次签名:wemfOGxqCfOTPi0ND
  4. 块大小是指第一个签名的块大小,第二个签名的块大小是第一个签名的块大小(此处:384 x 2 = 768)。每个文件都有这些复合哈希中的一个,这意味着每个文件都有两个具有不同块大小的签名。

    只有在块大小对应的情况下才能比较spamsum签名。也就是说,上面的复合哈希可以与包含块大小为384或768的签名的任何其他复合哈希进行比较。具有相似块大小的哈希的签名字符串的相似性可以作为两者之间相似性的度量。哈希表示的文件。

    所以如果我们有:

    • file1.blk2 = 768
    • file1.sig2 = wemfOGxqCfOTPi0ND
    • file2.blk1 = 768
    • file2.sig1 = LsmfOGxqCfOTPi0ND

    通过计算两个签名的一些加权编辑距离(如Levenshtein距离),我们可以了解两个文件的相似程度。这两个文件看起来非常相似。

    leven_dist(file1.sig2, file2.sig1) = 2
    

    还可以计算两个哈希值之间的归一化相似度得分(详见here)。

    我想根据这些哈希找到任何两个超过70%相似的文件,我非常喜欢使用可用的软件包(或API / SDK),尽管我不怕编码我的解决问题的方法。

    我已经尝试打破哈希并使用Lucene(4.7.0)索引它们,但搜索似乎是缓慢而乏味的。以下是我尝试过的Lucene查询的示例(对于每个单一签名 - 每个哈希两次并使用区分大小写的KeywordAnalyzer):

    (blk1:768 AND sig1:wemfOGxqCfOTPi0ND~0.7) OR (blk2:768 AND sig2:wemfOGxqCfOTPi0ND~0.7)
    

    似乎Lucene的incredibly fast Levenshtein automata不接受超过2的编辑距离限制(我需要它支持高达0.7 x64≃19)并且其正常编辑距离算法未针对长搜索术语进行优化({ {3}}。)那就是说,我的查询可能没有针对我想做的事情进行优化,所以请不要犹豫,纠正我。

    我想知道我是否可以使用Lucene提供的任何算法来完成我所需要的,而不是直接计算编辑距离。我听说BK树是索引此类搜索的最佳方式,但我不知道算法的可用实现(Lucene是否使用了这些?)。我也听说过一个可能的解决方案是使用n-gram方法缩小搜索列表的范围,但我不确定如何在包容性和速度方面编辑距离计算(我很确定Lucene支持那个)。顺便说一句,有没有办法让Lucene在并行模式下运行术语搜索?

    鉴于我使用Lucene仅预先匹配哈希并且我稍后使用适当的算法计算真实的相似性得分,我只需要一种至少与相似性得分计算中使用的Levenshtein距离一样包容的方法 - - 也就是说,我不希望预匹配方法排除由评分算法标记为匹配的哈希值。

    感谢任何帮助/理论/参考/代码或线索。

1 个答案:

答案 0 :(得分:3)

这不是问题的明确答案,但从那以后我尝试了很多方法。我假设哈希值保存在数据库中,但这些建议对于内存数据结构仍然有效。

  1. 将所有签名(每个哈希2个)及其对应的块大小保存在单独的子表中。由于只能将相同大小的签名相互比较,因此您可以在开始比较签名之前按块大小过滤表。
  2. 将超过三个字符的所有重复序列减少为三个字符(' bbbbb' - >' bbb')。 Spamsum的比较算法会自动执行此操作。
  3. Spamsum使用7的滚动窗口来比较签名,并且在消除过多重复之后,不会比较任何两个没有7个字符重叠的签名。如果您使用支持列表/数组作为字段的数据库,请创建一个字段,其中列出了从每个签名中提取的所有可能的7个字符序列。然后在此字段上创建您可以访问的最快的完全匹配索引。在尝试找到两个签名的距离之前,首先尝试在此字段上进行完全匹配(共有7克?)。
  4. 我正在尝试的最后一步是将签名及其七克保存为二分图的两种模式,将图形投影为单一模式(仅由哈希组成),然后仅在相邻节点上计算Levenshtein距离具有相似的块大小。
  5. 上述步骤进行了良好的预匹配,并且大大减少了每个签名必须与之进行比较的签名数量。只有在这些之后才能计算出改进的Levenshtein / Damreau距离。