【问题标题】:How can I measure the similarity between 2 strings? [closed]如何测量 2 个字符串之间的相似度? [关闭]
【发布时间】:2021-03-13 10:56:43
【问题描述】:

给定两个字符串text1text2

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

例子:

  1. 第一个字符串:堆栈溢出

    第二个字符串:StaqOverflow

    返回:相似度为 91%

    回报可以是 % 或类似的东西。

  2. 第一个字符串:简单的文本测试

    第二个字符串:复杂的文本测试

    返回:值可以认为相等

有什么想法吗?最好的方法是什么?

【问题讨论】:

  • 为什么您认为示例 2 中的两个字符串应该比较相等?
  • 我在这里遗漏了什么吗?原始发帖人是否表明他关心的是语音而不是字符,除了第一个例子可以被视为暗示语音相似性的事实?第二个例子当然没有。
  • 我猜“Similarity”和“Phonetic”是最接近的,但是是不同的东西。 “相似度”验证需要使用“语音”算法和“相似度”算法来正确验证文本。
  • @kcrumley:第二个例子只是假设性的。每个找到的单词都有一些相似性的字符串,可以认为是相似的。

标签: c# string comparison phonetics


【解决方案1】:

有多种不同的方法可以做到这一点。查看Wikipedia "String similarity measures" page 以获取到其他具有算法的页面的链接。

我不认为这些算法中的任何一个都将声音考虑在内 - 因此“staq 溢出”与“堆栈溢出”与“staw 溢出”相似,尽管第一个更多发音相似。

我刚刚找到了another page,它提供了更多的选择...特别是Soundex 算法 (Wikipedia) 可能更接近您所追求的。

【讨论】:

  • 仅供参考,如果您碰巧使用 SQL Server 处理数据,它有一个 SOUNDEX() 函数。
  • 另外,应该注意的是,Soundex 是一种旧算法,主要用于英语单词。如果您想要多语言的现代算法,请考虑使用 Metaphone。本文更详细地讨论了这些差异:informit.com/articles/article.aspx?p=1848528
【解决方案2】:

Levenshtein distance 可能就是您要找的。​​p>

【讨论】:

【解决方案3】:

这是我为我正在从事的项目编写的一些代码。我需要知道字符串的相似度和基于字符串单词的相似度。 最后一个,我想知道最小字符串的单词相似度(所以如果所有单词都存在并且在较大的字符串中匹配,结果将是 100%)和较大字符串的单词相似度(我称之为 RealWordsRatio )。 我使用 Levenshtein 算法来找到距离。到目前为止,该代码尚未优化,但可以按预期工作。我希望你觉得它有用。

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

【讨论】:

    【解决方案4】:

    不久前我写了一个Double Metaphone implementation in C#。你会发现它大大优于 Soundex 等。

    Levenshtein distance 也有人提出,它是一个很好的算法,有很多用途,但语音匹配并不是它真正的作用;有时似乎只是这样,因为语音上相似的单词通常也拼写相似。我做了一个analysis of various fuzzy matching algorithms,你可能也会觉得它有用。

    【讨论】:

      【解决方案5】:

      要处理“声音相似”,您可能需要考虑使用语音算法(如 Double Metaphone 或 soundex)进行编码。我不知道在语音编码字符串上计算 Levenshtein 距离是否有益,但可能是一种可能性。或者,您可以使用启发式方法:将字符串中的每个单词转换为其编码形式,并删除两个字符串中出现的任何单词,并在计算 Levenshtein 距离之前将它们替换为单个表示。

      【讨论】:

      • 医学界使用 soundex 算法来警告类似发音的姓氏。它包含在许多标准库中。
      • Metaphone 远胜于 Soundex。
      【解决方案6】:

      您可能会查找字符串“距离”,例如the Levenshtein distance

      【讨论】:

        【解决方案7】:

        Perl 模块Text::Phonetic 实现了各种算法。

        【讨论】:

          【解决方案8】:

          Jeff Atwood wrote about looking for a similar solution 用于确定 Wiki 帖子的作者身份,这可以帮助您缩小搜索范围。

          【讨论】:

            【解决方案9】:

            如果您要比较 SQL 数据库中的值,您可以使用 SOUNDEX 函数。如果你在 Google 上查询 SOUNDEX 和 C#,有些人已经为它和 VB 编写了类似的函数。

            【讨论】:

              【解决方案10】:

              我也必须推荐 Soundex,我过去曾使用它来处理拼写错误的城市名称。这是一个很好的使用链接:http://whitepapers.zdnet.com/abstract.aspx?docid=352953

              【讨论】:

                【解决方案11】:

                如果您想在语音上进行比较,请查看 Soundex 和 Metaphone 算法:http://www.blackbeltcoder.com/Articles/algorithms/phonetic-string-comparison-with-soundex

                【讨论】:

                  【解决方案12】:

                  Metaphone 3 是 Metaphone 算法的第三代。它 从 Double 的 89% 提高语音编码的准确性 98% 的变音,根据最常见的数据库测试 英语单词,以及北方熟悉的名称和非英语单词 美国。这产生了一个极其可靠的语音编码 美式发音。

                  Metaphone 3 由 Lawrence Philips 设计和开发,他 设计并开发了原始的Metaphone和Double Metaphone 算法。

                  【讨论】:

                    猜你喜欢
                    • 2011-08-27
                    • 1970-01-01
                    • 2013-07-18
                    • 2021-10-12
                    • 2010-09-06
                    • 2018-07-15
                    相关资源
                    最近更新 更多