Levenshteinafstand

In inligtingsteorie word die **Levenshteinafstand** of **redigeerafstand** tussen twee karakterstringe gegee deur die minimum aantal bewerkings benodig om een string in 'n ander te transformeer, waar 'n bewerking 'n invoeging, skrapping, of vervanging is. Dit is vernoem na Vladimir Levenshtein, wat die afstand in 1965 oorweeg het. Dit is nuttig in toepassings wat moet bepaal hoe soortgelyk twee stringe is, soos speltoetsers en vertalingsgeheues.

Byvoorbeeld, die Levenshteinafstand tussen "kitten" en "sitting" is 3 omdat daar drie redigeerbewerkings nodig is om die een in die ander te verander, en daar is geen manier om dit in minder as drie verwerkings te doen nie:

* kitten
# sitten (vervanging van 'k' met 's')
# sittin (vervanging van 'e' met 'i')
# sitting (byvoeg van 'g' aan die einde)

Dit kan as 'n veralgemening van die Hammingafstand gesien word wat vir stringe van dieselfde lengte gebruik word en wat slegs vervangingsbewerkings oorweeg.  Daar is ook verdere veralgemenings van die Levenshteinafstand wat byvoorbeeld omruiling van twee karakters as 'n enkele verwerking hanteer.

## Die algoritme 
'n Algemene onder-na-bo dinamieseprogrammeringalgoritme vir die berekening van die Levenshteinafstand behels die gebruik van 'n (n + 1) &times; (m + 1) matriks, waar n en m die lengtes van die twee stringe is. Hier is pseudokode vir 'n funksie *Levenshteinafstand* wat twee stringe neem, *strA* met lengte *lenStrA*, en *strB* met lengte *lenStrB*, en die Levenshteinafstand tussen hulle bereken:

 **heelgetal** Levenshteinafstand(**karakter** strA[1..lenStrA], **karakter** strB[1..lenStrB])
    ''// d is 'n tabel met lenStrA+1 rye en lenStrB+1 kolomme*
    **verklaar** **int** d[0..lenStrA, 0..lenStrB]
    *// i en j word vir iterasie oor strA en strB gebruik*
    **verklaar** **heelgetal** i, j, koste
  
    **vir** i **vanaf** 0 **tot** lenStrA
        d[i, 0] := i
    **vir** j **vanaf** 0 **tot** lenStrB
        d[0, j] := j
  
    **vir** i **vanaf** 1 **tot** lenStrA
        **vir** j **vanaf** 1 **tot** lenStrB
            **as** strA[i] = strB[j] **dan** koste := 0
                                 **anders** koste := 1
            d[i, j] := minimum(
                                 d[i-1, j  ] + 1,     *// skrapping*
                                 d[i  , j-1] + 1,     *// invoeging*
                                 d[i-1, j-1] + koste   *// vervanging''
                             )
  
    **raporteer** d[lenStrA, lenStrB]

Die invariant wat deurgaans in die algoritme behoue bly is dat ons die aanvanklike segment `str1[1..i]` in `str2[1..j]` kan transformeer deur 'n minimum van `d[i,j]` verwerkings te gebruik. Aan die einde bevat die regter onderste element van die skikking die antwoord.

### Moontlike verbeteringe 
Moontlike verbeteringe aan die algoritme sluit in:
* Ons kan die algoritme aanpas om minder plek (geheue) te gebruik, O(m) in plaas van O(mn), aangesien dit slegs vereis dat die vorige ry en die huidige ry op enige gegewe tydstip gestoor word.
* Ons kan die aantal invoegings, skrappings en vervangings of selfs die posisies waar hulle voorkom, wat altyd `j` is,  afsonderlik stoor.
* Ons kan verskillende kostes aan invoeging, skrapping en vervanging toeken.
* Die inisialisering van `d[i,0]` kan na binne die hoof buite lus geskuif word.
* Hierdie algoritme paralleliseer nie goed nie, as gevolg van 'n groot aantal data-afhanklikhede. Al die kostes, `cost`, kan egter in parallel bereken word en die algoritme kan aangepas word om die `minimum` funksie in fases uit te voer om afhanklikhede te elimineer.

