U informatici, Vagner-Fišerov algoritam je algoritam dinamičkog programiranja koji meri Levenštajnovo rastojanje između dve niske karaktera.
Vagner-Fišerov algoritam izračunava Levenštajnovo rastojanje na osnovu posmatranja, na primer ako napravimo matricu koja će da sadrži Levenštajnova rastojanja između svih prefiksa prve niske i svih prefiksa druge niske, onda možemo da izračunamo vrednosti u matrici tako što ćemo da „poplavimo“ matricu (takozvanim algoritmom „punjenje poplavom“ ili na eng. flood filling).Poslednja izračunata vrednost će predstavljati rastojanje između dve niske. Što je Levenštajnovo rastojanje dve niske veće, to se one više razlikuju među sobom.
Jednostavna implementacija, u obliku pseudokoda za funkciju LevenstajnovoRastojanje uzima dve niske, m je dužina niske s, i n je dužina niske t, i vraća njihovo Levenštajnovo rastojanje:
int LevenstajnovoRastojanje(char s[1..m], char t[1..n]) { // za svako i i j, d[i,j] ce predstavljati Levenstajnovo rastojanje // izmedju prvih i karaktera niske s i prvih j karaktera niske t; // napomena, matrica d je velicine (m+1)*(n+1) int d[0..m, 0..n] for i from 0 to m d[i, 0] := i // rastojanje od bilo koje prve niske do druge prazne niske for j from 0 to n d[0, j] := j // / rastojanje od bilo koje druge niske do prve prazne niske for j od 1 do n { for i od 1 do m { if s[i] = t[j] then d[i, j] := d[i-1, j-1] // nije potrebna nikakva operacija else d[i, j] := minimum ( d[i-1, j] + 1, // operacija brisanja d[i, j-1] + 1, // operacija umetanja d[i-1, j-1] + 1 // operacija zamene ) } } return d[m,n] }
Primer formiranja matrice:
|
|
Na kraju, u donjem desnom uglu se nalazi odgovor koliko možemo minimalno operacija da iskoristimo da bismo segment s[1..i] transformisali u segment t[1..j].
Invarijanta algoritma je da možemo da transformišemo početni segment s[1…i]
u t[1…j]
koristeći minimalni broj operacija d[i,j]
. Ovo važi jer:
s[1..i]
može biti transformisan u praznu nisku t[1…0]
tako što se jednostavno izbacuju svi i
karakteri. Isto tako, možemo transformisati s[1..0]
u t[1..j]
jednostavno dodajući sve j
karaktere.s[i]=t[j]
, i ako možemo da transformišemo s[1..i-1]
u t[1..j-1]
uz pomoć k
operacija, onda to možemo da uradimo i segmentu s[1..i]
, ostavljajući poslednji karakter po strani.s[1..i]
u t[1..j]
uz pomoć k
operacija , to znači da možemo jednostavno da dodamo t[j]
da bi posle toga dobili t[1..j]
uz pomoć k+1
operacija (umetanje).s[1..i-1
] u t[1..j]
uz pomoć k
operacija, to znači da možemo da uklonimo s[i]
i da zatim uradimo istu transformaciju uz pomoć k+1
operacija. (brisanje).s[1..i-1]
u t[1..j-1] uz pomoć k
operacija, to znači da to možemo isto uraditi i segmentu s[1..i]
, i zameniti originalni s[i]
sa t[j]
, uz pomoć k+1
operacija (zamena).s[1..n]
u t[1..m]
, je naravno broj koji je potreban da bi se svako s
transformisalo u t
, zbog toga je rezultat d(n,m)
.Ovaj dokaz ne potvrđuje da je broj d[i,j]
stvarno minimalni broj operacija; to je mnogo teži dokaz, uključuje kontradikciju u kojoj pretpostavimo da je d[i,j]
manji od minimuma tri mogućih načina transformacija, i koristimo tu pretpostavku da pokažemo da jedan od ta tri načina nije minimum.
Moguća poboljšanja ovog algoritma uključuju sledeće:
j
.[0,1]
.k
, onda je dovoljno da se izračuna dijagonala širine 2k+1
u matrici. Na ovaj način, algoritam može imati vremensku složenost O(kl), gde je l dužina najkraće niske.[1]Levenštajnovo rastojanje ima nekoliko prostih gornjih i donjih granica koje su korisne u primenama u kojima se računaju i upoređuju. Ovo uključuje da: