آرین سلیمان‌زاده
  • خانه
  • وبلاگ
  • پادکست‌ها
  • ویدیوها
  • کتابخانه
  • تماس با من
العربيةArabic
DeutschGerman
EnglishEnglish
فارسیPersian
한국어Korean
中文Chinese
پنل•تماس سریع

Languages

Choose your interface locale

ar

العربية

Arabic

de

Deutsch

German

en

English

English

fa

فارسی

Persian

ko

한국어

Korean

zh

中文

Chinese

درخواست همکاری یا مشاوره

پیام خود را درباره همکاری، مشاوره، توسعه محصول یا پروژه نرم‌افزاری ارسال کنید.

LinkedInپاسخ سریع
خانه/مقاله‌ها/فاصله لونشتاین (Levenshtein Distance) چیست؟ آموزش Edit Distance با Dynamic Programming
Algorithmsمقاله

فاصله لونشتاین (Levenshtein Distance) چیست؟ آموزش Edit Distance با Dynamic Programming

Levenshtein Distance حداقل تعداد عملیات درج، حذف و جایگزینی لازم برای تبدیل یک رشته به رشته دیگر را محاسبه می‌کند. در این مقاله الگوریتم را با مثال، Dynamic Programming، پیاده‌سازی TypeScript و کاربردهای واقعی بررسی می‌کنیم.

۲۸ مرداد ۱۴۰۵9 دقیقه مطالعه16 بازدید
#Algorithms#Levenshtein Distance#Edit Distance#Dynamic Programming#String Algorithms#TypeScript#Computer Science

Arian Soleimanzadeh

Software Engineer & Researcher

نمایش الگوریتم Levenshtein Distance و جدول Dynamic Programming برای تبدیل دو رشته

Arian Soleimanzadeh

هوش مصنوعی · کد · محصول

پژوهش + مهندسی
در این صفحه
یک مثال معروفچرا مقایسه ساده کافی نیست؟مسئله را چگونه حل کنیم؟Base Caseهارابطه اصلی Dynamic ProgrammingInsertDeleteReplaceمثال ساده با جدول DPپیاده‌سازی TypeScriptپیچیدگی زمانی و فضاییآیا می‌توان Space Complexity را کاهش داد؟Levenshtein Distance در Spell Checkerکاربرد در Fuzzy Searchکاربرد در Data Cleaning و Deduplicationکاربرد در NLPکاربرد در DNA و BioinformaticsLevenshtein Distance و Hamming DistanceLevenshtein و Longest Common SubsequenceWeighted Edit Distance چیست؟یک سؤال رایج مصاحبهچرا Greedy به‌سادگی جواب نمی‌دهد؟اشتباهات رایج در پیاده‌سازیاشتباه در Indexهافراموش کردن Base Caseهااضافه کردن هزینه هنگام Matchاشتباه گرفتن Insert و Deleteچه زمانی Levenshtein انتخاب خوبی نیست؟جمع‌بندی

Levenshtein Distance که معمولاً با عنوان Edit Distance نیز شناخته می‌شود، معیاری برای اندازه‌گیری میزان تفاوت میان دو رشته است.

ایده اصلی بسیار ساده است:

حداقل چند عملیات لازم است تا یک رشته را به رشته دیگری تبدیل کنیم؟

در نسخه استاندارد Levenshtein سه عملیات مجاز داریم:

  • Insert — اضافه کردن یک کاراکتر
  • Delete — حذف یک کاراکتر
  • Replace — جایگزینی یک کاراکتر

هر عملیات معمولاً هزینه 1 دارد.

برای مثال:

Code
123
cat
↓
cut

فقط کافی است a را با u جایگزین کنیم.

بنابراین:

Code
1
Levenshtein Distance = 1

یک مثال معروف

دو رشته زیر را در نظر بگیرید:

Code
12
kitten
sitting

می‌توانیم تبدیل را این‌طور انجام دهیم:

Code
1234567
kitten
↓ Replace k → s
sitten
↓ Replace e → i
sittin
↓ Insert g
sitting

در مجموع سه عملیات انجام شده است:

Code
1
Levenshtein Distance = 3

این مقدار حداقل تعداد عملیات لازم است.


چرا مقایسه ساده کافی نیست؟

در Hamming Distance فقط موقعیت‌های متناظر را مقایسه می‌کردیم و دو رشته باید طول برابر می‌داشتند.

اما در Levenshtein Distance می‌توانیم رشته‌هایی با طول متفاوت داشته باشیم.

مثلاً:

Code
12
cat
cats

فقط یک Insert لازم داریم:

Code
1
cat → cats

پس:

Code
1
Distance = 1

این ویژگی باعث می‌شود Levenshtein برای Spell Checking، Fuzzy Search و مقایسه متن بسیار کاربردی‌تر باشد.


مسئله را چگونه حل کنیم؟

فرض کنید دو رشته داریم:

Code
12
word1 = "horse"
word2 = "ros"

می‌خواهیم کمترین تعداد عملیات را پیدا کنیم.

برای این کار معمولاً از Dynamic Programming استفاده می‌کنیم.

یک ماتریس تعریف می‌کنیم که:

Code
1
dp[i][j]

نشان می‌دهد حداقل هزینه تبدیل:

Code
1
word1[0 ... i-1]

به:

Code
1
word2[0 ... j-1]

چقدر است.


Base Caseها

اگر رشته اول خالی باشد:

Code
1
"" → "abc"

باید سه کاراکتر Insert کنیم.

بنابراین:

Code
1
dp[0][j] = j

اگر رشته دوم خالی باشد:

Code
1
"abc" → ""

باید تمام کاراکترها حذف شوند:

Code
1
dp[i][0] = i

این مقادیر سطر و ستون اول جدول DP را تشکیل می‌دهند.


رابطه اصلی Dynamic Programming

اگر دو کاراکتر فعلی برابر باشند:

Code
1
word1[i - 1] === word2[j - 1]

هیچ عملیات جدیدی لازم نیست:

Code
1
dp[i][j] = dp[i - 1][j - 1]

اما اگر متفاوت باشند، سه انتخاب داریم.

Insert

Code
1
dp[i][j - 1] + 1

Delete

Code
1
dp[i - 1][j] + 1

Replace

Code
1
dp[i - 1][j - 1] + 1

پس:

Code
12345
dp[i][j] = 1 + min(
    dp[i - 1][j],
    dp[i][j - 1],
    dp[i - 1][j - 1]
)

مثال ساده با جدول DP

فرض کنید:

Code
12
word1 = cat
word2 = cut

جدول اولیه:

Code
12345
      ""  c  u  t
""     0  1  2  3
c      1
 a     2
 t     3

پس از تکمیل جدول:

Code
12345
      ""  c  u  t
""     0  1  2  3
c      1  0  1  2
a      2  1  1  2
t      3  2  2  1

آخرین خانه:

Code
1
dp[3][3] = 1

پس فاصله میان cat و cut برابر 1 است.


پیاده‌سازی TypeScript

TypeScript
12345678910111213141516171819202122232425262728293031323334353637
function levenshteinDistance(a: string, b: string): number {
  const rows = a.length + 1;
  const cols = b.length + 1;

  const dp: number[][] = Array.from(
    { length: rows },
    () => new Array(cols).fill(0)
  );

  for (let i = 0; i < rows; i++) {
    dp[i][0] = i;
  }

  for (let j = 0; j < cols; j++) {
    dp[0][j] = j;
  }

  for (let i = 1; i < rows; i++) {
    for (let j = 1; j < cols; j++) {
      if (a[i - 1] === b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1];
      } else {
        dp[i][j] = 1 + Math.min(
          dp[i - 1][j],
          dp[i][j - 1],
          dp[i - 1][j - 1]
        );
      }
    }
  }

  return dp[a.length][b.length];
}

console.log(
  levenshteinDistance("kitten", "sitting")
); // 3

پیچیدگی زمانی و فضایی

اگر طول رشته اول n و رشته دوم m باشد:

Code
12
Time Complexity: O(n × m)
Space Complexity: O(n × m)

زیرا تقریباً برای هر ترکیب از موقعیت‌های دو رشته یک Cell محاسبه می‌کنیم.


آیا می‌توان Space Complexity را کاهش داد؟

بله.

برای محاسبه هر Row فقط به Row قبلی نیاز داریم.

بنابراین لازم نیست کل ماتریس را نگه داریم.

می‌توانیم Space Complexity را به:

Code
1
O(min(n, m))

کاهش دهیم.

نمونه TypeScript:

TypeScript
12345678910111213141516171819202122232425262728293031
function levenshteinOptimized(a: string, b: string): number {
  if (a.length < b.length) {
    [a, b] = [b, a];
  }

  let previous = Array.from(
    { length: b.length + 1 },
    (_, i) => i
  );

  for (let i = 1; i <= a.length; i++) {
    const current = new Array(b.length + 1);
    current[0] = i;

    for (let j = 1; j <= b.length; j++) {
      if (a[i - 1] === b[j - 1]) {
        current[j] = previous[j - 1];
      } else {
        current[j] = 1 + Math.min(
          previous[j],
          current[j - 1],
          previous[j - 1]
        );
      }
    }

    previous = current;
  }

  return previous[b.length];
}

Levenshtein Distance در Spell Checker

فرض کنید کاربر کلمه زیر را تایپ کرده است:

Code
1
programing

ولی در Dictionary این کلمات وجود دارند:

Code
123
programming
programmer
processing

می‌توانیم فاصله کاربر با هر کلمه را محاسبه کنیم.

