Дамерау - Левенштейн Расстояние, добавляя порог

StackOverflow https://stackoverflow.com/questions/3841507

Вопрос

У меня есть следующая реализация, но я хочу добавить порог, поэтому, если результат будет больше, чем он, просто прекратите расчет и возврат.

Как бы я пошел об этом?

Редактировать: вот мой текущий код, threshold еще не использован ... целью является то, что она используется

    public static int DamerauLevenshteinDistance(string string1, string string2, int threshold)
    {
        // Return trivial case - where they are equal
        if (string1.Equals(string2))
            return 0;

        // Return trivial case - where one is empty
        if (String.IsNullOrEmpty(string1) || String.IsNullOrEmpty(string2))
            return (string1 ?? "").Length + (string2 ?? "").Length;


        // Ensure string2 (inner cycle) is longer
        if (string1.Length > string2.Length)
        {
            var tmp = string1;
            string1 = string2;
            string2 = tmp;
        }

        // Return trivial case - where string1 is contained within string2
        if (string2.Contains(string1))
            return string2.Length - string1.Length;

        var length1 = string1.Length;
        var length2 = string2.Length;

        var d = new int[length1 + 1, length2 + 1];

        for (var i = 0; i <= d.GetUpperBound(0); i++)
            d[i, 0] = i;

        for (var i = 0; i <= d.GetUpperBound(1); i++)
            d[0, i] = i;

        for (var i = 1; i <= d.GetUpperBound(0); i++)
        {
            for (var j = 1; j <= d.GetUpperBound(1); j++)
            {
                var cost = string1[i - 1] == string2[j - 1] ? 0 : 1;

                var del = d[i - 1, j] + 1;
                var ins = d[i, j - 1] + 1;
                var sub = d[i - 1, j - 1] + cost;

                d[i, j] = Math.Min(del, Math.Min(ins, sub));

                if (i > 1 && j > 1 && string1[i - 1] == string2[j - 2] && string1[i - 2] == string2[j - 1])
                    d[i, j] = Math.Min(d[i, j], d[i - 2, j - 2] + cost);
            }
        }

        return d[d.GetUpperBound(0), d.GetUpperBound(1)];
    }
}
Это было полезно?

Решение 4

Наконец-то получил ... Хотя это не так полезно, как я надеялся

    public static int DamerauLevenshteinDistance(string string1, string string2, int threshold)
    {
        // Return trivial case - where they are equal
        if (string1.Equals(string2))
            return 0;

        // Return trivial case - where one is empty
        if (String.IsNullOrEmpty(string1) || String.IsNullOrEmpty(string2))
            return (string1 ?? "").Length + (string2 ?? "").Length;


        // Ensure string2 (inner cycle) is longer
        if (string1.Length > string2.Length)
        {
            var tmp = string1;
            string1 = string2;
            string2 = tmp;
        }

        // Return trivial case - where string1 is contained within string2
        if (string2.Contains(string1))
            return string2.Length - string1.Length;

        var length1 = string1.Length;
        var length2 = string2.Length;

        var d = new int[length1 + 1, length2 + 1];

        for (var i = 0; i <= d.GetUpperBound(0); i++)
            d[i, 0] = i;

        for (var i = 0; i <= d.GetUpperBound(1); i++)
            d[0, i] = i;

        for (var i = 1; i <= d.GetUpperBound(0); i++)
        {
            var im1 = i - 1;
            var im2 = i - 2;
            var minDistance = threshold;

            for (var j = 1; j <= d.GetUpperBound(1); j++)
            {
                var jm1 = j - 1;
                var jm2 = j - 2;
                var cost = string1[im1] == string2[jm1] ? 0 : 1;

                var del = d[im1, j] + 1;
                var ins = d[i, jm1] + 1;
                var sub = d[im1, jm1] + cost;

                //Math.Min is slower than native code
                //d[i, j] = Math.Min(del, Math.Min(ins, sub));
                d[i, j] = del <= ins && del <= sub ? del : ins <= sub ? ins : sub;

                if (i > 1 && j > 1 && string1[im1] == string2[jm2] && string1[im2] == string2[jm1])
                    d[i, j] = Math.Min(d[i, j], d[im2, jm2] + cost);

                if (d[i, j] < minDistance)
                    minDistance = d[i, j];
            }

            if (minDistance > threshold)
                return int.MaxValue;
        }

        return d[d.GetUpperBound(0), d.GetUpperBound(1)] > threshold 
            ? int.MaxValue 
            : d[d.GetUpperBound(0), d.GetUpperBound(1)];
    }

Другие советы

Это относится к вашему ответу это: Дамерау - Левенштейн Расстояние, добавляя порог(Извините, не могу прокомментировать, как у меня еще нет 50 повторений)

Я думаю, что вы сделали ошибку здесь. Вы инициализировали:

var minDistance = threshold;

И ваше правило обновления:

if (d[i, j] < minDistance)
   minDistance = d[i, j];

Кроме того, ваши досрочные критерии выхода:

if (minDistance > threshold)
   return int.MaxValue;

Сейчас соблюдайте, что если выше, если выше, никогда не удержит! Вы предпочитаете инициализировать minDistance к int.MaxValue

Вот самый элегантный способ, которым я могу думать. После установки каждого индекса D см. Если он превышает ваш порог. Оценка - это постоянное время, поэтому это капля в ведро по сравнению с теоретическими N ^ 2 сложностью общего алгоритма:

public static int DamerauLevenshteinDistance(string string1, string string2, int threshold)
{
    ...

    for (var i = 1; i <= d.GetUpperBound(0); i++)
    {
        for (var j = 1; j <= d.GetUpperBound(1); j++)
        {
            ...

            var temp = d[i,j] = Math.Min(del, Math.Min(ins, sub));

            if (i > 1 && j > 1 && string1[i - 1] == string2[j - 2] && string1[i - 2] == string2[j - 1])
                temp = d[i,j] = Math.Min(temp, d[i - 2, j - 2] + cost);

            //Does this value exceed your threshold? if so, get out now
            if(temp > threshold) 
              return temp;
        }
    }

    return d[d.GetUpperBound(0), d.GetUpperBound(1)];
}

Вы также задавали это в качестве вопроса SQL CLR UDF, поэтому я отвечу в этом конкретном контексте: вы лучшее оптимизация не придет от оптимизации расстояния Левенштейна, но от уменьшения количества пар, которые вы сравниваете. Да, более быстрый алгоритм Левенштейна улучшит вещи, но не так много, как уменьшая количество сравнений от n квадрата (с n в миллионах строк) до n * некоторый фактор. Мое предложение состоит в том, чтобы сравнить только элементы, которые имеют разницу длины в пределах достойной дельты. На вашем большом столе вы добавляете сохраняемый компьютерный столбец на LEN(Data) а затем создать индекс на него с включенными данными:

ALTER TABLE Table ADD LenData AS LEN(Data) PERSISTED;
CREATE INDEX ndxTableLenData on Table(LenData) INCLUDE (Data);

Теперь вы можете ограничить простое проблемное пространство, присоединившись к максимальной разнице на Lenght (например, сказать 5), Если ваши данные LEN(Data) варьируется значительно:

SELECT a.Data, b.Data, dbo.Levenshtein(a.Data, b.Data)
FROM Table A
JOIN Table B ON B.DataLen BETWEEN A.DataLen - 5 AND A.DataLen+5
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top