Possível melhoria de Damerau-Levenshtein?

9

Eu recentemente implementei o algoritmo de distância Damerau-Levenshtein a partir do pseudocódigo na Wikipedia. Eu não poderia encontrar qualquer explicação sobre exatamente como ele funciona eo pseudocódigo usa nomes de variáveis completamente uninformative como DA, DB, i1, e j1que deixou-me coçar a cabeça.

Aqui está minha implementação em Python: https://gist.github.com/badocelot/5327337

A implementação do Python me ajudou a percorrer o programa e descobrir o que estava acontecendo, renomeando as variáveis ​​para nomes mais úteis. Eu estava familiarizado o suficiente com a abordagem de Wagner-Fischer para calcular a distância de Levenshtein e tinha um quadro de referência.

Correndo o risco de ser excessivamente longo, eis como eu entendo Damerau-Levenshtein:

As variáveis ​​misteriosas:

  • DA( last_rowno meu código) é um tipo de mapa que contém a última linha em que cada elemento foi visto; no meu código é um dicionário Python real
  • DB( last_match_col) mantém a última coluna em que a letra bcorresponde à letra ada linha atual
  • i1( last_matching_row) é o número da linha da DAletra atual emb
  • j1é apenas uma cópia do valor de DB/ last_match_colantes de ser potencialmente atualizado; no meu código acabei de me mudar para onde last_match_colé atualizado e eliminado essa variável

O custo de transposição:

H[i1][j1] + (i-i1-1) + 1 + (j-j1-1)

está calculando o custo de trocar o caractere atual bpelo último caractere bconhecido por a(a última correspondência), tratando todos os caracteres intermediários como adições ou exclusões.

Componentes do custo:

  • H[i1][j1] reverte o custo base para o ponto nos cálculos anteriores à transposição, pois encontrar uma transposição invalida trabalhos anteriores
  • (i-i1-1) é a distância entre a linha atual e a última linha correspondente ao caractere atual, que é o número de exclusões que seriam necessárias
  • (j-j1-1) é a distância entre a coluna atual e a última coluna com uma correspondência, que é o número de adições
  • O extra + 1é apenas o custo da própria transposição

Se essa análise estiver incorreta, eu adoraria saber onde errei. Como eu disse, não consegui encontrar nenhuma explicação detalhada de como o algoritmo funciona online.

Versão melhorada?

Tendo descoberto isso, no entanto, ocorreu-me que, calculando o custo de ambas as adições e exclusões entre as letras transpostas parecia falho: uma adição e uma eliminação é equivalente a uma substituição, que isso não está verificando.

Se tudo estiver correto, a solução deve ser trivial: o custo das letras entre as letras transpostas deve ser o mais alto das adições e exclusões: converta o maior número possível de substituições e adicione quaisquer adições ou exclusões restantes.

Portanto, o custo seria:

H[i1][j1] + max((i-i1-1), (j-j1-1)) + 1

Aqui está o meu código para esta versão: https://gist.github.com/badocelot/5327427

De alguns testes simples, isso parece correto. Por exemplo, "abcdef" -> "abcfad" fornece uma distância de edição de 2 (transponha "d" e "f", altere "e" para "a"), enquanto o algoritmo original fornece uma distância de 3 (três últimos) letras são substituições ou 1 transposição + 1 adição + 1 exclusão).

Agora, não posso ser a primeira pessoa a pensar nisso. Então, por que eu não o encontrei? Eu apenas não procurei por tempo suficiente? Ou existe alguma falha sutil que impede que isso realmente funcione?

James Jensen
fonte
Eu decidi escrever um post explicando DL em detalhes: scarcitycomputing.blogspot.com/2013/04/...
James Jensen

Respostas:

3

Eu tive que procurar a distância Damerau-Levenshtein na wikipedia, então me perdoe se isso estiver errado. Mas parece que só permite transpor cartas adjacentes e não arbitrárias. Portanto, seu exemplo "abcdef" -> "abcfad" com transposição de d e f não funciona. Parece-me que você modificou a definição do algoritmo e não está mais calculando a distância Damerau-Levenshtein.

Steve
fonte
Hmm, entendo o que você quer dizer. O DL permite transposições antes de adições ou após exclusões. Se ambos ocorreram, não é realmente uma transposição adjacente; portanto, os disparos de custo e o custo de transposição não serão escolhidos como o novo custo. Parecia que estava lidando com os dois porque os evita através de um efeito colateral da minimização de custos.
James Jensen