LevenshteinDistance方法无法提供最准确的结果

时间:2015-08-07 13:20:37

标签: c# levenshtein-distance fuzzy-search

我有一个" X"名字的数量,我需要将这些名称与另一个文件相匹配,看看这些名字是否在其中,但是以不同的方式书写(" Verizon" - >" Verizon LTD&#34 ;)

我正在使用"模糊查找"在视觉工作室2008上互操作,并取得了不错的成绩。

现在我尝试实现LevenshteinDistance方法来实现此结果,以便该方法使用完整列表迭代我需要的另一个文件的名称,并返回具有最佳分数/概率的名称是一样的。

我使用的代码如下:

public static int LevenshteinDistance(string src, string dest)
{
    int[,] d = new int[src.Length + 1, dest.Length + 1];
    int i, j, cost;

    for (i = 0; i <= src.Length; i++)
    {
        d[i, 0] = i;
    }
    for (j = 0; j <= dest.Length; j++)
    {
        d[0, j] = j;
    }


    for (i = 1; i <= src.Length; i++)
    {
        for (j = 1; j <= dest.Length; j++)
        {

            if (src[i - 1] == dest[j - 1])
                cost = 0;
            else
                cost = 1;

            d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);

        }
    }

    return d[src.Length, dest.Length];
}

public static List<string> Search(string word, List<string> wordList, double fuzzyness)
{
    List<string> foundWords = new List<string>();

    foreach (string s in wordList)
    {
        // Calculate the Levenshtein-distance:
        int levenshteinDistance =
            LevenshteinDistance(word.ToUpper(), s.ToUpper());

        // Length of the longer string:
        int length = Math.Max(word.Length, s.Length);

        // Calculate the score:
        double score = 1.0 - (double)levenshteinDistance / length;

        // Match?
        if (score >= fuzzyness)
        {
            foundWords.Add(s);
        }
    }
    return foundWords;
}

以下示例是我运行的测试,其中我们想要匹配的单词是&#34; ILCA INC&#34;,我们按如下方式运行:

相似度设置:&gt; = 0.77

搜索字词列表 &#34; ILCA&#34; 0.5 aprox - &gt;这是我们使用VS2008&#34;模糊查找&#34;得到的结果。 &#34; ICE INC&#34; 0.77 aprox - &gt;这是我的代码带来的。

如果我可以就此主题获得任何意见,我会非常感激,但是我无法让这个应用程序达到相同的结果,即模糊查找&#34;模糊查找&#34;确实

如果我能提供更多信息,或者我的问题表达错误,请告诉我。

2 个答案:

答案 0 :(得分:0)

根据结果,微软的模糊搜索结果并不像1 - EditDistance / WordLength那么简单。 “ILCA INC”和“ICE INC”之间的编辑距离是2 - 一次插入和一次替换。与Microsoft的模糊查找返回的更好结果相比,编辑的次数更少。

虽然模糊查找可能使用编辑距离作为其等式的部分,但我假设确定匹配分数的整体方法是专有的,本质上是算法和启发式的。正如您可能已经知道的那样,模糊查找优先考虑具有从0开始的子字符串匹配的单词,而不是具有较低编辑距离的单词。

答案 1 :(得分:0)

编写一个调试例程来转储d数组的内容非常方便,这样你就可以确保它的工作原理。例如,您提到的比较:

    I C E   I N C
  0 1 2 3 4 5 6 7
I 1 0 1 2 3 4 5 6
L 2 1 1 2 3 4 5 6
C 3 2 1 2 3 4 5 5
A 4 3 2 2 3 4 5 6
  5 4 3 3 2 3 4 5
I 6 5 4 4 3 2 3 4
N 7 6 5 5 4 3 2 3
C 8 7 6 6 5 4 3 2

正如另一张海报所提到的,2的距离是正确的,你的比较中有2个拼写错误(A的L和E都下降了)。得分为0.75,我不确定你是怎么得到的.77。

我愿意打赌微软算法正在以不同方式计算得分。它可能取两个长度的最小值或平均值而不是最大值,就像你一样。

计算正确百分比&#39;使用Levenshtein等算法是一个难题。正如您在示例中所看到的,短字符串比较产生百分比的狂野波动,并且使用适用于更长时间比较的阈值并不适用于较短的(反之亦然)。

阈值斜坡: 无论输入字符串长度如何,您当前的决策逻辑都使用常量值。但是,使用&#39; ramp&#39;有时更实际,其中over / under值根据字符串长度而变化。例如,您可能认为三个字符以下的字符串必须完全匹配(100%),四个字符串必须匹配超过70%,五个字符字符串为75%,六个为80%等。在某些时候(大约8-之后) 10个字符),通常可以坚持使用单个值。

实现非常简单,使用int []查找表:

double[] thresholds=new double[] {100, 100, 100, 70, 75, 80, (etc) };
double targetThreshold=thresholds[Math.Max(src.Length,dest.Length)-1];

...

if (score >= targetThreshold)
  foundWords.Add(s);

(使用适合您需要的阈值)。它通常会带来更实际的结果。

这种技术的缺点是,如果你想要一个真正可变的阈值百分比,很难编码。正如您在我的示例中所看到的,我忽略了模糊度输入参数。