Question

J'ai l'implémentation suivante, mais je veux ajouter un seuil, donc si le résultat va être plus grand que lui, arrêtez simplement de calculer et de revenir.

Comment pourrais-je aller à ce sujet?

Edit: voici mon code actuel, threshold n'est pas encore utilisé ... l'objectif est qu'il est utilisé

    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)];
    }
}
Était-ce utile?

La solution 4

Finalement l'avoir ... bien que ce ne soit pas aussi bénéfique que je l'avais espéré

    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)];
    }

Autres conseils

Ceci est en ce qui concerne votre réponse: Damerau - Distance de Levenshtein, ajoutant un seuil(désolé je ne peux pas commenter car je n'ai pas encore 50 répétitions)

Je pense que vous avez fait une erreur ici. Vous avez initialisé:

var minDistance = threshold;

Et votre règle de mise à jour est:

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

De plus, vos critères de sortie précoces sont:

if (minDistance > threshold)
   return int.MaxValue;

Maintenant, observez que la condition IF ci-dessus ne tiendra jamais vrai! Vous devriez plutôt initialiser minDistance à int.MaxValue

Voici la manière la plus élégante à laquelle je peux penser. Après avoir réglé chaque index de D, voyez s'il dépasse votre seuil. L'évaluation est constante, c'est donc une baisse du seau par rapport à la complexité théorique n ^ 2 de l'algorithme global:

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)];
}

Vous avez également posé cela en tant que question SQL CLR UDF, je vais donc répondre dans ce contexte spécifique: vous la meilleure optmiziation ne proviendra pas de l'optimisation de la distance de Levenshtein, mais de la réduction du nombre de paires que vous comparez. Oui, un algorithme de Levenshtein plus rapide améliorera les choses, mais pas autant que la réduction du nombre de comparaisons de N carré (avec n dans les millions de lignes) à n * un facteur. Ma proposition est de comparer uniquement les éléments qui ont la différence de longueur dans un delta tolérable. Sur votre grande table, vous ajoutez une colonne calculée persistante sur LEN(Data) puis créer un index avec des données incluent:

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

Maintenant, vous pouvez restreindre l'espace de problème pur en rejoignant dans une différence maximale sur Lenght (par exemple, dire 5), Si vos données sont LEN(Data) varie considérablement:

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top