web-dev-qa-db-fra.com

Comment puis-je mesurer la similitude entre 2 chaînes?

Étant donné deux chaînes text1 et text2

public SOMEUSABLERETURNTYPE Compare(string text1, string text2)
{
     // DO SOMETHING HERE TO COMPARE
}

Exemples:

  1. Première chaîne: StackOverflow

    Deuxième chaîne: StaqOverflow

    Retour: la similitude est de 91%

    Le retour peut être en% ou quelque chose comme ça.

  2. Première chaîne: le test de texte simple

    Deuxième chaîne: le test de texte complexe

    Retour: les valeurs peuvent être considérées comme égales

Des idées? Quelle est la meilleure façon de procéder?

55
Zanoni

Il existe différentes façons de procéder. Jetez un œil à la page Wikipedia "Mesures de similarité des chaînes" pour les liens vers d'autres pages avec des algorithmes.

Je ne pense pas que l'un de ces algorithmes prenne en considération les sons, cependant - donc "débordement staq" serait aussi similaire à "débordement de pile" que " staw overflow "bien que le premier soit plus similaire en termes de prononciation.

Je viens de trouver ne autre page qui donne un peu plus d'options ... en particulier, l'algorithme Soundex ( Wikipedia ) peut être plus proche de ce que vous êtes après.

42
Jon Skeet

distance Levenshtein est probablement ce que vous recherchez.

27
LiraNuna

