web-dev-qa-db-fra.com

Comparaison de chaînes avec tolérance

Je cherche un moyen de comparer une chaîne avec un tableau de chaînes. Bien sûr, faire une recherche exacte est assez facile, mais je veux que mon programme tolère les fautes d'orthographe, les parties manquantes de la chaîne, etc.

Existe-t-il une sorte de cadre qui peut effectuer une telle recherche? J'ai quelque chose à l'esprit que l'algorithme de recherche renverra un ordre de quelques résultats par le pourcentage de correspondance ou quelque chose comme ça.

57
Oliver Hanappi

Vous pouvez utiliser algorithme de distance de Levenshtein .

"La distance Levenshtein entre deux chaînes est définie comme le nombre minimum de modifications nécessaires pour transformer une chaîne en une autre, les opérations d'édition autorisées étant l'insertion, la suppression ou la substitution d'un seul caractère. " - Wikipedia.com

Celui-ci est de dotnetperls.com :

using System;

/// <summary>
/// Contains approximate string matching
/// </summary>
static class LevenshteinDistance
{
    /// <summary>
    /// Compute the distance between two strings.
    /// </summary>
    public static int Compute(string s, string t)
    {
        int n = s.Length;
        int m = t.Length;
        int[,] d = new int[n + 1, m + 1];

        // Step 1
        if (n == 0)
        {
            return m;
        }

        if (m == 0)
        {
            return n;
        }

        // Step 2
        for (int i = 0; i <= n; d[i, 0] = i++)
        {
        }

        for (int j = 0; j <= m; d[0, j] = j++)
        {
        }

        // Step 3
        for (int i = 1; i <= n; i++)
        {
            //Step 4
            for (int j = 1; j <= m; j++)
            {
                // Step 5
                int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;

                // Step 6
                d[i, j] = Math.Min(
                    Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
                    d[i - 1, j - 1] + cost);
            }
        }
        // Step 7
        return d[n, m];
    }
}

class Program
{
    static void Main()
    {
        Console.WriteLine(LevenshteinDistance.Compute("aunt", "ant"));
        Console.WriteLine(LevenshteinDistance.Compute("Sam", "Samantha"));
        Console.WriteLine(LevenshteinDistance.Compute("flomax", "volmax"));
    }
}

Vous pouvez en fait préférer utiliser algorithme de distance Damerau-Levenshtein , qui permet également de transposer les caractères, ce qui est une erreur humaine courante dans la saisie des données. Vous en trouverez une implémentation C # ici .

65
RedFilter

Il n'y a rien dans le framework .NET qui vous aidera avec ce prêt à l'emploi.

Les fautes d'orthographe les plus courantes sont celles où les lettres sont une représentation phonétique décente du mot, mais pas l'orthographe correcte du mot.

