2009-06-23 24 views
53

Dados dos cadenas text1 y text2¿Cómo puedo medir la similitud entre 2 cadenas?

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

Ejemplos:

  1. primera cadena: StackOverflow

    segunda cadena: StaqOverflow

    de devolución: La similitud es del 91%

    El retorno puede ser en% o algo así.

  2. primera cadena: El texto simple análisis de

    segunda cadena: El texto complejo prueba

    de devolución: Los valores pueden ser considerados iguales

¿Alguna idea? ¿Cuál es la mejor manera de hacer esto?

+8

¿Por qué crees que las dos cadenas en el ejemplo 2 deben ser iguales? –

+0

¿Me falta algo aquí? ¿Dio el cartel original alguna indicación de que estaba interesado en la fonética, más que en los personajes, aparte del hecho de que el primer ejemplo podría implicar una similitud fonética? El segundo ejemplo ciertamente no. –

+0

Supongo que "Similitud" y "Fonética" son las más cercanas, pero son cosas diferentes. La validación de "Similitud" necesita usar un algoritmo "fonético" y un algoritmo de "similitud" para validar correctamente un texto. – Zanoni

Respuesta

40

Existen varias formas diferentes de hacerlo. Eche un vistazo a Wikipedia "String similarity measures" page para enlaces a otras páginas con algoritmos.

No creo cualquiera de esos algoritmos toman suena en cuenta, sin embargo - por lo que "desbordamiento STAQ" sería lo más parecido a "desbordamiento de pila" como "desbordamiento staw" a pesar del primer ser más similares en términos de pronunciación.

Acabo de encontrar another page que da más opciones ... en particular, el algoritmo Soundex (Wikipedia) puede estar más cerca de lo que buscas.

+8

FYI, si trabaja con los datos con SQL Server, tiene una función SOUNDEX(). –

+2

Además, debe tenerse en cuenta que Soundex es un viejo algoritmo destinado principalmente a las palabras en inglés. Si desea un algoritmo moderno multilingüe, considere usar Metaphone. Este artículo analiza las diferencias en mayor detalle: http://www.informit.com/articles/article.aspx?p=1848528 – Seanny123

26

Levenshtein distance es probablemente lo que estás buscando.

+0

Aquí hay un gran ejemplo de cómo escribirlo, tengo que amar DotNetPerls. http://www.dotnetperls.com/levenshtein – jamesbar2

4

Para tratar con "sonidos similares" es posible que desee buscar en la codificación utilizando un algoritmo fonético como Double Metaphone o soundex. No sé si computar las distancias de Levenshtein en cadenas fonéticas codificadas sería beneficioso o no, pero podría ser una posibilidad. Alternativamente, podría usar una heurística como: convierta cada palabra de la cadena a su forma codificada y elimine las palabras que aparecen en ambas cadenas y reemplácelas con una sola representación antes de calcular la distancia de Levenshtein.

+0

El algoritmo soundex es utilizado por la comunidad médica para advertir acerca de cómo suenan los apellidos. Está incluido en muchas bibliotecas estándar. – slypete

+1

Metaphone es muy superior a Soundex. –

3

El módulo Perl Text::Phonetic tiene implementaciones de varios algoritmos.

2

Si está comparando valores en una base de datos SQL, puede usar la función SOUNDEX. Si consulta Google por SOUNDEX y C#, algunas personas han escrito una función similar para eso y para VB.

5

Escribí hace un tiempo Double Metaphone implementation in C#. Lo encontrarás muy superior a Soundex y similares.

Levenshtein distance también se ha sugerido, y es un gran algoritmo para muchos usos, pero el emparejamiento fonético no es realmente lo que hace; a veces solo parece así porque las palabras fonéticamente similares también suelen escribirse de manera similar. Hice un analysis of various fuzzy matching algorithms que también podría serle útil.

13

Aquí hay un código que he escrito para un proyecto que estoy trabajando. Necesito saber la relación de similitud de las cadenas y la relación de similitud en función de las palabras de las cadenas. Este último, quiero saber tanto la relación de similitud de palabras de la cadena más pequeña (si todas las palabras existen y coinciden en la cadena más grande el resultado será 100%) y la relación de similitud de palabras de la cadena más grande (que llamo RealWordsRatio). Uso el algoritmo de Levenshtein para encontrar la distancia. El código no está optimizado, hasta el momento, pero funciona como se esperaba. Espero que le sea útil.

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

Metaphone 3 es la tercera generación del algoritmo Metaphone. Se aumenta la precisión de codificación fonética del 89% de Doble Metaphone a 98%, como prueba contra una base de datos de los palabras más comunes en inglés, y los nombres y palabras no inglesas conocidas en el norte de América del . Esto produce una codificación fonética extremadamente confiable para pronunciaciones estadounidenses.

Metaphone 3 fue diseñado y desarrollado por Lawrence Philips, que diseñó y desarrolló los algoritmos originales Metaphone y Double Metaphone .

Cuestiones relacionadas