匹配两个包含少于 10 个拉丁文单词的字符串的最佳算法是啥

Posted

技术标签:

【中文标题】匹配两个包含少于 10 个拉丁文单词的字符串的最佳算法是啥【英文标题】:What is the best algorithm for matching two string containing less than 10 words in latin script匹配两个包含少于 10 个拉丁文单词的字符串的最佳算法是什么 【发布时间】:2015-01-27 07:45:04 【问题描述】:

我正在比较歌曲标题,使用拉丁字母(尽管并非总是如此),我的目标是一种算法,如果两首歌曲标题似乎是相同的标题,则给出高分,如果它们什么都没有,则给出非常低的分数共同点。

现在我已经必须使用 Lucene 和 RAMDirectory 编写代码 (Java) 来编写这个 - 但是使用 Lucene 来比较两个字符串太重了,因此太慢了。我现在开始使用https://github.com/nickmancol/simmetrics,它有很多很好的算法来比较两个字符串:

https://github.com/nickmancol/simmetrics/tree/master/src/main/java/uk/ac/shef/wit/simmetrics/similaritymetrics

BlockDistance
ChapmanLengthDeviation
ChapmanMatchingSoundex
ChapmanMeanLength
ChapmanOrderedNameCompoundSimilarity
CosineSimilarity
DiceSimilarity
EuclideanDistance
InterfaceStringMetric
JaccardSimilarity
Jaro
JaroWinkler
Levenshtein
MatchingCoefficient
MongeElkan
NeedlemanWunch
OverlapCoefficient
QGramsDistance
SmithWaterman
SmithWatermanGotoh
SmithWatermanGotohWindowedAffine
Soundex

但我对这些算法并不精通,什么是好的选择?

我认为 Lucene 以某种形式使用 CosineSimilarity,所以这是我的出发点,但我认为可能会有更好的方法。

具体来说,该算法应该适用于短字符串,并且应该理解单词的概念,即应该对空格进行特殊处理。拉丁文字的良好匹配是最重要的,但其他文字(如韩文和中文)的良好匹配也是相关的,但我希望由于它们处理空格的方式而需要不同的算法。

【问题讨论】:

简单化:将空格上的字符串拆分为单词数组,进行数组交集。计数较高的交叉点意味着更多的共同词。 由 TermQueries 组成的 Lucene BooleanQuery 会根据匹配词的数量对每个文档进行评分,这听起来很像您正在寻找的内容。 @MarkoTopolnik 是的,但我只是想比较两个字符串处理,因为 lucene neans 创建了两个文档、一个 ram 目录、分析器并且太慢了。不使用 lucene 应该可以得到很好的匹配 @MarcB 但这不考虑拼写变化和拼写错误我正在寻求帮助,使用已创建的相似性指标之一进行选择 我以为你想比较 N 个标题,每个标题。当然,如果您的工作单元只是比较 两个 标题,那是没有回报的。在这种情况下,一个愚蠢的 O(n^2) 算法,但常数因子低,将是一个更好的选择。 【参考方案1】:

他们都很好。它们作用于字符串的不同属性并具有不同的匹配属性。最适合您的取决于您的需求。

我正在使用 JaccardSimilarity 来匹配名称。我选择 JaccardSimilarity 是因为它相当快,而且对于短字符串而言,它擅长将名称与常见的错字匹配,同时迅速降低其他任何东西的分数。给空间额外的重量。它也对词序不敏感。我需要这种行为,因为误报的影响比误报的影响要大得多,空格可能是拼写错误,但不常见,而且词序并不那么重要。

请注意,这是结合删除非变音符号的简化器和将剩余字符映射到 a-z 范围的映射器完成的。这是通过将所有单词分隔符标准化为单个空格的规范化传递的。最后,解析名称以挑选出首字母、前缀和后缀。这是因为名称具有一种结构和格式,它对字符串比较具有相当的抵抗力。

要做出选择,您需要列出您想要的标准,然后寻找满足这些标准的算法。您还可以创建一个相当大的测试集并在该测试集上运行所有算法,也可以查看在时间、正数、误报、误报和负数方面的权衡,您的系统应该处理的错误类别,等等,等等。

如果您仍然不确定自己的选择,您还可以设置系统以在运行时切换精确的比较算法。这使您可以进行 A-B 测试并查看哪种算法在实践中效果最佳。

TLDR;您想要哪种算法取决于您需要什么,如果您不知道自己需要什么,请确保您可以稍后更改它并即时运行测试。

【讨论】:

