Talk:Levenshtein distance

message: What is the time complexity for calculating Levenshtein distance? For example, is it O(n*m) where n and m are the lengths of the two strings being compared? I think this information would be very helpful to include for any implementations described in the article. --Showeropera (talk) 13:32, 14 January 2016 (UTC)
replies: text: The Wagner-Fisher algorithm takes O(n*m) in the worst case, which is O(n²) assuming that m = Θ(n). This follows trivially from the structure of the algorithm; count the number of times the loops run, realize that each iteration performs O(1) work, and multiply. QVVERTYVS (hm?) 10:12, 18 January 2016 (UTC)
thread_title:  Time Complexity 

message: "if s[i-1] = t[j-1]" should have been "if s[i] = t[j]", since s and t are declared as "char s[1..m], char t[1..n]", and the loops go from 1 to m and 1 to n.  i-1 and j-1 are needed in implementations in other languages where string character indexing starts at 0 (e.g C).  Fixed.  --Anon
:Someone at 68.99.153.115 put the "-1"s back in.  We should keep an eye one this to make sure they don't do it again.  Fixed, Again. Adam Butt (talk) 17:01, 26 February 2009 (UTC)
replies: text: Appeared where? There is no such code currently in the article. -- X7q (talk) 19:34, 25 November 2011 (UTC)

text: There isn't any in the actual article, but I've seen this as the implementation everywhere I've looked, as well as within this discussion page. -- Lee.  — Preceding unsigned comment added by 86.150.194.119 (talk) 21:42, 25 November 2011 (UTC)

text: Both codes work. Essentially the difference between them is only that the latter code incorporates the fact that edit distance between two strings doesn't change if you drop their common suffixes/prefixes. So you don't need to check all possibilities in this case to compute minimum, it is always optimal to just drop the common suffix. -- X7q (talk) 22:45, 25 November 2011 (UTC)
thread_title:  Incorrect Calculation of 'cost' 

message: Levenshtein's paper was published in 1966, not 1965. cf. V. Levenshtein,  Binary codes capable of correcting deletions, insertions and reversals. Soviet Physice – Doklady 10, pp. 707-710, 1966. ralian 03:15, 11 April 2007 (UTC)
replies: text: The Russian version at http://www.mathnet.ru/links/e8cd05ac34a785493c702e3ed733e49a/dan31411.pdf lists 1965.  — Preceding unsigned comment added by 62.225.68.26 (talk) 11:45, 5 June 2018 (UTC)
thread_title:  Incorrect Date 

message: Why in the pseudocode do int arrays start at index 0 and char arrays start at index 1?
:Because it simplifies the algorithm. Anything else would require confusing offsets to be inserted for one array or the other. If you read the correctness section you can see why this numbering fits with the invariants. Deco 02:15, 19 Feb 2005 (UTC)
replies: text: The purpose of pseudocode is not to make the algorithm easily implementable in your-limited-language-of-choice (if it was, we would write algorithms in it, not pseudocode.) It is to give the clearest explanation of the algorithm, and conceptually, this algorithm indexes strings from one and the distance matrix from 0. As stated in the invariants section, this is because D[0][0] means the distance from the zero-length string to the zero-length string (and the algorithm needs this) and string[..0] must be the zero-length string, not a length-1 string. I've just implemented this in my-limited-language-of-choice, and I thought about it this way (without looking at this page -- I came here to check myself), although of course I didn't implement it that way. On a lighter note, this reminded me of XKCD #163 --Taejo|대조 08:15, 7 September 2007 (UTC)
thread_title:  Inconsistent Pseudocode 

