让Levenstein的Distance算法符合我的需求

时间:2012-11-01 14:01:01

标签: c# algorithm levenshtein-distance

我在使用Levenstein距离算法时遇到了一些麻烦。

我使用Levensteins距离算法将产品名称与产品名称列表进行比较,以找到最接近的匹配项。但是,我需要稍微调整一下。我正在使用dotnetperls.com中的示例。

假设我有一个来自我自己的数据库的2000个产品名称的列表A.我自己卖掉所有这些产品。

然后突然间,我从我的一个供应商处获得了一个清单B,其中包含产品名称和每种产品的新价格。这可能每年发生一次以上,所以我想开发软件来手动完成工作。

问题是该供应商在一致性方面不是很好。所以他时不时地对名字做一些小改动,这意味着我不能做一个简单的字符串比较。

我已经实现了距离算法,但它并不能满足我的需求。 - 还是!

在浏览供应商列表时,我遇到了一个名为

的产品
  

American Crew Anti Dandruff Shampoo 250 ml

此产品已成功与我自己的名为

的产品相匹配
  

American Crew Anti-Dandruff 250 ml。

距离为10。

问题

我还遇到了一个名为

的产品
  

American Crew 3合1洗发露450 ml。

错误匹配
  

American Crew Daily Shampoo 450 ml。

而不是我的

  

American Crew 3 in 1 450 ml。

我明白为什么!但我不确定如何从这里改变算法。

有什么想法吗?

顺便说一句,我对算法并不是很了解,但我相信某种称量可以帮助我解决这个问题。

修改

计算时间并不是真正的问题。即使需要十个小时才能完成,它仍然比手动完成要好得多:P

4 个答案:

答案 0 :(得分:3)

一种方法是使用多种方法来搜索并对每个方法应用权重。我假设你有一些类Item至少有一个字符串Name属性:

double levWeight = 1.0;   // adjust these weights as you see fit
double matchWeight = 1.0;

// add whatever to here you'd like
var splitOn = new[] { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '-' };
Func<string, string[]> split = 
    s => s.Split(splitOn, StringSplitOptions.RemoveEmptyEntries);

var matches =
    from xx in mine
    let xp = split(xx.Name)
    select new {
        Item = xx,
        Matches =
           (from yy in theirs
            let yp = split(yy.Name)

            /* found how many name components match */
            let mm = xp.Intersect(yp, StringComparer.OrdinalIgnoreCase).Count()

            /* find the Levenshtein distance of the two names */
            let ld = LevDist(xx, yy)

            /* weight our two criteria */
            let ww = (matchWeight * mm) + (levWeight / ld)

            /* should match on at least ONE name component */
            where mm > 0
            orderby ww descending
            select yy)
    };

当您对数据语料库运行时,我会收到以下输出:

  • American Crew Anti Dandruff洗发露250 ml
    1. American Crew Anti-Dandruff 250 ml
    2. American Crew Daily Shampoo 450 ml
    3. 美国船员3合1 450毫升
  • 美国船员3合1洗发水450毫升
    1. 美国船员3合1 450毫升
    2. American Crew Daily Shampoo 450 ml
    3. American Crew Anti-Dandruff 250 ml

如果您有更多条件,则只需将它们添加到查询中(与其他let子句一起内联)并对其结果应用一些权重。

其他可能的应用程序是“以相同顺序命名组件”

let or = xp.Zip(yp, (a,b) => String.Compare(a, b, true))
           .TakeWhile(c => c == 0)
           .Count()

答案 1 :(得分:1)

你可以将Levensthein和第二遍没有 - ,空格,圆点和第三遍相结合,计算出类似的单词,以及根据产品名中第一个不同单词的第四遍吗?

可能你可以看看神经网络吗?创建培训数据库需要很长时间,但它也可能是一个答案(或答案的一部分)。

答案 2 :(得分:1)

您需要插入一些有关数据的外部知识。例如,在您展示的情况下,“洗发水”一词并未提供有关产品的信息(即,它不会添加有关两种产品是否相似的有用信息)。如果你从你的数据中删除了“洗发水”一词,那么该方法就可以了。

因此,您可以做的一件事是计算搜索非常频繁的单词(出现在许多单词中)并从数据中删除,因为它们很可能对您的问题无法提供信息。然后运行算法。

第二个小调整就是使用你对角色' - '的了解。这个角色可以被认为类似于空格字符''。因此,在运行算法之前,将' - '替换为''。

通过这种方式,您不必更改算法,但仍然使用您对问题的了解(我知道您希望避免更改算法)。

最后,您可能想要考虑更激烈的变化,例如不使用Levenstein距离或比较整个单词。您不显示数据集,但如果差异在少量字符中(例如拼写错误),则您当前使用的表单中的Levenstein距离最佳。

答案 3 :(得分:0)

算法不会给你一个匹配但是给你一个距离。所以在你的问题场景中你应该比较你从两个不同句子得到的距离,最低距离更好,但仍然没有确定的匹配。我想最好的解决方案是根据算法向用户显示您认为匹配的不同产品名称。然后用户可以做出最终选择。但是,如果你不介意偶尔在正确的道路上错配。只需选择一个你认为匹配的距离的数字,但这会给你带来很多误报。