کلمه‌ای که Levenshtein Distance کمتری دارد احتمالاً پیشنهاد مناسب‌تری است.

مثلاً:

Code
1
programing → programming = 1

زیرا فقط یک m کم است.


کاربرد در Fuzzy Search

گاهی کاربر دقیقاً عبارت موجود در Database را وارد نمی‌کند.

برای مثال نام مشتری:

Code
1
Alexander

ولی جستجو می‌کند:

Code
1
Alexnder

Exact Matching ممکن است هیچ نتیجه‌ای برنگرداند.

اما با Edit Distance متوجه می‌شویم که دو عبارت بسیار نزدیک‌اند.

این ایده در سیستم‌هایی مانند:

  • Search
  • CRM
  • Contact Matching
  • Product Search
  • Deduplication

کاربرد دارد.


کاربرد در Data Cleaning و Deduplication

فرض کنید در CRM دو رکورد داریم:

Code
12
Arian Soleimanzadeh
Arian Soleimanzade

ممکن است این دو رکورد متعلق به یک نفر باشند ولی به دلیل Typo دو بار ثبت شده باشند.

Levenshtein Distance می‌تواند یکی از Signalهای تشخیص Duplicate باشد.

البته در سیستم واقعی معمولاً نباید فقط بر اساس نام تصمیم بگیریم؛ داده‌هایی مانند Email، Phone و سایر ویژگی‌ها نیز باید بررسی شوند.


کاربرد در NLP

Levenshtein Distance در بعضی مسائل پردازش زبان طبیعی برای مقایسه Tokenها یا Sequenceها استفاده می‌شود.

برای مثال:

  • Typo Detection
  • Text Normalization
  • OCR Correction
  • Transliteration Matching
  • Approximate String Matching

کاربرد در DNA و Bioinformatics

رشته‌های DNA را می‌توان مانند Sequenceهای متنی تصور کرد.

برای مثال:

Code
12
ACGTAC
ACGTC

Edit Distance می‌تواند میزان اختلاف میان دو Sequence را اندازه‌گیری کند.

البته مسائل واقعی Bioinformatics معمولاً الگوریتم‌ها و Cost Modelهای تخصصی‌تری دارند.


Levenshtein Distance و Hamming Distance

Hamming Distance فقط اختلاف موقعیت‌های متناظر را می‌شمارد و در تعریف استاندارد ورودی‌ها باید هم‌طول باشند.

Code
1234
Hamming:
cat
cut
Distance = 1

اما:

Code
12
cat
cats

برای Hamming استاندارد مناسب نیست.

Levenshtein می‌گوید:

Code
12
Insert s
Distance = 1

بنابراین Levenshtein انعطاف بیشتری دارد ولی محاسبه آن گران‌تر است.


Levenshtein و Longest Common Subsequence

این دو مسئله ارتباط مفهومی نزدیکی دارند، زیرا هر دو معمولاً با Dynamic Programming حل می‌شوند.

اما هدف متفاوت است.

Levenshtein می‌پرسد:

حداقل چند Edit لازم است؟

در حالی که LCS می‌پرسد:

طول بلندترین زیرتوالی مشترک چقدر است؟

درک این تفاوت در مسائل مصاحبه بسیار مهم است.


Weighted Edit Distance چیست؟

همیشه لازم نیست هزینه Insert، Delete و Replace برابر 1 باشد.

مثلاً می‌توانیم تعریف کنیم:

Code
123
Insert  = 1
Delete  = 1
Replace = 2

یا در یک Keyboard Correction System، جایگزینی کلیدهای نزدیک هزینه کمتری داشته باشد.

مثلاً اشتباه:

Code
1
hello → helo

ممکن است Cost متفاوتی نسبت به یک تغییر کاملاً نامرتبط داشته باشد.

این نسخه‌ها به Weighted Edit Distance معروف‌اند.


یک سؤال رایج مصاحبه

صورت مسئله:

دو رشته word1 و word2 داده شده‌اند. حداقل تعداد عملیات Insert، Delete و Replace را برای تبدیل word1 به word2 پیدا کنید.

مثلاً:

Code
12
word1 = horse
word2 = ros

پاسخ:

Code
1
3

این مسئله نمونه کلاسیک Dynamic Programming است.


چرا Greedy به‌سادگی جواب نمی‌دهد؟

ممکن است تصور کنیم در هر mismatch بهترین عملیات فعلی را انتخاب کنیم.

اما یک انتخاب محلی می‌تواند روی ادامه رشته اثر بگذارد.

به همین دلیل باید حالت‌های مختلف Insert، Delete و Replace را مقایسه کنیم.

Dynamic Programming نتیجه مسائل کوچک‌تر را ذخیره می‌کند تا بهترین مسیر کلی پیدا شود.


اشتباهات رایج در پیاده‌سازی

