Possibile miglioramento di Damerau-Levenshtein?


9

Di recente ho implementato l'algoritmo di distanza Damerau-Levenshtein dallo pseudocodice su Wikipedia. Non riuscivo a trovare alcuna spiegazione esattamente come funziona e lo pseudocodice utilizza nomi di variabili completamente uninformative come DA, DB, i1, e j1che mi ha lasciato graffiare la mia testa.

Ecco la mia implementazione in Python: https://gist.github.com/badocelot/5327337

L'implementazione di Python mi ha aiutato a seguire il programma e capire cosa stava succedendo, rinominando le variabili con nomi più utili. Conoscevo abbastanza l'approccio di Wagner-Fischer al calcolo della distanza di Levenshtein da avere un quadro di riferimento.

A rischio di essere troppo lungo, ecco come capisco Damerau-Levenshtein:

Le variabili misteriose:

  • DA( last_rownel mio codice) è una specie di mappa che contiene l'ultima riga su cui è stato visto ogni elemento; nel mio codice è un vero dizionario Python
  • DB( last_match_col) contiene l'ultima colonna in cui la lettera bcorrisponde alla lettera adella riga corrente
  • i1( last_matching_row) è il numero di riga di DAper la lettera corrente inb
  • j1è solo una copia del valore di DB/ last_match_colprima che sia potenzialmente aggiornato; nel mio codice ho appena spostato dove last_match_colviene aggiornato ed eliminato questa variabile

Il costo di recepimento:

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

sta calcolando il costo dello scambio del personaggio corrente bcon l'ultimo personaggio in cui bsi trova a(l'ultima partita), trattando tutti i personaggi in mezzo come aggiunte o eliminazioni.

Componenti del costo:

  • H[i1][j1] ripristina il costo base al punto nei calcoli prima del recepimento, poiché la ricerca di un recepimento invalida il lavoro precedente
  • (i-i1-1) è la distanza tra la riga corrente e l'ultima riga corrispondente al carattere corrente, che è il numero di eliminazioni che sarebbero richieste
  • (j-j1-1) è la distanza tra la colonna corrente e l'ultima colonna con una corrispondenza, che è il numero di aggiunte
  • Il supplemento + 1è solo il costo della trasposizione stessa

Se questa analisi non è corretta, mi piacerebbe sapere dove ho sbagliato. Come ho detto, non sono riuscito a trovare alcuna spiegazione dettagliata di come funziona l'algoritmo online.

Versione migliorata?

Avendolo capito, però, mi ha colpito il fatto che calcolando il costo di entrambe le aggiunte e le eliminazioni tra lettere trasposte sembrava difettoso: un'aggiunta e una cancellazione equivalgono a una sostituzione, che non sta verificando.

Se tutto ciò che è corretto, la soluzione dovrebbe essere banale: il costo delle lettere tra le lettere trasposte dovrebbe essere il maggiore tra le aggiunte e le cancellazioni: convertire il maggior numero di sostituzioni possibile e aggiungere eventuali aggiunte o eliminazioni rimaste.

Quindi il costo sarebbe:

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

Ecco il mio codice per questa versione: https://gist.github.com/badocelot/5327427

Da alcuni semplici test, questo sembra corretto. Ad esempio, "abcdef" -> "abcfad" fornisce una distanza di modifica di 2 (trasporre "d" e "f", cambia "e" in "a"), mentre l'algoritmo originale fornisce una distanza di 3 (le ultime tre le lettere sono sostituzioni o 1 trasposizione + 1 aggiunta + 1 cancellazione).

Ora, non posso essere la prima persona a pensarci. Quindi, perché non l'ho incontrato? Non ho cercato abbastanza a lungo? O c'è qualche sottile difetto che impedisce a questo di funzionare davvero?


Ho deciso di scrivere un post sul blog spiegando in dettaglio DL: scarcitycomputing.blogspot.com/2013/04/…
James Jensen

Risposte:


3

Ho dovuto cercare la distanza Damerau-Levenshtein su Wikipedia, quindi perdonami se è sbagliato. Ma sembra che consenta solo di trasporre lettere adiacenti e non di lettere arbitrarie. Quindi il tuo esempio "abcdef" -> "abcfad" con trasposizione di d e f non funziona. Mi sembra che tu abbia modificato la definizione dell'algoritmo e non stia più calcolando la distanza Damerau-Levenshtein.


Capisco cosa intendi. DL consente trasposizioni prima delle aggiunte o dopo le cancellazioni. Se si sono verificati entrambi, non si tratta in realtà di una trasposizione adiacente, quindi il costo salirà alle stelle e il costo della trasposizione non verrà scelto come nuovo costo. Sembrava che gestisse entrambi perché li evita attraverso un effetto collaterale della minimizzazione dei costi.
James Jensen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.