高度可并行化的Levenshtein距离算法

时间:2013-04-16 11:43:37

标签: c# .net algorithm math parallel-processing

我实际上必须实现一个字符串比较,我在最后得到一个匹配的百分比(不仅仅是布尔结果匹配/不匹配)。所以,为此,我发现了Levenstein距离算法。但现在问题在于表现。例如,我有1k个字符串可以相互比较,现在大约需要10分钟。对于每一个我已经并行调用算法,并且在每个算法中并行完成。 所以我得到了伪语言:

Foreach strings
    Call in parallel the comparaison method.

在比较法中

Foreach stringsToCompare
    Call in parallel the Levenstein Distance algo.

在i5 @ 2.6Ghz上,100%的CPU占用率仍然是10分钟......

这是我的实施

public static double GetSimilarity(string firstString, string secondString)
{
    if (ReferenceEquals(firstString, null)) throw new ArgumentNullException("firstString");
    if (ReferenceEquals(secondString, null)) throw new ArgumentNullException("secondString");
    if (firstString == secondString) return 100;

    return (1 - GetLevensteinDistance(firstString, secondString) / (double)Math.Max(firstString.Length, secondString.Length)) * 100;
}

private static int GetLevensteinDistance(string firstString, string secondString)
{
    if (ReferenceEquals(firstString, null)) throw new ArgumentNullException("firstString");
    if (ReferenceEquals(secondString, null)) throw new ArgumentNullException("secondString");
    if (firstString == secondString) return 1;

    int[,] matrix = new int[firstString.Length + 1, secondString.Length + 1];

    for (int i = 0; i <= firstString.Length; i++)
        matrix[i, 0] = i; // deletion
    for (int j = 0; j <= secondString.Length; j++)
        matrix[0, j] = j; // insertion

    for (int i = 0; i < firstString.Length; i++)
        for (int j = 0; j < secondString.Length; j++)
            if (firstString[i] == secondString[j])
                matrix[i + 1, j + 1] = matrix[i, j];
            else
            {
                matrix[i + 1, j + 1] = Math.Min(matrix[i, j + 1] + 1, matrix[i + 1, j] + 1); //deletion or insertion
                matrix[i + 1, j + 1] = Math.Min(matrix[i + 1, j + 1], matrix[i, j] + 1); //substitution
            }
    return matrix[firstString.Length, secondString.Length];
}

那么你知道一个类似的算法可能更适合长文本比较或高度并行化吗?

2 个答案:

答案 0 :(得分:4)

您实际在做的是使用Needleman-Wunsch(NW)算法吗? NW算法基于计算DP矩阵,其中每个场依赖于3个相邻场:左侧,左侧和顶部。因此,它是逐行解决的。
但是,如果您想并行化它,那么最常见的一个想法是通过antidiagonals解决DP矩阵。这样,您就可以独立计算反对角线中的每个场。

这就是你的函数getLevensetinDistance现在如何工作的方式:你逐行计算,这意味着你必须逐字段地计算并且不能进行并行化,如图所示:

enter image description here

您需要更改函数getLevesteinDistance才能并行化它。这是我前面描述的反对角线思想的图片,其中反对角线中的每个字段都可以独立计算,这意味着你可以进行并行化(具有相同数字的字段可以并行计算):

enter image description here

您能解释一下如何并行调用算法?因为你的函数getLevensteinDistance()接受了两个字符串,所以在并行调用它时没有任何意义,除非比较多对字符串,但是你已经提到你并行调用函数compare()。

顺便说一下,应该是Levenshtein,而不是Levenstein :)。

另外,我实际上最终为Levenshtein距离实现了一个C / C ++库,这是更长字符串的最快实现之一,你可以在这里查看:https://github.com/Martinsos/edlib - 也许这比实现你的更好拥有,虽然它只适用于一个线程(但你可以自己在多个线程上运行它)。

答案 1 :(得分:0)

我找到了一个名为SimMetrics的supa dupa库,其中包含很多相似度算法的实现,当我对它们进行基准测试时,在我的情况下有一些非常有用的更快。

如果您也感兴趣:http://sourceforge.net/projects/simmetrics/