### Korrektheidsbewys 
Soos vroeër genoem, is die invariant dat ons die aanvanklike segment `s[1..i]` in `t[1..j]` kan transformeer deur 'n minimum  `d[i,j]` verwerkings te gebruik. Die invariant geld aangesien:
* Dit is aanvanklik waar is vir ry en kolom 0 omdat `s[1..i]` in die leë string `t[1..0]` getransformeer kan word deur eenvoudig al `i` karakters te laat gaan. Op soortgelyke manier kan ons `s[1..0]` na `t[1..j]` , transformeer deur eenvoudig al `j` karakters op te tel.
* Die minimum word oor drie afstande geneem, elkeen waarvoor  moontlik is:
** As ons `s[1..i]` na `t[1..j-1]` in `k` verwerkings kan transformeer dan kan ons `t[j]` na die tyd optel om `t[1..j]` in `k+1` verwerkings te kry.
** As ons `s[1..i-1]` na `t[1..j]` in `k` verwerkings kan transformeer, dan kan ons dieselfde verwerkings doen op `s[1..i]` en dan die oorspronklike `s[i]` teen die einde in `k+1` verwerkings verwyder.
** As ons `s[1..i-1]` na `t[1..j-1]` in `k` verwerkings kan transformeer, kan ons dieselfde doen aan `s[1..i]` en dan 'n vervanging van `t[j]` vir die oorspronklike `s[i]` teen die einde indien nodig wat  `k+cost` verwerkings benodig.
* Die verwerkings benodig om `s[1..n]` in `t[1..m]` te transformeer is natuurlik die aantal benodig om al  `s` in alle  `t` te transformeer, en dus `d[n,m]` geld ons resultaat.

Die bewys slaag nie daarin om te bevestig dat die getal geplaas in `d[i,j]` inderdaad minimaal is nie; dit is moeiliker om aan te toon, en behels 'n  argument by contradiction waarin ons aanneem dat `d[i,j]` kleiner is as die minimum van drie, en dit dan gebruik om te wys dat een van die drie nie minimaal is nie.

### Implementasies 
Die bronkode vir voorbeeld implementasies in verskillende rekenaartale kan gevind word by  Wikisource .

## Bo- en onder-grense 
Die Levenshteinafstand het verskillende bo- en onder-grense wat nuttig is in toepassings wat baie daarvan moet bereken en vergelyk. Dit sluit in:

* Dit is altyd ten minste die verskil in grootte van die twee stringe.
* Dit is op die meeste die lengte van die langste string.
* Dit is nul as en slegs as die stringe identies is.
* As die stringe dieselfde lengte het is die Hammingafstand 'n bo-grens is vir die Levenshteinafstand.
* As die stringe  `s` en `t` genoem word is die aantal karakters (sonder om duplikate te tel) in `s` wat nie in in `t` gevind word nie 'n onder-grens.

## Kyk ook 
* Bitap algoritme
* Levenshteinautomata
* Metric space

## Eksterne skakels 
* Levenshtein Distance, in Three Flavors, by Michael Gilleland
* NIST's Dictionary of Algorithms and Data Structures: Levenshtein Distance
* CSE 590BI, Winter 1996 Algorithms in Molecular Biology The algorithms from lectures 2, 3 and 4 are based on the Levenshtein distance but implement a different scoring function.  The Haskell example was based on these notes.
* Levenshtein Distance - visualized
* Distance between strings - Levenshtein and Hamming
* Another Edit Distance Visualization (very fast)
* Wie funktioniert der Levenshtein-Algorithmus...? 
* Continuous variants, spike train metrics, and applications to neurophysiology