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.