اشتباه در Indexها

در جدول DP، موقعیت i معمولاً مربوط به:

Code
1
a[i - 1]

است، زیرا Row صفر برای رشته خالی استفاده شده است.

فراموش کردن Base Caseها

سطر و ستون اول باید به شکل صحیح مقداردهی شوند.

اضافه کردن هزینه هنگام Match

اگر دو کاراکتر برابر باشند:

Code
1
dp[i][j] = dp[i - 1][j - 1]

و نباید 1 اضافه کنیم.

اشتباه گرفتن Insert و Delete

درک معنای Cellهای مجاور بسیار مهم است:

Code
123
up       → Delete
left     → Insert
diagonal → Replace

چه زمانی Levenshtein انتخاب خوبی نیست؟

اگر میلیون‌ها رشته را بخواهیم با یک Query مقایسه کنیم، اجرای مستقیم Edit Distance برای همه آن‌ها ممکن است بسیار گران باشد.

در سیستم‌های Search واقعی معمولاً ابتدا Candidateها با روش‌هایی مانند:

  • Indexing
  • Prefix Filtering
  • N-grams
  • Trigrams
  • Search Engines

محدود می‌شوند و سپس Similarity دقیق‌تر محاسبه می‌شود.

بنابراین Levenshtein یک ابزار مهم است، اما همیشه نباید آن را روی کل Dataset به صورت Brute Force اجرا کرد.


جمع‌بندی

Levenshtein Distance حداقل تعداد عملیات Insert، Delete و Replace برای تبدیل یک رشته به رشته دیگر را محاسبه می‌کند.

نسخه استاندارد آن یکی از مثال‌های کلاسیک Dynamic Programming است.

Code
1234567
Operations:
Insert
Delete
Replace

Time:  O(n × m)
Space: O(n × m)

و با بهینه‌سازی حافظه می‌توان Space را به:

Code
1
O(min(n, m))

کاهش داد.

مهم‌ترین نکته این است که Levenshtein فقط یک الگوریتم دانشگاهی نیست؛ مفاهیم آن در Spell Checking، Fuzzy Search، CRM Deduplication، Data Cleaning، NLP، OCR و بسیاری از سیستم‌های واقعی کاربرد دارند.

در این صفحه
یک مثال معروفچرا مقایسه ساده کافی نیست؟مسئله را چگونه حل کنیم؟Base Caseهارابطه اصلی Dynamic ProgrammingInsertDeleteReplaceمثال ساده با جدول DPپیاده‌سازی TypeScriptپیچیدگی زمانی و فضاییآیا می‌توان Space Complexity را کاهش داد؟Levenshtein Distance در Spell Checkerکاربرد در Fuzzy Searchکاربرد در Data Cleaning و Deduplicationکاربرد در NLPکاربرد در DNA و BioinformaticsLevenshtein Distance و Hamming DistanceLevenshtein و Longest Common SubsequenceWeighted Edit Distance چیست؟یک سؤال رایج مصاحبهچرا Greedy به‌سادگی جواب نمی‌دهد؟اشتباهات رایج در پیاده‌سازیاشتباه در Indexهافراموش کردن Base Caseهااضافه کردن هزینه هنگام Matchاشتباه گرفتن Insert و Deleteچه زمانی Levenshtein انتخاب خوبی نیست؟جمع‌بندی

جزئیات مقاله

اطلاعات انتشار، زمان مطالعه و تعداد بازدید این محتوا.

انتشار

۲۸ مرداد ۱۴۰۵

آخرین ویرایش

۱۱ آبان ۱۴۰۵

زمان مطالعه

9 دقیقه مطالعه

بازدید

16

نویسنده

Arian Soleimanzadeh

مقاله قبلی

B2B CRM و B2C CRM چه تفاوتی دارند؟ از فرآیند فروش تا معماری نرم‌افزار

مقاله بعدی

الگوریتم Kosaraju چیست و اصلاً چه مشکلی را حل می‌کند؟

آرین سلیمان‌زاده

پورتفولیوی شخصی با تمرکز بر Agentic CRM، سامانه‌های هوشمند کسب‌وکار، مهندسی مدرن وب، طراحی سیستم‌های رابط کاربری و توسعه محصولات نرم‌افزاری کاربردی.

لینک‌های سریع

  • درباره من
  • وبلاگ
  • کتابخانه
  • تماس

ارتباط

  • soleimanzadeh.a.work@gmail.com
  • soleimanzadeh.uni@gmail.com

در دسترس: روزهای کاری

معمولاً پاسخ در ۲۴ ساعت

خبرنامه

به‌روزرسانی‌های مربوط به نوشته‌ها، پروژه‌ها و انتشارهای جدید را دریافت کنید.

© 2026 ariansoleimanzadeh.dev — تمامی حقوق محفوظ است.

لینکدین