【问题标题】:Ruby: How does one test for similarity between two blocks of text?Ruby:如何测试两个文本块之间的相似性?
【发布时间】:2012-12-09 21:13:57
【问题描述】:

所以,假设我有这些文本:

文本1:

绝对服从被称为主宰的虫族集体感知。主宰指挥着虫群中的每一个异虫生物的行动,通过一个次等感知者的等级制度运作。

文本2:

虫群中的虫族生物,通过次等感知者的等级制度运作。虽然主宰主要是由它的消费和同化欲望驱动的

文字 3

当虫族第一次到达科普鲁星区时,他们通过绝对服从被称为主宰的虫族集体意识而统一起来。主宰指挥着虫群中的每一个异虫生物的行动,通过一个次等感知者的等级制度运作。虽然主宰的主要动力是它想要吞噬和同化先进的星灵种族,但它在人类身上发现了有用但尚未开发的材料。

现在,Text1 的结尾和 text2 的开头重叠,所以我们可以说文本块不是唯一的。同样,对于 Text3,可以在其中找到 Text1(以及 Text2),因此由于重叠,这也不是唯一的。

所以,我的问题:

如何编写可以查看连续字母或单词并确定唯一性的内容?理想情况下,我希望这样的方法返回一些值,表示相似度——也许是匹配词的数量超过两个文本块大小的平均值。当它返回 0 时,测试的两个文本都应该是完全唯一的。

我在使用 Ruby 的字符串方法时遇到了一些问题。

首先,我开始尝试找到两个字符串的交集。

>> a = "nt version, there are no ch"  
>> b = "he current versi"  
>> (a.chars.to_a & b.chars.to_a).join  
=> "nt versihc"  

上述方法的问题是它只是在结果的末尾附加了共同的字母(我们失去了字符的顺序),这使得很难测试唯一性。但我不认为交集是开始这种相似性比较的最佳方式。在被比较的两个文本中都可以出现任意数量的单词组合。所以也许如果我做了一系列连续的相似性......但这将要求我们遍历其中一个文本的次数与我们尝试短语长度的次数一样多。

我想我真的不知道从哪里开始,并且以一种高效而不是O(n^too_high)的方式开始。

【问题讨论】:

    标签: ruby performance search text comparison


    【解决方案1】:

    我相信您正在寻找的是Longest Common Substring problem,即在给定两个字符串的情况下,寻找它们共有的最长子字符串的问题。该链接指向 Wikipedia 页面,该页面将帮助您了解该领域,并包含在 O(nm) 时间内运行的算法的伪代码示例。

    此外,Wikibooks 的算法实现书有an implementation in Ruby。它包括一个lcs_size 方法,这可能就是您所需要的。简而言之,如果 lcs_size(text1, text2) 返回 4,这意味着 text1text2 几乎没有共同的连续文本,可能只有一个单词,但如果它返回,比如 40,它们可能有一个共同的句子。

    希望对你有帮助!

    【讨论】:

    • 这很有帮助!谢谢!这可能正是我开始所需要的 =D
    【解决方案2】:

    这是一个 Ruby implementation of the Levenshtein distance algorithm。安装gem之后,就可以这样使用了:

    require 'rubygems'
    require 'Text'
    
    t1 = "absolute obedience to the zerg collective sentience known as the Overmind. The Overmind directed the actions of every zerg creature in the Swarm, functioning through a hierarchy of lesser sentients."
    
    t2 = "zerg creature in the Swarm, functioning through a hierarchy of lesser sentients. Although the Overmind was primarily driven by its desire to consume and assimilate"
    
    puts Text::Levenshtein.distance(t1,t2)
    

    【讨论】:

    • 我觉得这是在每个字母的基础上完成的,而不是连续的单词。 =\ 身份证。就像...我比较了我的代码块之后的大段(在上面的问题中)和代码块之前的大段。我做了 Text::Levenshtein.distance 并除以两个段落长度的平均长度。我得到的差异百分比(越接近 1,越不同;0,更相似)与您给出的示例几乎相同,因为这是我在上面两段之间的比较(以“问题与”开头,并且'我该怎么走')
    • “连续词”称为“最长公共子串”算法。
    【解决方案3】:

    你的问题不是 Ruby。就是算法。您可以将每个文本拆分为单词,然后运行最小距离算法 (http://en.wikipedia.org/wiki/Levenshtein_distance) 来获得。

    数字越小,文字越相似。

    【讨论】:

      【解决方案4】:

      这可以改进很多,但这是一个想法:

      txt1 = "absolute obedience to the zerg collective sentience known as the Overmind. The Overmind directed the actions of every zerg creature in the Swarm, functioning through a hierarchy of lesser sentients."
      txt2 = "zerg creature in the Swarm, functioning through a hierarchy of lesser sentients. Although the Overmind was primarily driven by its desire to consume and assimilate"
      
      def txt_to_ary(txt)
          txt.gsub(/\.|,/, ' ').downcase.split(/\s+/)
      end
      
      def longest_match(txt1, txt2)
          longest = 0
          txt1.each_with_index do |w1, i|
              txt2.each_with_index do |w2, j|
                  next unless w1 == w2
                  k = 0
                  k += 1 while txt1[i+k] == txt2[j+k]
                  longest = k if k > longest          
              end
          end
          longest
      end
      
      txt1 = txt_to_ary( txt1 )
      txt2 = txt_to_ary( txt2 )
      
      puts longest_match(txt1, txt2) #=>12
      

      【讨论】:

      • 这一行:k += 1 while txt1[i+k] == txt2[j+k] never end when txt1.length - i == txt2.length -j
      【解决方案5】:

      amatch gem 非常适合字符串比较。

      【讨论】:

        猜你喜欢
        • 2017-09-23
        • 1970-01-01
        • 2019-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-12
        • 2015-12-12
        相关资源
        最近更新 更多