Levenshtein-Distanz

Maß für die Unterschiedlichkeit zweier Zeichenketten
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 8. Juli 2007 um 19:04 Uhr durch Sulai (Diskussion | Beiträge) (Algorithmus: Fehler in Tabelle korrigiert). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Die Levenshtein-Distanz (auch Edit-Distanz, Editierdistanz oder Editierabstand) bezeichnet in der Informationstheorie ein Maß für den Unterschied zwischen zwei Zeichenketten bezüglich der minimalen Anzahl der Operationen Einfügen, Löschen und Ersetzen, um die eine Zeichenkette in die andere zu überführen. Benannt ist die Distanz nach dem russischen Wissenschaftler Wladimir Lewenstein, der sie 1965 einführte.

Um beispielsweise von „Tier“ zu „Tor“ zu kommen ist eine Ersetzung und eine Löschung notwendig, die Levenshtein-Distanz beträgt also 2:

  1. Tier
  2. Toer (Ersetze i durch o)
  3. Tor (Lösche e)

In der Praxis wird die Levenshtein-Distanz zur Bestimmung der Ähnlichkeit von Zeichenketten beispielsweise zur Rechtschreibprüfung oder bei der Duplikaterkennung angewandt.

Die Levenshtein-Distanz kann als Erweiterung des Hamming-Abstands angesehen werden, welche sich auf Ersetzungen beschränkt und daher nur Zeichenketten gleicher Länge bemessen kann. Erweiterungen der Levenshtein-Distanz oder parallele Entwicklungen, wie z.B. von Damerau, berücksichtigen auch weitere Operationen wie beispielsweise das Vertauschen zweier Zeichen oder gewichten die Operationen unterschiedlich. Mathematisch definiert die Levenshtein-Distanz eine Metrik auf dem Raum der Symbolsequenzen.

Die Editierdistanz ist eine Sonderform der Dynamic-Time-Warping-Distanz (DTW).

Algorithmus

Ein allgemein benutzter, einfacher Algorithmus zur Berechnung benötigt eine Matrix der Form (n + 1) × (m+1), wobei n und m die Längen der zu vergleichenden Strings sind. So sieht er in Pseudocode aus:

int LevenshteinDistance(char s[1..n], char t[1..m])
   declare int d[0..n,0..m]
   declare int i, j, cost
   for i := 0 to n
       d[i,0] := i
   for j := 0 to m
       d[0,j] := j
   for i := 1 to n
       for j := 1 to m
           if s[i] = t[j] then cost := 0
                          else cost := 1
           d[i,j] := minimum(d[i-1,j  ] + 1,    // insertion
                             d[i,  j-1] + 1,    // deletion
                             d[i-1,j-1] + cost) // substitution
   return d[n,m]


Das Verfahren lässt sich auch leicht manuell in einer Tabelle durchführen. Hier ein Beispiel mit den beiden Stings "Tier" und "Tor".

  ε T o r
ε 0 1 2 3
T 1 0 1 2
i 2 1 1 2
e 3 2 2 2
r 4 3 3 2

Der Abstand der beiden Strings ist 2. ε steht hierbei für den leeren String "".

Schranken

Für die Levenshtein-Distanz gelten einige obere und untere Schranken:

  • sie beträgt mindestens den Unterschied der Längen beider Strings
  • sie beträgt höchstens die Länge des längeren Strings
  • sie ist dann und nur dann 0, wenn beide Strings identisch sind
  • sie ist nicht größer als die Hamming-Distanz plus dem Längenunterschied der Strings

Damerau-Levenshtein erweitert die Funktionalität von Levenshtein um die Fähigkeit, zwei vertauschte Zeichen zu identifizieren, beispielsweise Raisch <--> Rasich. Um die eine Zeichenfolge in die Andere zu überführen, wären mit Levenshtein 2 Operationen notwendig. Damerau-Levenshtein benötigt nur eine Einzige.

Im Folgenden die Funktion in C#:

       private int DamerauLevenshteinDistance(string src, string dest){
           int[,] d = new int[src.Length + 1, dest.Length + 1];
           int i, j, cost;
           char[] str1 = src.ToCharArray();
           char[] str2 = dest.ToCharArray();    
           
           for (i = 0; i <= str1.Length; i++){
               d[i, 0] = i;
           }
           for (j = 0; j <= str2.Length; j++){
               d[0, j] = j;
           }
           for (i = 1; i <= str1.Length; i++){
               for (j = 1; j <= str2.Length; j++){
 
                   if (str1[i - 1] == str2[j - 1])
                       cost = 0;
                   else
                       cost = 1;
 
                   d[i, j] =
                       Math.Min(d[i - 1, j] + 1,     // Deletion
                       Math.Min(d[i, j - 1] + 1,     // Insertion
                       d[i - 1, j - 1] + cost));     // Substitution
 
                   if ((i > 1) && (j > 1) && (str1[i - 1] == str2[j - 2]) && (str1[i - 2] == str2[j - 1])){
                       d[i, j] = Math.Min(d[i, j], d[i - 2, j - 2] + cost);
                   }
               }
           }
           return d[str1.Length, str2.Length];
       }

Verwandte Verfahren

Die Kosten der einzelnen Operationen können auch unterschiedlich oder abhängig von den jeweiligen Zeichen gewichtet werden.

Literatur

  • Vladimir I. Levenshtein: Binary codes capable of correcting deletions, insertions, and reversals. In: Doklady Akademii Nauk SSSR, 163(4) S. 845-848, 1965 (Russisch). Englische Übersetzung in: Soviet Physics Doklady, 10(8) S. 707-710, 1966.