Как я могу измерить сходство двух струн?

Даны две строки text1 и text2:

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

Примеры:

  1. Первая строка: StackOverflow

    Вторая строка: StaqOverflow

    Доходность: сходство 91%

    Возврат может быть в% или что-то в этом роде.

  2. Первая строка: простой текстовый тест

    Вторая строка: сложный текстовый тест

    Результат: значения можно считать равными

Любые идеи? Как лучше всего это сделать?


person Zanoni    schedule 23.06.2009    source источник
comment
Как вы думаете, почему две строки в примере 2 должны сравниваться как равные?   -  person Sinan Ünür    schedule 24.06.2009
comment
Я что-то упустил? Дает ли исходный плакат какое-либо указание на то, что он был озабочен фонетикой, а не персонажами, кроме того факта, что первый пример может подразумевать фонетическое сходство? Второй пример, конечно же, нет.   -  person Kevin Crumley    schedule 24.06.2009
comment
Думаю, что «Сходство» и «Фонетика» ближе всего, но это разные вещи. Проверка подобия должна использовать фонетический алгоритм и алгоритм подобия для правильной проверки текста.   -  person Zanoni    schedule 24.06.2009
comment
@kcrumley: Второй пример - гипотетический. Строки, имеющие некоторое сходство для каждого найденного слова, можно считать похожими.   -  person Zanoni    schedule 24.06.2009


Ответы (12)


Это можно сделать разными способами. Посетите страницу Википедии «Меры сходства строк» ​​, чтобы найти ссылки на другие страницы с алгоритмами. .

Однако я не думаю, что какой-либо из этих алгоритмов учитывает звуки - поэтому «переполнение стека» будет таким же похоже на «переполнение стека», как «переполнение стека», несмотря на то, что первый более похож по терминам. произношения.

Я только что нашел другую страницу, которая дает гораздо больше возможностей .. . в частности, алгоритм Soundex (Wikipedia) может быть ближе к тому, что вам нужно.

person Jon Skeet    schedule 23.06.2009
comment
К вашему сведению, если вы работаете с данными с помощью SQL Server, у него есть функция SOUNDEX (). - person Paul Draper; 02.04.2013
comment
Также следует отметить, что Soundex - это старый алгоритм, предназначенный в основном для английских слов. Если вам нужен современный многоязычный алгоритм, подумайте об использовании Metaphone. В этой статье различия обсуждаются более подробно: informit.com/articles/article.aspx ? p = 1848528 - person Seanny123; 05.11.2013

Возможно, вы ищете расстояние Левенштейна.

person LiraNuna    schedule 23.06.2009
comment
Вот отличный пример того, как это писать, надо любить DotNetPerls. dotnetperls.com/levenshtein - person jamesbar2; 12.09.2013

Вот код, который я написал для проекта, над которым работаю. Мне нужно знать коэффициент схожести строк и коэффициент схожести на основе слов в строках. В этом последнем случае я хочу знать коэффициент схожести слов для наименьшей строки (поэтому, если все слова существуют и совпадают в более крупной строке, результат будет 100%), и коэффициент схожести слов для большей строки (которую я называю RealWordsRatio ). Я использую алгоритм Левенштейна, чтобы найти расстояние. Код пока не оптимизирован, но работает как положено. Надеюсь, вы сочтете это полезным.

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;
    }
person ThunderGr    schedule 22.06.2012

Некоторое время назад я написал реализацию Double Metaphone на C #. Вы обнаружите, что он значительно превосходит Soundex и тому подобное.

Также предлагалось расстояние Левенштейна, и это отличный алгоритм для множества применений, но на самом деле фонетическое сопоставление - это не то, что он делает; это только иногда кажется так, потому что фонетически похожие слова также обычно пишутся одинаково. Я провел анализ различных алгоритмов нечеткого сопоставления, который также может оказаться полезным.

person anelson    schedule 25.06.2009

Чтобы справиться со «подобными звуками», вы можете захотеть изучить кодирование с использованием фонетического алгоритма, такого как Double Metaphone или soundex. Я не знаю, будет ли полезно вычисление расстояний Левенштейна на строках с фонетической кодировкой, но это возможно. В качестве альтернативы вы можете использовать эвристику, например: преобразовать каждое слово в строке в его закодированную форму и удалить все слова, встречающиеся в обеих строках, и заменить их одним представлением перед вычислением расстояния Левенштейна.

person bdk    schedule 23.06.2009
comment
Алгоритм soundex используется медицинским сообществом для предупреждения о похожих фамилиях. Он включен во многие стандартные библиотеки. - person slypete; 23.06.2009
comment
Метафон намного превосходит Soundex. - person Dour High Arch; 24.06.2009

Вы можете искать строку «расстояния», например расстояние Левенштейна.

person schnaader    schedule 23.06.2009

Модуль Perl Text :: Phonetic имеет реализации различных алгоритмов.

person Sinan Ünür    schedule 23.06.2009

Джефф Этвуд писал о поиске аналогичного решения для определения авторства сообщений вики. что может помочь вам сузить область поиска.

person John Sheehan    schedule 23.06.2009

Если вы сравниваете значения в базе данных SQL, вы можете использовать функцию SOUNDEX. Если вы запросите Google для SOUNDEX и C #, некоторые люди написали аналогичную функцию для этого и VB.

person Rob    schedule 24.06.2009

Я тоже должен порекомендовать Soundex, я использовал его в прошлом для обработки названий городов с ошибками. Вот хорошая ссылка для использования: http://whitepapers.zdnet.com/abstract.aspx?docid=352953

person Community    schedule 24.06.2009

Если вы хотите сравнить фонетически, ознакомьтесь с алгоритмами Soundex и Metaphone: http://www.blackbeltcoder.com/Articles/algorithms/phonetic-string-comparison-with-soundex.

person Jonathan Wood    schedule 14.01.2011

Метафон 3 - это третье поколение алгоритма Метафон. Он увеличивает точность фонетического кодирования с 89% двойного метафона до 98%, что подтверждается проверкой на базе данных наиболее распространенных английских слов, а также имен и неанглийских слов, известных в Северной Америке. Это обеспечивает чрезвычайно надежное фонетическое кодирование американского произношения.

Metaphone 3 был разработан и разработан Лоуренсом Филипсом, который спроектировал и разработал оригинальные алгоритмы Metaphone и Double Metaphone.

person bjan    schedule 06.02.2013