2009-06-23 14 views
53

Date due stringhe text1 e text2Come posso misurare la somiglianza tra 2 stringhe?

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

Esempi:

  1. prima stringa: StackOverflow

    seconda stringa: StaqOverflow

    ritorno: somiglianza è del 91%

    Il ritorno può essere in% o qualcosa del genere.

  2. prima stringa: La semplice testo di prova

    seconda stringa: Il testo complesso di prova

    di ritorno: I valori possono essere considerati uguali

Tutte le idee? Qual è il modo migliore per farlo?

+8

Perché pensi che le due stringhe nell'esempio 2 debbano essere uguali? –

+0

Mi manca qualcosa qui? Il poster originale indicava che si trattava di fonetica, piuttosto che dei personaggi, a parte il fatto che il primo esempio poteva essere considerato implicante la somiglianza fonetica? Il secondo esempio certamente non lo è. –

+0

Immagino che "Similarity" e "Phonetic" siano i più vicini, ma sono cose diverse. La convalida "Similarity" deve utilizzare un algoritmo "fonetico" e un algoritmo di "similarità" per convalidare correttamente un testo. – Zanoni

risposta

40

Ci sono vari modi per farlo. Dai uno sguardo allo Wikipedia "String similarity measures" page per i link ad altre pagine con algoritmi.

non penso uno qualsiasi di questi algoritmi prendono in considerazione i suoni, tuttavia - in modo "staq overflow" sarebbe più simile a "overflow dello stack" come "troppo pieno staw", nonostante il primo è più simile in termini di pronuncia.

Ho appena trovato another page che offre molte più opzioni ... in particolare, l'algoritmo Soundex (Wikipedia) potrebbe essere più vicino a quello che stai cercando.

+8

FYI, se ti capita di lavorare con i dati con SQL Server, ha una funzione SOUNDEX(). –

+2

Inoltre, va notato che Soundex è un vecchio algoritmo inteso principalmente per le parole inglesi. Se si desidera un algoritmo multilingue multilingue, prendere in considerazione l'utilizzo di Metaphone. Questo articolo discute le differenze in modo più dettagliato: http://www.informit.com/articles/article.aspx?p=1848528 – Seanny123

26

Levenshtein distance è probabilmente quello che stai cercando.

+0

Ecco un ottimo esempio di come scriverlo, devo amare DotNetPerls. http://www.dotnetperls.com/levenshtein – jamesbar2

4

Per gestire "suoni simili", è possibile esaminare la codifica utilizzando un algoritmo fonetico come Double Metaphone o soundex. Non so se calcolare le distanze di Levenshtein su stringhe codificate fonetiche sarebbe vantaggioso o meno, ma potrebbe essere una possibilità. In alternativa, è possibile utilizzare un euristico come: convertire ogni parola nella stringa nella sua forma codificata e rimuovere le parole che si verificano in entrambe le stringhe e sostituirle con una singola rappresentazione prima di calcolare la distanza di Levenshtein.

+0

L'algoritmo soundex viene utilizzato dalla comunità medica per mettere in guardia su come pronunciare cognomi. È incluso in molte librerie standard. – slypete

+1

Il metaphone è di gran lunga superiore a quello di Soundex. –

2

Se si confrontano valori in un database SQL, è possibile utilizzare la funzione SOUNDEX. Se interroghi Google per SOUNDEX e C#, alcune persone hanno scritto una funzione simile per questo e VB.

5

Ho scritto un Double Metaphone implementation in C# qualche tempo fa. Lo troverai enormemente superiore a Soundex e simili.

La distanza di Levenshtein è stata anche suggerita, ed è un algoritmo eccezionale per molti usi, ma la corrispondenza fonetica non è esattamente ciò che fa; a volte sembra così a volte perché le parole foneticamente simili sono di solito scritte in modo simile. Ho fatto uno analysis of various fuzzy matching algorithms che potresti trovare utile.

13

Ecco il codice che ho scritto per un progetto cui sto lavorando. Ho bisogno di conoscere il rapporto di somiglianza delle stringhe e il rapporto di somiglianza basato sulle parole delle stringhe. Quest'ultimo, voglio conoscere sia il rapporto di similarità parole della stringa più piccola (quindi se tutte le parole esistono e corrispondono nella stringa più grande il risultato sarà 100%) e il rapporto di similarità parole della stringa più grande (che io chiamo RealWordsRatio). Uso l'algoritmo Levenshtein per trovare la distanza. Il codice non è ottimizzato, finora, ma funziona come previsto. Spero che lo trovi 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; 
    } 
1

Metaphone 3 è la terza generazione dell'algoritmo Metaphone. E aumenta la precisione della codifica fonetica dal 89% di Double Metaphone al 98%, come testato con un database di più comuni parole in inglese, e nomi e le parole non in lingua inglese familiari in Nord America. Questo produce una codifica fonetica estremamente affidabile per le pronunce americane .

Metaphone 3 è stato progettato e sviluppato da Lawrence Philips, che ha progettato e sviluppato gli algoritmi Metaphone e Double Metaphone originali .

Problemi correlati