Distância Levenshtein
Em teoria da informação, a distância Levenshtein ou distância de edição entre dois "strings" (duas sequências de caracteres) é dada pelo número mínimo de operações necessárias para transformar um string no outro. Entendemos por "operações" a inserção, deleção ou substituição de um carácter. O nome advém do cientista russo Vladimir Levenshtein, que considerou esta distância já em 1965. É muito útil para aplicações que precisam determinar quão semelhantes dois strings são, como é por exemplo o caso com os verificadores ortográficos.
Exemplo
[editar | editar código]Por exemplo, a distância Levenshtein entre as palavras inglesas "kitten" (gato) e "sitting" (sentando-se) é 3, já que com apenas 3 edições conseguimos transformar uma palavra na outra, e não há maneira de o fazer com menos de três edições:
- kitten
- sitten (substituição de 'k' por 's')
- sittin (substituição de 'e' por 'i')
- sitting (inserção de 'g' no final)
A distância de Levenshtein pode ser considerada como uma generalização da Distância de Hamming, usada para strings com o mesmo tamanho, a qual só considera edições por substituição. Há também outras generalizações da distância Levenshtein que consideram, por exemplo, a troca de dois caracteres como uma aplicação.
O algoritmo
[editar | editar código]Um algoritmo bottom-up de programação dinâmica usado frequentemente para calcular a distância Levenshtein usa uma matriz (n + 1) × (m + 1), na qual n e m são o número de caracteres dos dois strings. Aqui um pseudocódigo para uma função LevenshteinDistance que usa dois strings, str1 de comprimento lenStr1, e str2 de comprimento lenStr2, e calcula a distância Levenshtein entre eles:
Função LevenshteinDistance(Caracter : str1[1..lenStr1], Caracter : str2[1..lenStr2]) : INTEIRO
Início
// tab é uma tabela com lenStr1+1 linhas e lenStr2+1 colunas
Inteiro: tab[0..lenStr1, 0..lenStr2]
// X e Y são usados para iterar str1 e str2
Inteiro: X, Y, cost
Para X de 0 até lenStr1
tab[X, 0] ← X
Para Y de 0 até lenStr2
tab[0, Y] ← Y
Para X de 1 até lenStr1
Para Y de 1 até lenStr2
Se str1[X] = str2[Y] Então cost ← 0
Se-Não cost ← 1 // Custo da substituição deve ser 1, deleção e inserção
tab[X, Y] := menor(
tab[X-1, Y ] + 1, // Deletar
tab[X , Y-1] + 1, // Inserir
tab[X-1, Y-1] + cost // Substituir
)
LevenshteinDistance ← tab[lenStr1, lenStr2]
Fim
A constante mantida ao longo do algoritmo é que podemos transformar o segmento inicial str1[1..X] em str2[1..Y] usando um mínimo de operações tab[X,Y]. No final, o elemento no fundo ao lado direito do array contém a resposta.
Melhoramentos possíveis
[editar | editar código]Melhoramentos possíveis para este algoritmo poderiam ser por exemplo:
- Podemos adaptar o algoritmo para usar menos espaço, O(m) em vez de O(mn), já que ele apenas requer que a linha anterior e a linha actual sejam armazenadas ao mesmo tempo.
- Podemos armazenar o número de inserções, deleções e substituições separadamente, ou mesmo as posições em que elas ocorrem, que são sempre
Y. - Podemos dar diferentes penalidades de custo à inserção, deleção e substituição.
- A inicialização do
tab[i,0]pode passar para dentro do grande loop principal exterior. - Este algoritmo paraleliza de uma forma pouco eficiente, devido a grande número de dependências de dados. No entanto, todo o
custopode ser calculado em paralelo, e o algoritmo pode ser adaptado para perfazer a funçãomínimoem fases para eliminar dependências.
Prova de sucesso
[editar | editar código]Como foi mencionado, a constante é que podemos transformar o segmento inicial s[1..X] em t[1..Y] usando um mínimo de tab[X,Y] operações. Esta constante é verdadeira já que:
- É inicialmente verdadeira na linha e colunas 0 porque
s[1..X]pode ser transformado num string vaziot[1..0]por simplesmente apagando todos osXcaracteres. Do mesmo modo, podemos transformars[1..0]emt[1..Y]ao simplesmente adicionando todos os caracteresY. - O mínimo é tomado em três distâncias, sendo em qualquer das quais possível que:
- Se podemos transformar
s[1..X]at[1..Y-1]emkoperações, então nós podemos simplesmente adicionart[Y]depois para obtert[1..Y]emk+1operações. - Se podemos transformar
s[1..X-1]at[1..Y]emkoperações, então nós podemos fazer as mesmas operações ems[1..X]e depois remover os[X]original ao fim dek+1operações. - Se podemos transformar
s[1..X-1]at[1..Y-1]emkoperações, então podemos fazer o mesmo coms[1..X]e depois fazer uma substituição det[Y]pelass[X]originais no final, se necessário, requerindok+costoperações.
- Se podemos transformar
- As operações requiridas para transformar
s[1..n]emt[1..m]é o número necessário para transformar todos ossem todos ost, e logotab[n,m]contém o nosso resultado desejado.
Esta prova não confirma que o número colocado em tab[X,Y] seja de facto o mínimo; isso é mais difícil de provar e exige um argumento Reductio ad absurdum no qual assumimos que tab[X,Y] é menor que o mínimo dos três, e usamos isto para mostrar que um dos três não é mínimo.
Limites superior e inferior
[editar | editar código]A distância Levenshtein tem vários limites superior e inferior simples que são úteis em aplicações que calculam vários deles e os comparam. Estes incluem:
- É sempre pelo menos igual à diferença dos tamanhos dos dois strings.
- É no máximo igual ao tamanho do string mais longo.
- É zero se e só se os strings são idênticos.
- Se os strings têm o mesmo tamanho, a distância de Hamming é um limite superior da distância Levenshtein.
- Se os strings são chamados
set, o número de caracteres (não contando os duplicados) que encontramos emsmas não emté um limite inferior.
Links (em português, inglês e alemão)
[editar | editar código]- Levenshtein distance - Rosetta Code - Implementações do algoritmo em diversas linguagens de programação
- O fantástico mundo da distância de edição (um survey em formato PDF sobre distância de edição)
- Levenshtein Distance, in Three Flavors, by Michael Gilleland
- NIST's Dictionary of Algorithms and Data Structures: Levenshtein Distance
- CSE 590BI, Winter 1996 Algorithms in Molecular Biology[ligação inativa] The algorithms from lectures 2, 3 and 4 are based on the Levenshtein distance but implement a different scoring function. The Haskell example was based on these notes.
- Levenshtein Distance - visualized
- Distance between strings - Levenshtein and Hamming
- Another Edit Distance Visualization (very fast)
- Wie funktioniert der Levenshtein-Algorithmus…?