Ich habe kürzlich den Damerau-Levenshtein-Distanzalgorithmus aus dem Pseudocode auf Wikipedia implementiert. Ich konnte keine Erklärung genau das finden , wie es funktioniert und der Pseudo - Code verwendet vollständig uninformativ Variablennamen wie DA
, DB
, i1
, und j1
das ließ ich meinen Kopf kratzen.
Hier ist meine Implementierung in Python: https://gist.github.com/badocelot/5327337
Die Python-Implementierung hat mir geholfen, durch das Programm zu gehen und herauszufinden, was passiert ist, und die Variablen in hilfreichere Namen umzubenennen. Ich war mit dem Wagner-Fischer-Ansatz zur Berechnung der Levenshtein-Entfernung so vertraut, dass ich einen Bezugsrahmen hatte.
Ich verstehe Damerau-Levenshtein folgendermaßen:
Die mysteriösen Variablen:
DA
(last_row
in meinem Code) ist eine Art Karte, die die letzte Zeile enthält, in der jedes Element gesehen wurde. In meinem Code ist es ein aktuelles Python-WörterbuchDB
(last_match_col
) enthält die letzte Spalte, in der der Buchstabeb
mit dem Buchstaben ina
der aktuellen Zeile übereinstimmti1
(last_matching_row
) ist die Zeilennummer vonDA
für den aktuellen Buchstaben inb
j1
ist nur eine Kopie des Werts vonDB
/,last_match_col
bevor er möglicherweise aktualisiert wird; In meinem Code habe ich gerade verschoben, wolast_match_col
diese Variable aktualisiert und entfernt wurde
Die Umsetzungskosten:
H[i1][j1] + (i-i1-1) + 1 + (j-j1-1)
berechnet die Kosten für das Austauschen des aktuellen Zeichens b
gegen das letzte b
bekannte Zeichen a
(die letzte Übereinstimmung) und behandelt alle dazwischen liegenden Zeichen entweder als Hinzufügung oder als Löschung.
Kostenbestandteile:
H[i1][j1]
Setzt die Grundkosten auf den Punkt in den Berechnungen vor der Umsetzung zurück, da das Auffinden einer Umsetzung frühere Arbeiten ungültig macht(i-i1-1)
ist der Abstand zwischen der aktuellen Zeile und der letzten Zeile, der mit dem aktuellen Zeichen übereinstimmt. Dies ist die Anzahl der Löschungen, die erforderlich wären(j-j1-1)
ist der Abstand zwischen der aktuellen Spalte und der letzten Spalte mit einer Übereinstimmung, dh die Anzahl der Hinzufügungen- Das Extra
+ 1
sind nur die Kosten für die Umsetzung selbst
Wenn diese Analyse falsch ist, würde ich gerne wissen, wo ich falsch gelaufen bin. Wie gesagt, ich konnte keine detaillierte Erklärung finden, wie der Algorithmus online funktioniert.
Verbesserte Version?
Nachdem das heraus, aber es fiel mir auf, dass durch die Kosten für die Berechnung der beiden Ergänzungen und Streichungen zwischen Buchstaben schien fehlerhaft: eine Addition und eine Deletion zu einer Substitution entspricht, die dies nicht überprüft.
Wenn alles richtig ist, sollte die Lösung trivial sein: Die Kosten für die Buchstaben zwischen transponierten Buchstaben sollten die höheren der Hinzufügungen und Löschungen sein: Konvertieren Sie so viele in Substitutionen wie möglich und fügen Sie alle verbleibenden Ergänzungen oder Löschungen hinzu.
Die Kosten wären also:
H[i1][j1] + max((i-i1-1), (j-j1-1)) + 1
Hier ist mein Code für diese Version: https://gist.github.com/badocelot/5327427
Nach einigen einfachen Tests scheint dies richtig zu sein. Zum Beispiel gibt "abcdef" -> "abcfad" einen Bearbeitungsabstand von 2 an (transponieren Sie "d" und "f", ändern Sie "e" in "a"), während der ursprüngliche Algorithmus einen Abstand von 3 angibt (entweder die letzten drei) Buchstaben sind Substitutionen oder 1 Transposition + 1 Addition + 1 Deletion).
Jetzt kann ich nicht die erste Person sein, die daran gedacht hat. Warum bin ich nicht darauf gestoßen? Habe ich gerade nicht lange genug gesucht? Oder gibt es einen subtilen Fehler, der verhindert, dass dies tatsächlich funktioniert?