我想我已经很清楚我需要什么了,我的问题是不清楚这些不同的算法到底是为了什么。但是好的建议和你关于 JaccardSimailrity 的具体建议听起来很有希望。 还可以查看 org.simmetrics.SimonWhite 变体。看起来它是与 Jaccard 相关的 Sorensons 指标的实现。 @PaulTaylor 我必须做一些更正。在我自己的项目中,我使用的是 JaccardSimilarity。但是,我使用的是 2-gram 标记器,而不是 simmetrics 使用的空白标记器。在 simmetrics 中,这是 DiceSimilarity 度量。它使用 2-gram 分词器。骰子相似度也称为 Sorensons 度量。 SimonWhite 变体与两者相似,但使用空格和 2-gram 标记器的组合,并使用数组交集而不是设置差异。对排名的影响相当微妙且过于复杂,无法在评论中描述。 我还没有根据您的建议实施解决方案,但这似乎是个好建议并且直接回答了问题,所以我为您提供赏金 干杯。我在 simmetrics 库中获得了一些乐趣,我很高兴你为我指明了它的方向。目前正在进行重构,以便我可以合并我自己开发的一些东西。这是一个不错的库,但在过去的两年里一直没有维护,它显示了。签出:github.com/mpkorstanje/simmetrics/blob/master/src/main/java/uk/…SimonWhite.java 或 Dice.java 与 TokeniserWordQGram.java 和 TokeniserQGram2.java 它将为您节省一些时间。【参考方案2】:

您可能需要解决string-to-string correction problem。 Levenshtein 距离算法is implemented in many languages。在运行它之前,我会从字符串中删除所有空格,因为它们不包含任何敏感信息,但可能会影响两个字符串的差异。对于字符串搜索前缀树也很有用,你也可以看看这个方向。 For example here 或 here。已经在SO 上讨论过。如果在您的情况下空格非常重要,只需为它们分配更大的权重。

【讨论】:

那么,您描述的最佳算法是否没有名称,而不仅仅是使用 Levenstein,而是让它变得更好,我已经知道 Levenstein,但问题中还有很多其他算法,我会将它们添加到问题中以使其更清楚。 最佳算法取决于您的具体领域。我会从某人开始。然后修改它解决我的问题。 Levenstein 看起来是个不错的候选人。【参考方案3】:

每个算法都将关注两个字符串的相似但略有不同的方面。老实说,这完全取决于您要完成的工作。你说算法需要理解单词,但它是否也应该理解这些单词之间的交互?如果没有,您可以根据空格分解每个字符串,并将第一个字符串中的每个单词与第二个字符串中的每个单词进行比较。如果它们共享一个单词,则需要增加两个字符串的共性因子。

通过这种方式,您可以创建自己的算法,只关注您关心的问题。如果您想测试其他人制作的另一种算法,您可以在网上找到示例并运行您的数据,以查看估计的每个算法的共性有多准确。

我认为http://jtmt.sourceforge.net/ 是一个不错的起点。

【讨论】:

【参考方案4】:

有趣。您是否考虑过基数排序?

http://en.wikipedia.org/wiki/Radix_sort

基数排序背后的概念是它是一种非比较整数排序算法,它通过按单个数字对键进行分组来使用整数键对数据进行排序。如果您将字符串转换为字符数组,该数组将是一个不超过 3 位的数字,那么您的 k=3(最大位数)并且您的 n = 要比较的字符串数。这将对所有字符串的第一个数字进行排序。然后你将有另一个因素 s=最长字符串的长度。排序的最坏情况是 3*n*s,最好的情况是 (3 + n) * s。在此处查看一些字符串的基数排序示例:

http://algs4.cs.princeton.edu/51radix/LSD.java.html

http://users.cis.fiu.edu/~weiss/dsaajava3/code/RadixSort.java

【讨论】:

【参考方案5】:

你有没有看一下levenshtein距离?

int org.apache.commons.lang.StringUtils.getLevenshteinDistance(String s, String t)

求两个字符串之间的 Levenshtein 距离。

这是将一个字符串更改为所需的更改次数 另一个,其中每个更改都是单个字符修改 (删除、插入或替换)。

Levenshtein 距离算法的先前实现是 来自http://www.merriampark.com/ld.htm

Chas Emerick 用 Ja​​va 编写了一个实现,它避免了 使用我的 Java 实现时可能发生的 OutOfMemoryError 非常大的字符串。 Levenshtein 的这种实现 距离算法来自http://www.merriampark.com/ldjava.htm

无论如何,我很想知道在这种情况下你会选择什么!

【讨论】:

以上是关于匹配两个包含少于 10 个拉丁文单词的字符串的最佳算法是啥的主要内容,如果未能解决你的问题,请参考以下文章

字符串列包含通过 spark scala 精确匹配的单词

将大字符串中的子字符串匹配到大量关键字的最佳方法是啥

leetcode 824. 山羊拉丁文

Python 正则表达式匹配包含两个或更少 o 字符的空格分隔的单词

从大型歌手中寻找最佳匹配词

匹配两个 RDD [字符串]