Fast Levenshtein distance using a Trie
stevehanov.ca
stevehanov.ca
> Peter Norvig approaches the problem using a different way of thinking. He first stores his dictionary in a hash-table for fast lookup. Then he goes through hundreds, or even thousands of combinations of spelling mutations of the target word and checks if each one is in the dictionary. This system is clever, but breaks down quickly if you want to find words with an error greater than 1.
Norvig writes [1]:
> The literature on spelling correction claims that 80 to 95% of spelling errors are an edit distance of 1 from the target. As we shall see shortly, I put together a development corpus of 270 spelling errors, and found that only 76% of them have edit distance 1. Perhaps the examples I found are harder than typical errors. Anyway, I thought this was not good enough, so we'll need to consider edit distance 2. That's easy: just apply edits1 to all the results of edits1:
I wrote some code for this a few months ago and tried tries, which ultimately rejected in favor of a fast Levenshtein implementation. On a side note the author misses one other possible avenue, which is a family of algorithms using bit Vector operations [2] [3] to measure edit distance.
Anyway IIRC my code went the opposite way. It traversed the word then looked in the trie for matches. This quickly broke down as edit distance increased and I suspect this is what the author is observing in the other implementation.
The author's approach of going the other way is uninteresting idea and I might retrofit that to my code to see how it performs.
[1]: http://norvig.com/spell-correct.html
[2]: http://portal.acm.org/citation.cfm?id=846095
[3]: http://www.odinlake.net/papers/onsjo-cudastrmatch-sigal09.pd...
BTW, do you use speech recognition? I think you typod "an interesting" as "uninteresting" at the end there. :)
http://sphinxsearch.com/downloads/release/
It also lets you feed in your own dictionary from your index and works great for spelling suggestions. I haven't benchmarked it but it seems extremely fast - as sphinx is with most things.
Here's the suggest text file from the sphinx 0.9.9 source:
0) What's this. This sample shows how to implement simple keyword correction suggestions (ie. "did you mean") using Sphinx.
1) Requirements. You will need Sphinx, MySQL, and PHP CLI.
2) Quickstart. (Skip first indexer command to use bundled sample.)
indexer YOURINDEX --config YOURCONFIG.CONF --buildstops
dict.txt 100000 --buildfreqs
cat dict.txt | php suggest.php --builddict > dict.sql
mysql -u root test < dict.sql
indexer --config suggest.conf --all
searchd --config suggest.conf
php suggest.php --query sphynx
O( <number of words> * <max word length> ^2 )
correct ? It seems to me that the upper bound should be O(<number of words> * <max word length>) ^2
Looks like a typo.In fact there is another fairly simple optimization one can make if one is interested only in computing the distance, but not interested in the actual edit sequence. If you look at the algorithm for filling up the table you will see you refer only to the previous row. So one can work with just two consecutive rows, thereby substantially reducing the space complexity, but not the time complexity unfortunately.
The time complexity can however be reduced on average to O(n * d), where n is the length of the longer word and d is the edit distance between the two words, this optimization is by Ukkonen. There have been other optimizations since then. The best I believe gives a time complexity of O(n + d^2)
Having a smaller table => it can fit in the cache => better temporal locality of reference => faster runtime.
(This insight could be implemented directly in Haskell by using e.g. the MemoTrie package)
The standard imperative Levenshtein algorithm (with dynamic programming) is also basically just a memoised version of that recursive version -- except that the memoisation is cleared immediately after the result you originally asked for is returned.
I (as well as a dozen other students) were taught an IR course where we had to implement a search engine from scratch. We used tries both to provide spelling suggestions and to build inverted indices for text. That was 7 years ago.
Not to discredit Steve, he's done great work and his article is a great resource, but it's not the groundbreaking discovery you make it to be.
PS. Tries are awesome. <3
yielded results that surpass those of the academics and giants of the field
That's an impressive pinch :)I love Tries, I implemented one last year for a bog standard auto-complete suggestion box. While they may not be the fastest or most efficient data structures in the world, they're simple to write and have very reasonable performance.