Voici un code que j'ai écrit pour un projet sur lequel je travaille. J'ai besoin de connaître le rapport de similarité des chaînes et le rapport de similarité basé sur les mots des chaînes. Ce dernier, je veux connaître à la fois le rapport de similarité des mots de la plus petite chaîne (donc si tous les mots existent et correspondent dans la plus grande chaîne, le résultat sera 100%) et le rapport de similarité des mots de la plus grande chaîne (que j'appelle RealWordsRatio ). J'utilise l'algorithme Levenshtein pour trouver la distance. Jusqu'à présent, le code n'est pas optimisé, mais il fonctionne comme prévu. J'espère que tu trouves cela utile.

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

double GetSimilarityRatio(String FullString1, String FullString2, out double WordsRatio, out double RealWordsRatio)
    {
        double theResult = 0;
        String[] Splitted1 = FullString1.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
        String[] Splitted2 = FullString2.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
        if (Splitted1.Length < Splitted2.Length)
        {
            String[] Temp = Splitted2;
            Splitted2 = Splitted1;
            Splitted1 = Temp;
        }
        int[,] theScores = new int[Splitted1.Length, Splitted2.Length];//Keep the best scores for each Word.0 is the best, 1000 is the starting.
        int[] BestWord = new int[Splitted1.Length];//Index to the best Word of Splitted2 for the Splitted1.

        for (int loop = 0; loop < Splitted1.Length; loop++) 
        {
            for (int loop1 = 0; loop1 < Splitted2.Length; loop1++) theScores[loop, loop1] = 1000;
            BestWord[loop] = -1;
        }
        int WordsMatched = 0;
        for (int loop = 0; loop < Splitted1.Length; loop++)
        {
            String String1 = Splitted1[loop];
            for (int loop1 = 0; loop1 < Splitted2.Length; loop1++)
            {
                String String2 = Splitted2[loop1];
                int LevenshteinDistance = Compute(String1, String2);
                theScores[loop, loop1] = LevenshteinDistance;
                if (BestWord[loop] == -1 || theScores[loop, BestWord[loop]] > LevenshteinDistance) BestWord[loop] = loop1;
            }
        }

        for (int loop = 0; loop < Splitted1.Length; loop++)
        {
            if (theScores[loop, BestWord[loop]] == 1000) continue;
            for (int loop1 = loop + 1; loop1 < Splitted1.Length; loop1++)
            {
                if (theScores[loop1, BestWord[loop1]] == 1000) continue;//the worst score available, so there are no more words left
                if (BestWord[loop] == BestWord[loop1])//2 words have the same best Word
                {
                    //The first in order has the advantage of keeping the Word in equality
                    if (theScores[loop, BestWord[loop]] <= theScores[loop1, BestWord[loop1]])
                    {
                        theScores[loop1, BestWord[loop1]] = 1000;
                        int CurrentBest = -1;
                        int CurrentScore = 1000;
                        for (int loop2 = 0; loop2 < Splitted2.Length; loop2++)
                        {
                            //Find next bestword
                            if (CurrentBest == -1 || CurrentScore > theScores[loop1, loop2])
                            {
                                CurrentBest = loop2;
                                CurrentScore = theScores[loop1, loop2];
                            }
                        }
                        BestWord[loop1] = CurrentBest;
                    }
                    else//the latter has a better score
                    {
                        theScores[loop, BestWord[loop]] = 1000;
                        int CurrentBest = -1;
                        int CurrentScore = 1000;
                        for (int loop2 = 0; loop2 < Splitted2.Length; loop2++)
                        {
                            //Find next bestword
                            if (CurrentBest == -1 || CurrentScore > theScores[loop, loop2])
                            {
                                CurrentBest = loop2;
                                CurrentScore = theScores[loop, loop2];
                            }
                        }
                        BestWord[loop] = CurrentBest;
                    }

                    loop = -1;
                    break;//recalculate all
                }
            }
        }
        for (int loop = 0; loop < Splitted1.Length; loop++)
        {
            if (theScores[loop, BestWord[loop]] == 1000) theResult += Splitted1[loop].Length;//All words without a score for best Word are max failures
            else
            {
                theResult += theScores[loop, BestWord[loop]];
                if (theScores[loop, BestWord[loop]] == 0) WordsMatched++;
            }
        }
        int theLength = (FullString1.Replace(" ", "").Length > FullString2.Replace(" ", "").Length) ? FullString1.Replace(" ", "").Length : FullString2.Replace(" ", "").Length;
        if(theResult > theLength) theResult = theLength;
        theResult = (1 - (theResult / theLength)) * 100;
        WordsRatio = ((double)WordsMatched / (double)Splitted2.Length) * 100;
        RealWordsRatio = ((double)WordsMatched / (double)Splitted1.Length) * 100;
        return theResult;
    }
14
ThunderGr

J'ai écrit un implémentation Double Metaphone en C # il y a quelque temps. Vous le trouverez largement supérieur à Soundex et similaires.

La distance de Levenshtein a également été suggérée, et c'est un excellent algorithme pour de nombreuses utilisations, mais la correspondance phonétique n'est pas vraiment ce qu'elle fait; cela ne semble parfois que parce que des mots phonétiquement similaires sont également généralement orthographiés de la même manière. J'ai fait un analyse de divers algorithmes de correspondance floue que vous pourriez également trouver utile.

5
anelson

Pour gérer les "sons similaires", vous voudrez peut-être étudier l'encodage à l'aide d'un algorithme phonétique comme Double Metaphone ou soundex. Je ne sais pas si le calcul des distances de Levenshtein sur des chaînes codées phonétiques serait bénéfique ou non, mais pourrait être une possibilité. Alternativement, vous pouvez utiliser une heuristique comme: convertir chaque mot de la chaîne en sa forme codée et supprimer tous les mots qui se produisent dans les deux chaînes et les remplacer par une seule représentation avant de calculer la distance Levenshtein.

4
bdk

Vous pouvez rechercher des "distances" de chaîne, par exemple la distance Levenshtein .

3
schnaader

Module Perl Text :: Phonetic a des implémentations de divers algorithmes.

3
Sinan Ünür

Jeff Atwood a écrit sur la recherche d'une solution similaire pour déterminer la paternité des articles wiki qui peuvent vous aider à affiner votre recherche.

2
John Sheehan

Si vous comparez des valeurs dans une base de données SQL, vous pouvez utiliser la fonction SOUNDEX . Si vous interrogez Google pour SOUNDEX et C #, certaines personnes ont écrit une fonction similaire pour cela et VB.

2
Rob

Si vous souhaitez comparer phonétiquement, consultez les algorithmes Soundex et Metaphone: http://www.blackbeltcoder.com/Articles/algorithms/phonetic-string-comparison-with-soundex

1
Jonathan Wood

Je dois également recommander Soundex, je l'ai utilisé dans le passé pour traiter les noms de villes mal orthographiés. Voici un bon lien pour l'utilisation: http://whitepapers.zdnet.com/abstract.aspx?docid=35295

1
pmatah

Metaphone 3 est la troisième génération de l'algorithme Metaphone. Il augmente la précision de l'encodage phonétique de 89% de Double Metaphone à 98%, comme testé par rapport à une base de données des mots anglais les plus courants, et des noms et des mots non anglais familiers en Amérique du Nord. Cela produit un codage phonétique extrêmement fiable pour les prononciations américaines.

Metaphone 3 a été conçu et développé par Lawrence Philips, qui a conçu et développé les algorithmes originaux Metaphone et Double Metaphone.

1
bjan