Par exemple, on pourrait soutenir que les mots sword et sord (oui, c'est un mot) ont les mêmes racines phonétiques (ils sonnent de la même manière lorsque vous les prononcez).

Cela étant dit, il existe un certain nombre d'algorithmes que vous pouvez utiliser pour traduire des mots (même mal orthographiés) en variantes phonétiques.

Le premier est Soundex . Il est assez simple à implémenter et il existe un bon nombre de implémentations .NET de cet algorithme . C'est assez simple, mais cela vous donne de vraies valeurs que vous pouvez comparer les unes aux autres.

Un autre est métaphone . Bien que je ne puisse pas trouver une implémentation native .NET de Metaphone, le lien fourni contient des liens vers un certain nombre d'autres implémentations qui pourraient être converties. Le plus simple à convertir serait probablement le implémentation Java de l'algorithme Metaphone .

Il convient de noter que l'algorithme du métaphone a subi des révisions. Il y a Double Metaphone (qui a une implémentation .NET ) et Metaphone . Metaphone 3 est une application commerciale, mais a un taux de précision de 98% par rapport à un taux de précision de 89% pour l'algorithme Double Metaphone lorsqu'il est exécuté sur une base de données de mots anglais courants. Selon votre besoin, vous souhaiterez peut-être rechercher (dans le cas de Double Metaphone) ou acheter (dans le cas de Metaphone 3) la source de l'algorithme et le convertir ou y accéder via la couche P/Invoke (il existe des implémentations C++ abonder).

Metaphone et Soundex diffèrent en ce sens que Soundex produit des touches numériques de longueur fixe, tandis que Metaphone produit des touches de longueur différente, donc les résultats seront différents. En fin de compte, les deux feront le même genre de comparaison pour vous, il vous suffit de trouver celui qui convient le mieux à vos besoins, compte tenu de vos besoins et de vos ressources (et des niveaux d'intolérance pour les fautes d'orthographe, bien sûr).

20
casperOne

Voici une implémentation de la méthode LevenshteinDistance qui utilise beaucoup moins de mémoire tout en produisant les mêmes résultats. Il s'agit d'une adaptation C # du pseudo-code trouvé dans cet article wikipedia sous l'en-tête "Itératif avec deux lignes de matrice".

public static int LevenshteinDistance(string source, string target)
{
    // degenerate cases
    if (source == target) return 0;
    if (source.Length == 0) return target.Length;
    if (target.Length == 0) return source.Length;

    // create two work vectors of integer distances
    int[] v0 = new int[target.Length + 1];
    int[] v1 = new int[target.Length + 1];

    // initialize v0 (the previous row of distances)
    // this row is A[0][i]: edit distance for an empty s
    // the distance is just the number of characters to delete from t
    for (int i = 0; i < v0.Length; i++)
        v0[i] = i;

    for (int i = 0; i < source.Length; i++)
    {
        // calculate v1 (current row distances) from the previous row v0

        // first element of v1 is A[i+1][0]
        //   edit distance is delete (i+1) chars from s to match empty t
        v1[0] = i + 1;

        // use formula to fill in the rest of the row
        for (int j = 0; j < target.Length; j++)
        {
            var cost = (source[i] == target[j]) ? 0 : 1;
            v1[j + 1] = Math.Min(v1[j] + 1, Math.Min(v0[j + 1] + 1, v0[j] + cost));
        }

        // copy v1 (current row) to v0 (previous row) for next iteration
        for (int j = 0; j < v0.Length; j++)
            v0[j] = v1[j];
    }

    return v1[target.Length];
}

Voici une fonction qui vous donnera le pourcentage de similitude.

/// <summary>
/// Calculate percentage similarity of two strings
/// <param name="source">Source String to Compare with</param>
/// <param name="target">Targeted String to Compare</param>
/// <returns>Return Similarity between two strings from 0 to 1.0</returns>
/// </summary>
public static double CalculateSimilarity(string source, string target)
{
    if ((source == null) || (target == null)) return 0.0;
    if ((source.Length == 0) || (target.Length == 0)) return 0.0;
    if (source == target) return 1.0;

    int stepsToSame = LevenshteinDistance(source, target);
    return (1.0 - ((double)stepsToSame / (double)Math.Max(source.Length, target.Length)));
}
5
Ben Gripka

Votre autre option est de comparer phonétiquement en utilisant Soundex ou Metaphone. Je viens de terminer un article qui présente le code C # pour les deux algorithmes. Vous pouvez le consulter sur http://www.blackbeltcoder.com/Articles/algorithms/phonetic-string-comparison-with-soundex .

4
Jonathan Wood

Voici deux méthodes qui calculent la Levenshtein Distance entre les chaînes.

La distance Levenshtein entre deux chaînes est définie comme le nombre minimum de modifications nécessaires pour transformer une chaîne en l'autre, les opérations d'édition autorisées étant l'insertion, la suppression ou la substitution d'un seul caractère.

Une fois le résultat obtenu, vous devrez définir la valeur que vous souhaitez utiliser comme seuil pour une correspondance ou non. Exécutez la fonction sur un tas d'exemples de données pour avoir une bonne idée de la façon dont cela fonctionne pour vous aider à décider de votre seuil particulier.

    /// <summary>
    /// Calculates the Levenshtein distance between two strings--the number of changes that need to be made for the first string to become the second.
    /// </summary>
    /// <param name="first">The first string, used as a source.</param>
    /// <param name="second">The second string, used as a target.</param>
    /// <returns>The number of changes that need to be made to convert the first string to the second.</returns>
    /// <remarks>
    /// From http://www.merriampark.com/ldcsharp.htm
    /// </remarks>
    public static int LevenshteinDistance(string first, string second)
    {
        if (first == null)
        {
            throw new ArgumentNullException("first");
        }
        if (second == null)
        {
            throw new ArgumentNullException("second");
        }

        int n = first.Length;
        int m = second.Length;
        var d = new int[n + 1, m + 1]; // matrix

        if (n == 0) return m;
        if (m == 0) return n;

        for (int i = 0; i <= n; d[i, 0] = i++)
        {
        }

        for (int j = 0; j <= m; d[0, j] = j++)
        {
        }

        for (int i = 1; i <= n; i++)
        {

            for (int j = 1; j <= m; j++)
            {
                int cost = (second.Substring(j - 1, 1) == first.Substring(i - 1, 1) ? 0 : 1); // cost
                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[n, m];
    }
3
Samuel Neff

Vous pouvez trouver des implémentations de soundex et des algorithmes de distance levenshtein dans l'open source projet CommonLibrary.NET .

2
Elijah