Нещодавно я реалізував алгоритм відстані Дамерау-Левенштайн від псевдокоду у Вікіпедії. Я не міг знайти ніякого пояснення того , як саме вона працює і псевдокод використовує імена повністю неінформативні змінні , як DA
, DB
, i1
, і j1
що залишив мене почухав голову.
Ось моя реалізація в Python: https://gist.github.com/badocelot/5327337
Реалізація Python допомогла мені пройти програму і зрозуміти, що відбувається, перейменувавши змінні на більш корисні імена. Я був досить знайомий з підходом Вагнера-Фішера до обчислення відстані Левенштейна, що у мене був орієнтир.
Я ризикую бути надмірно довгим, ось як я розумію Дамерау-Левенштейн:
Змінні таємниці:
DA
(last_row
у моєму коді) - це вид карти, що містить останній рядок, на якому бачили кожен елемент; в моєму коді це власне словник PythonDB
(last_match_col
) містить останній стовпець, де літера уb
відповідній буквіa
для поточного рядкаi1
(last_matching_row
) - це номер рядкаDA
для поточної літери вb
j1
- це лише копія значенняDB
/last_match_col
до того, як воно потенційно оновиться; у своєму коді я щойно перемістився там, деlast_match_col
оновлена та усунена ця змінна
Вартість транспозиції:
H[i1][j1] + (i-i1-1) + 1 + (j-j1-1)
обчислює вартість заміни поточного символу b
на останній символ, за яким b
відомо, що знаходиться a
(остання відповідність), обробляючи всі символи між ними як доповнення або видалення.
Компоненти вартості:
H[i1][j1]
повертає базову вартість до точки в розрахунках перед транспозицією, оскільки пошук транспозиції визнає недійсною попередню роботу(i-i1-1)
- відстань між поточним рядком та останнім рядком, що відповідає поточному символу, що є кількістю видалення, яке потрібно(j-j1-1)
- відстань між поточним стовпцем та останнім стовпцем із збігом, що є кількістю доповнень- Додатковим
+ 1
є лише вартість самого транспозиції
Якщо цей аналіз невірний, я хотів би знати, де я пішов не так. Як я вже сказав, я не міг знайти будь - або докладне пояснення того , як працює алгоритм онлайн.
Поліпшена версія?
Зрозумівши , що з, хоча, це мене вразило , що при розрахунку вартості обох додавань і вилучень між транспонуватися буквами здавалося помилковим: одне доповнення та один видалення еквівалентно заміщенню, що це не перевіряє.
Якщо все правильно, рішення має бути тривіальним: вартість букв між перенесеними літерами повинна бути більшою за кількість доповнень та видалень: перетворити якомога більше підстановок та додати будь-які доповнені або видалені залишки.
Отже вартість буде:
H[i1][j1] + max((i-i1-1), (j-j1-1)) + 1
Ось мій код цієї версії: https://gist.github.com/badocelot/5327427
З деяких простих тестів це здається правильним. Наприклад, "abcdef" -> "abcfad" дає відстань редагування 2 (транспоніруйте "d" і "f", змініть "e" на "a"), тоді як вихідний алгоритм дає відстань 3 (або три останніх літери - це заміщення, або 1 перенесення + 1 додавання + 1 видалення).
Тепер я не можу бути першою людиною, яка подумала про це. Отже, чому я не наткнувся на це? Я просто не шукав досить довго? Або є якась тонка вада, яка заважає цьому реально працювати?