2013-05-01 3 views
18

Можно ли измерить расстояние между двумя строками с помощью Ruby?Измерьте расстояние между двумя строками с помощью Ruby?

т.е .:

compare('Test', 'est') # Returns 1 
compare('Test', 'Tes') # Returns 1 
compare('Test', 'Tast') # Returns 1 
compare('Test', 'Taste') # Returns 2 
compare('Test', 'tazT') # Returns 5 
+0

ли вы имеете в виду разницу? – nzifnab

+7

Ищите «левенштейн расстояния рубина» и см. [Левенштейн-расстояние] (http://en.wikipedia.org/wiki/Levenshtein_distance). (Я не совсем уверен, почему последний вызов должен возвращать 5, но максимальное расстояние редактирования [ограничено] (http://en.wikipedia.org/wiki/Levenshtein_distance#Upper_and_lower_bounds) на входные длины.) – user2246674

+0

@nzifnab Итак, мне нужен целочисленный возврат. –

ответ

18

Я нашел это для вас:

def levenshtein_distance(s, t) 
    m = s.length 
    n = t.length 
    return m if n == 0 
    return n if m == 0 
    d = Array.new(m+1) {Array.new(n+1)} 

    (0..m).each {|i| d[i][0] = i} 
    (0..n).each {|j| d[0][j] = j} 
    (1..n).each do |j| 
    (1..m).each do |i| 
     d[i][j] = if s[i-1] == t[j-1] # adjust index into string 
        d[i-1][j-1]  # no operation required 
       else 
        [ d[i-1][j]+1, # deletion 
        d[i][j-1]+1, # insertion 
        d[i-1][j-1]+1, # substitution 
        ].min 
       end 
    end 
    end 
    d[m][n] 
end 

[ ['fire','water'], ['amazing','horse'], ["bamerindos", "giromba"] ].each do |s,t| 
    puts "levenshtein_distance('#{s}', '#{t}') = #{levenshtein_distance(s, t)}" 
end 

Это удивительный результат: =)

levenshtein_distance('fire', 'water') = 4 
levenshtein_distance('amazing', 'horse') = 7 
levenshtein_distance('bamerindos', 'giromba') = 9 

Источник: http://rosettacode.org/wiki/Levenshtein_distance#Ruby

+1

смех @ giromba –

11

Намного проще, я Руби понты временами ...

# Levenshtein distance, translated from wikipedia pseudocode by ross 

def lev s, t 
    return t.size if s.empty? 
    return s.size if t.empty? 
    return [ (lev s.chop, t) + 1, 
      (lev s, t.chop) + 1, 
      (lev s.chop, t.chop) + (s[-1, 1] == t[-1, 1] ? 0 : 1) 
     ].min 
end 
+4

Это может быть медленным, но это отличная отправная точка, если вы хотите адаптировать код для вычисления расстояния Левенштейна для чего-то другого, кроме строк (например, списков слов). –

+0

На самом деле, это быстрее, чем другие версии Ruby ... – DigitalRoss

+0

Ответ 'require 'levenshtein' также работает для массивов слов, фактически массивов всего, что понимает': hash' и ': eql?'. –

21

Гораздо проще и быстрее за счет связывания родной C:

gem install levenshtein-ffi 
gem install levenshtein 

require 'levenshtein' 

Levenshtein.normalized_distance string1, string2, threshold 

http://rubygems.org/gems/levenshtein http://rubydoc.info/gems/levenshtein/0.2.2/frames

+0

Обратите внимание, что это не обрабатывает Unicode (камень levensthein-ffi на момент написания этой статьи) – whistler

1

I сделал damerau-levenshtein gem, где алгоритмы реализованы в C

require "damerau-levenshtein" 
dl = DamerauLevenshtein 
dl.distance("Something", "Smoething") #returns 1 
4

Там это метод полезности в Rubygems, что на самом деле должны быть публичными, но это не так, в любом случае:

require "rubygems/text" 
ld = Class.new.extend(Gem::Text).method(:levenshtein_distance) 

p ld.call("asd", "sdf") => 2 
Смежные вопросы