message: Here is an O(n) version, using iteration over an initial row. This is much faster (with GHC; have not tried others) since
* it is O(n) in space,
* laziness left away by GHC strictness analysis is removed at one point using seq,
* it is tail recursive in the outer iterations
        distance :: String -> String -> Int
        distance s1 s2 = iter s1 s2 [0..length s2] where
                iter (c:cs) s2 row@(e:es) =
                        iter cs s2 (e' : rest e' c s2 row) where
                                e' = e + 1
                iter [] _ row = last row
                iter _ _ _ = error "iter (distance): unexpected arguments"
                rest e c (c2:c2s) (e1:es@(e2:es')) =
                        seq k (k : rest k c c2s es) where
                                k = (min (e1 + if c == c2 then 0 else 1) $
                                        min (e+1) (e2+1))
                rest _ _ [] _ = []
                rest _ _ _ _ = error "rest (distance): unexpected arguments"
-- Abhay Parvate 05:33, 4 July 2006 (UTC)
thread_title:  An O(n) in space, faster, stricter, tail recursive version 

message: I don't see any particular reason to point out his ethnicity/religion in this article. If you want to put it in his article, be my guest -- but please provide documentation of some sort.--SarekOfVulcan 21:22, 3 November 2005 (UTC)
replies: text: I believe the point of the edit was to indicate that Levenshtein was not an ethnic Russian, just a Jewish guy who lived in Russia. As you suggest, I think such fine points of Levenshtein's nationality are best reserved for his own article, if anyone can drag up enough info to write it. Deco 00:23, 4 November 2005 (UTC)
replies: text: I dragged up enough on the web to stub it.--SarekOfVulcan 00:41, 4 November 2005 (UTC)
replies: text: It might help when learning to pronounce the name of the algorithm. -- Mikeblas 08:13, 3 January 2006 (UTC)
thread_title:  Levenshtein's nationality/ethnicity 

message: I can transform "kitten" into "sitting" in two steps:
* kitten
* (delete "kitten")
* sitting (insert "sitting" at end)
Can someone explain why this is not acceptable?  Are we talking single-character edits here?  --Doradus 17:48, 16 January 2006 (UTC)
replies: text: I went ahead and added "of a single character" to the intro sentence.  Please feel free to revert if I have this wrong.  --Doradus 17:51, 16 January 2006 (UTC)
replies: text: I guess it doesn't hurt to be pedantic. Deco 18:20, 16 January 2006 (UTC)
replies: text: Heh...  Well, if you use "diff" you wind up with a list of edits which are most definitely not single-character edits.  Plus, I think I'm a fairly reasonable person, and I didn't realize it was single-character edits until I read the algorithm.  --Doradus 23:46, 16 January 2006 (UTC)
replies: text: Sorry for the confusion, I hope it's clearer now. :-) Deco 00:15, 17 January 2006 (UTC)

text: Try to do this two strings of 40 billion characters. Or, do it with your examples, but 99 billion times in a minute. 92.81.167.231 (talk) 23:48, 31 January 2010 (UTC)
thread_title:  Minimality 

message: I removed the Haskell example, for two reasons:
# it was the only instance of Haskell code on Wikipedia outside the Haskell article itself
# it relied on compiler support for memoization, which is "not guaranteed" according to the accompanying text.
If anyone knows of a language, not terribly obscure, in which memoization is guaranteed by the standard, please add an implementation in such a language. I think the clarity of such an implementation would be a great addition to the article.
For more information on my recent edits to example-code articles, and proposal to get rid of a lot of relatively esoteric example code along the lines that other editors have suggested in this section, see Wikipedia talk:WikiProject Programming languages#Category:Articles_with_example_code_proposal_and_call_for_volunteers. --Quuxplusone 01:34, 4 December 2006 (UTC)
thread_title: Haskell

message: Using the Common Lisp implementation described in the article, I get notably wrong results:
 [11]> (levenshtein-distance "cow" "cow")
 3
Whoever added this should probably fix it. The Levenshtein distance from any string to itself should be zero. --FOo 01:11, 28 August 2006 (UTC)
thread_title:  Common Lisp version broken 

message: I removed the implementations and replaced with a link even though we already had one.  It seems they were ignoring it anyway.  I didn't even look at the source code to see how good the implementations were... I'm not sure I care.  I'm not against people sharing their implementations, even multiple implementations per language, especially if one happens to use o(m) space instead of o(mn), or if there is some advantage to a particular implementation over another.  If two implementations in the article were compared and contrasted, with benefits and disadvantages for both, that might be encyclopedic and useful for a more in depth investigation of the calculation of this metric.  But all I saw was just code.  And I'm not even sure it was good code at that. Root4(one) 03:17, 28 September 2007 (UTC)
thread_title:  RE: Implementations. 

message: In the upper and lower bounds section, point 5 seems to be merely re-stating point 1.  I have taken the liberty of removing point 5, as it is the more complicated of the two.  If this is wrong, feel free to correct me.
--Gnack (talk) 05:21, 7 July 2008 (UTC)
thread_title:  Confusing wording? 

message: Could someone please extend Levenshtein automaton article, as other possible implementation of this string metric? Current stub version is merely nothing. For the particular case when we do not need a transition graph (memory consuming n*m sized matrix) but scalar result only, FSM approach looks more appealing than dynamic one.  —Preceding unsigned comment added by 91.78.191.197 (talk) 13:17, 28 July 2008 (UTC)
thread_title:  Levenshtein automaton as Finite State Machine approach 

message: The second para of the article currently says this  "is often used in applications that need to determine how similar, or different, two strings are, such as spell checkers."  Shouldn't that be "spell *correctors*"?  A spell checker need only check for exact matches, whereas a spell corrector needs to find the closest match(es). Mcswell (talk) 13:41, 13 October 2008 (UTC)
replies: text: I think that your remark makes sense but spell checker IS the commonly used word for such applications whether or not the intended end is correction or just exact match lookup.   (talk) 16:51, 31 July 2009 (UTC)
replies: text:  Don't some of them hold only a limited set of exact words and guess variations (tenses etc) of the words based on rules and stuff? --TiagoTiago (talk) 19:13, 20 October 2011 (UTC)
thread_title:  Spell checking or correction? 

message: There was an article about Levenshtein distance in Dr. Dobbs Magazine: s. http://drdobbs.com/architecture-and-design/184408746?pgno=2
Maybe someone who knows how to include citations in the article could add this.  — Preceding unsigned comment added by 84.128.62.142 (talk) 16:49, 2 July 2011 (UTC)
thread_title:  Citation 

message: The section Relationship with other edit distance metrics states that the length of the longest common subsequence gives an edit distance metric given addition and deletion as the allowable edit operations.  This seems incorrect.  If two strings have length s_1 and s_2 with a longest common subsequence of l symbols, then the edit distance should be s_1 + s_2 - 2l.  Am I making some silly mistake?  Otherwise this should be fixed. Augurar (talk) 02:01, 5 January 2012 (UTC)
replies: text: You're right, what should be said is that the work required to do one or the other is the same, or that the metrics can be expressed in terms of the lengths of the strings and their longest common subsequence. Rp (talk) 16:09, 6 January 2012 (UTC)
thread_title:  Metric with only additions and deletions 

message: When the OR is removed from this page, what remains is a rather trivial modification of Levenshtein distance. 
QVVERTYVS (hm?) 21:21, 25 November 2013 (UTC)
replies: text: In favor of merger. What is "OR"? I found the modified D-L (as described in Lowrance and Wagner in the references) to be quite distinct from simple L. distance.  I would not want to see those references removed (or the pseudocode which is the only real explanation provided for  how the algorithm works at the moment.)  --Yoderj (talk) 20:44, 14 July 2014 (UTC)
replies: text: Original research. Sorry for the jargon. What I'd like to keep is exactly the well-sourced bits. QVVERTYVS (hm?) 21:49, 14 July 2014 (UTC)
thread_title:  Proposed merge with [[Damerau–Levenshtein distance]] 

message: Does this variation of edit distance really deserve an article of its own? --Sigmundur (talk) 13:34, 31 December 2014 (UTC)
replies: text: The thing is that I started rewriting and generalizing this article's text as the article Edit distance. Levenshtein distance is the most common edit distance, so I think it deserves its own article, but we need to move some content around to put everything in its proper place. QVVERTYVS (hm?) 09:55, 16 March 2015 (UTC)
thread_title:  Most text is duplicate from [[Edit distance]] 

message: The start index of the string is 1 but the start index of the 2D int array is 0. This is an inconsistency that can cause confusion, especially when translated into a programming language. Most programming languages stick with either 1 or 0 but not both.  — Preceding unsigned comment added by Blackraven36 (talk • contribs) 18:19, 15 March 2015 (UTC)
thread_title:  Inconsistent Start Index 

message: There is a problem in 4.3 pseudocode: line v1[i] = i+1; causes writing outside the array boundary when s.Length > t.Length+1.  — Preceding unsigned comment added by 95.80.245.102 (talk) 12:47, 3 July 2015 (UTC)
thread_title:  Incorrect pseudocode 

message: I'm not sure why we're presenting a (presumably) fully working C implementation of the naïve recursive algorithm. No serious source presents this algorithm because it's so extremely inefficient. Anyone who wants to try it can derive it from the mathematical definition if they want. QVVERTYVS (hm?) 12:29, 14 October 2015 (UTC)
replies: text: I'm happy keeping the slow and stupid algorithm. If nothing else, it serves as a set up for the more efficient versions. Glrx (talk) 16:19, 20 October 2015 (UTC)
thread_title:  Recursive C implementation 

message: The current  page shows:
function LevenshteinDistance(char s[1..m], char t[1..n]):
    // create two work vectors of integer distances
    declare int v0[n + 1]
    declare int v1[n + 1]
I believe this should be:
function LevenshteinDistance(char s[1..m], char t[1..n]):
    // create two work vectors of integer distances
    declare int v0[n + 1]
    declare int v1[m + 1]
--165.225.81.1 (talk) 17:06, 5 March 2019 (UTC)
replies: text: No. The dimensions are correct. Both v0 and v1 represent rows that are n+1 columns wide. Glrx (talk) 19:00, 3 April 2019 (UTC)
thread_title:  Error in Two Row algorithm 

message: There is an implied loop in the statement
        // copy v1 (current row) to v0 (previous row) for next iteration
        swap v0 with v1
This loop just slows the algorithm down, and for no purpose. If the arrays were declared with an inner index then a 'swap' would consist of switching the inner index from 0 to 1 or 1 to 0.
I'm not going to clutter the existing code on the page, but for coders searching for an algorithm this should be noted.
— Preceding unsigned comment added by 165.225.81.1 (talk • contribs) 17:24, 5 March 2019 (UTC)
replies: text: There is no "implied loop". The array names are swapped rather than the contents of the arrays. Glrx (talk) 18:58, 3 April 2019 (UTC)
thread_title:  Efficiency in the Two Row algorithm 

message: The existing "naïve" implementation was in my opinion a good deal more naïve than it had to be.  I've gone ahead and replaced the code sample with a slight variation on the algorithm that makes it a good deal faster.  Of course the point of the naïve implementation was not to be fast, but to be easy to understand.  It is my opinion that the new version is easier to understand than the previous version.
The change to the algorithm is to not branch when looking at identical characters.  The previous algorithm would at every step branch into three calls, one for deletion, one for insertion, and one for both replacement and no action with a cost calculated based on the characters at the read heads.  The new version first checks the values read heads, then if they are the same, move both heads forward (equivalent to no action), only branching if they are different into the three possible moves (insertion, deletion, replacement).
This works properly since \forall s,t,a : d(s,t) = d(a:s,a:t), thus when calculating d(a:s,a:t), no branching is needed simply calculate d(s,t).
This is faster because it branches far less frequently.  Every place this algorithm branches the old one would too, but there are some places the old one branched that this one does not.  If you would like to verify this simply compare a very long string with itself on both versions.  The new version is nearly instant, since it is linear on equal strings while the old version stuggles to handle strings reaching even 100 characters, since it is exponential on equal strings.
This is, in my opinion, easier to understand because it separates the logic for performing steps off from simply moving through the string.  The cost calculation in the old version was a little too abstract, and having both no action and replacement occupy the same call was a little confusing.
Asside from the change of algorithm there are a few other changes.  The big change is that the code is now in Haskell instead of C, the biggest reason for this change is that I am more comfortable at writing high quality example Haskell than I am at doing so in C.  If there is a real good case that C should be used instead then someone confident in C should replace this code with C code that implements the new algorithm sensibly. The other reason is that Haskell is in its nature a recursive language, making recursive algorithms more clearly expressed in Haskell than C. I've avoided using the more opaque Haskell syntax like pattern guards, instead opting for if then else, to make this as approachable as possible to persons without Haskell knowledge.
The other change is that the algorithm now moves left to right instead of right to left.  This is a product of the differences between Haskell lists and C arrays.  I don't think this change impacts clarity.
AquitaneHungerForce (talk) 16:49, 8 March 2020 (UTC)
thread_title:  Overly Naïve Implementation 

message: I wanted to add a reducer-based Clojure implementation because reducers are a new functional programming concept ("new" as in "not around back in 2007 when the article was started"), and is an additional way of doing it (i.e. not iterative or recursive). It is less efficient computationally, but also terser, so I feel it merits adding. However, I saw the note not to add implementations and to come to the talk page, so I'll pop it here and see what others think:
(defn levenshtein [a b]
   (peek
      (reduce
         (fn [last this]
            (reduce
               (fn [row [diagonal above other]] (let [update (if ( = other this) diagonal (inc (min diagonal above (peek row))))] (conj row update)))
               [(inc (first last))]
               (map vector last (next last) b)))
         (map #(identity %2) (cons nil b) (range))
         a)))
--139.133.214.105 (talk) 11:38, 8 August 2022 (UTC)
thread_title:  Add reducer-based implementation? 

message: See https://stackoverflow.com/a/4057585/1709587 and http://www.berghel.net/publications/asm/asm.php, for instance.
"Where the earlier algorithm of Wagner and Fisher [6] was implemented in time O(n²), Ukkonen's algorithm runs in O(s * n) time, for edit distance s and string length n."
Obviously in the worst case s=n (so if you want to characterise the worst-case time complexity purely in terms of n, it's still O(n²)) but the point is that with the right algorithm, the time complexity approaches linear as the strings being compared approach being identical. This way of characterising time complexity of edit distance calculations is fairly normal - see also how the Myers diff algorithm (for calculating diffs between strings, with allowable edits being only insertions and deletions) is characterised (both typically and in the title of Myers' paper) as an O(nd) algorithm, where d is edit distance. ExplodingCabbage (talk) 09:14, 23 November 2023 (UTC)
thread_title:  We should explain that time complexity can be expressed in terms of both n and s (where s is edit distance), and that O(n*s) algorithms exist