حساب کاربری
​
زمان تقریبی مطالعه: 7 دقیقه
لینک کوتاه

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

بزرگترین زیردنباله مشترک (به انگلیسی: Longest Common Subsequence)، روشی است که برای پیدا کردن بزرگترین زیردنباله در مجموعه‌ای از دنباله‌ها (غالباً دو دنباله) به کار می‌رود و مسئله‌ای قدیمی در علم کامپیوتر است. تفاوت این مسئله با مسئله ی بزرگترین زیررشته ی مشترک در این است که برای یک زیردنباله از یک رشته، نیازی نیست که اعضای آن مجاور یک دیگر باشند و به‌طور متوالی آمده باشند. این مسئله اساس کار برنامه‌های مقایسه‌کننده فایل است که تفاوت دو فایل را نمایش می‌دهد. همین طور در بیوانفورماتیک برای مقایسه رشته‌های دی ان ای کاربرد دارد.

فهرست

  • ۱ با مسئله بزرگترین زیررشته مشترک اشتباه نشود
  • ۲ شرح مسئله
  • ۳ راه حل برای دو دنباله
    • ۳.۱ یک راه حل بازگشتی برای پیداکردن LCS
  • ۴ پیچیدگی زمانی
  • ۵ پانویس‌ها
  • ۶ منابع

با مسئله بزرگترین زیررشته مشترک اشتباه نشود

در علوم کامپیوتر، مسئله بلندترین زیررشته مشترک برای یافتن بلندترین رشته(یا رشته‌هایی) که زیر رشته (یا زیررشته‌هایی)از دویاچندین رشته هستند،می‌باشد.این مسئله نباید با مسئله بلندترین زیردنباله مشترک اشتباه گرفته شود.(برای توضیحی در مورد تفاوت میان یک زیررشته و یک زیردنباله به زیررشته درمقابل زیردنباله رجوع کنید).

شرح مسئله

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

S 1 = A C C G G T C G A G T G C G C G G A G C C G G C C G A A

S 2 = G T C G T T C G G A A T G C C G T T G C T C T G T A A A

هدف مقایسه این دو رشته و پیدا کردن شباهت بین آن‌ها است. بزرگترین زیردنباله مشترک این‌طور تعریف می‌شود که دنباله‌ای مانند S 3

است به‌طوری‌که حروف موجود در S 3
با حفظ ترتیب در S 1
و S 2
موجود باشد. اما مطلقاً لزومی ندارد که متوالی باشد. از طرفی S 3
باید بزرگترین دنباله ممکن با خواص بالا باشد. به‌طور کلی اگر دو رشته X = ⟨ x 1 , x 2 , . . . , x n ⟩
و Y = ⟨ y 1 , y 2 , . . . , y n ⟩
را در نظر بگیریم، یک بلندترین زیر دنباله مشترک را می‌توان با استفاده از برنامه نویسی پویا پیدا کرد.

راه حل برای دو دنباله

مسئله LCS دارای خصوصیت زیر ساختار بهینه (Optimal Substructure) است:

مسئله LCS را می‌توان به زیر مسئله‌های کوچک تر تقسیم نمود.

که این زیر مسئله‌ها جفت دنباله‌های پیشوندی دو دنباله ورودی هستند. اگر داشته باشیم: X = ⟨ x 1 , x 2 , . . . , x n ⟩

٫ آن گاه X i
به این صورت تعریف می‌شود: X i = ⟨ x 1 , x 2 , . . . , x i ⟩

فرض کنید X = ⟨ x 1 , x 2 , . . . , x m ⟩

و Y = ⟨ y 1 , y 2 , . . . , y n ⟩
دو رشته باشند و Z = ⟨ z 1 , z 2 , . . . , z k ⟩
بلندترین زیردنباله مشترک یا LCS دو رشته Y و X باشند. پیاده‌سازی الگوریتم با استفاده از روش برنامه‌نویسی پویا به این صورت است:

۱)اگر X m = Y n

باشد٫ آن گاه Z k = X m = Y n
و Z k − 1
یک LCS برای Y k − 1
و X k − 1
است.

۲)اگر X m ≠ Y n

باشد٫ آن گاه Z k ≠ X m
نتیجه می‌دهد که Z یک LCS برای Y
و X m − 1
است.

۳)اگر X m ≠ Y n

باشد٫ آن گاه Z k ≠ Y m
نتیجه می‌دهد که Z یک LCS برای Y n − 1
و X
است.

قضیه فوق نشان می‌دهد: LCS دو رشته٫ در داخل خودش٫ حاوی یک LCS برای پیشوندهای آن دو رشته نیز می‌باشد.

یک راه حل بازگشتی برای پیداکردن LCS

فرض کنید c [ i , j ]

یک عنصر از آرایه C باشد که نشان دهنده طول LCS دو دنباله X i
و Y i
است. بنابراین اگر i=0 یا j=0 باشد٫ یکی از دنباله‌ها دارای طول صفر است و در نتیجه LCS دو دنباله صفر خواهد شد. c [ i , j ] = { 0 i = 0   o r   j = 0 c [ i − 1 , j − 1 ] + 1 i , j > 0   a n d   X i = Y j m a x ( c [ i − 1 , j ] , c [ i , j − 1 ] ) i , j > 0   a n d   X i ≠ Y j
بنابراین وقتی که X i ≠ Y j
باشد٫ زیر مسئله ما پیدا کردن LCS برای X i − 1
و Y i − 1
است. در غیر این صورت ما دو زیر مسئله داریم:

پیدا کردن LCS برای Y i

و X i − 1
و

پیدا کردن LCS برای Y i − 1

و X i

شکل ۱

االگوریتم را با استفاده از روش برنامه‌نویسی پویا پیاده‌سازی می‌کنیم. الگوریتم دو رشته X = ⟨ x 1 , x 2 , . . . , x m ⟩

و Y = ⟨ y 1 , y 2 , . . . , y n ⟩
را به عنوان ورودی دریافت می‌کند(شکل ۲). الگوریتم مقادیر c [ i , j ]
که نشان دهدنده طول LCS است را داخل آرایه c [ 1.. m , 1.. n ]
ذخیره می‌کند. عناصر آرایه به ترتیب row-major پر می‌شوند. یهنی ابتدا اولین سطر c به ترتیب از چپ به راست پر می‌شود، سپس سطر دوم و الی آخر. علاوه بر این الگوریتم آرایه دیگری به نام b [ 1.. m , 1.. n ]
را نیز پر می‌کند. این آرایه جهت حرکت را ذخیره می‌کند.

شکل ۲

مقدار b با علامت‌های جهت ← , ↑ , ↖

پر می‌شود. همین طور مقدار c [ i , j ]
به این بستگی دارد که آیا x i = y i
باشد (خط ۹). جهت فلش‌ها طوری انتخاب می‌شود که همواره حرکت به سمت خانه‌ای با طول LCS بزرکتر را تضمین می‌کند. در نهایت برای پیدا کردن LCS از گوشه سمت راست و پایین آرایه b در جهت پیکان‌ها به سمت گوشه بالا و چپ آرایه حرکت می‌کنیم.

function print_LCS (b,X,i,j)
  if i=0 or j=0 then
     return
  if b[i,j] = '
  
    
      
        ↖
      
    
    
  
' then print_LCS(b,X,i-1,j-1) print x i
else if b[i,j]= ' ↑
' then print_LCS(b,X,i-1,j) else print_LCS(b,X,i,j-1)

پیچیدگی زمانی

زمانی که تعداد دنباله‌های ورودی ثابت باشند٫ این مسئله توسط برنامه‌نویسی پویا در زمان چندجمله‌ای حل می‌شود. فرض کنید N دنباله ورودی به طول n 1 , n 2 , . . . , n N

داشته باشیم. یک راه حل ابتدایی برای جستجوی LCS این است که هر یک از 2 n 1
زیردنباله دنباله اولی را برررسی کنیم که آیا زیر دنباله برای دیگر دنباله‌های ورودی است یا خیر. هر زیر ددنباله در زمانی خطی متناسب با طول دنباله‌های باقی‌مانده بررسی می‌شود. بنابراین زمان الگوریتم برابر خواهد بود با: O ( 2 n 1 ∑ i > 1 n i ) .
برای حالت ورودی با دو دنباله با n و m عنصر٫ پیچیدگی زمانی الگوریتم برابر O ( m n )
خواهد بود. برای تعداد ورودی‌های دلخواه برنامه‌نویسی پویا راه حلی با این زمان ارائه می‌کند: O ( N ∏ i = 1 N n i ) .

توابعی با پیچیدگی کمتر موجود است،

پانویس‌ها

  1. ↑ L. Bergroth and H. Hakonen and T. Raita (۲۰۰۰), "A Survey of Longest Common Subsequence Algorithms", SPIRE (به انگلیسی), IEEE Computer Society, vol. ۰۰, p. ۳۹–۴۸

منابع

  • دکتر جم زاد، منصور. جزوه درسی ساختمان داده و الگوریتم ها٫ نسخه ویرایش نشده. تهران: دانشگاه صنعتی شریف، ۱۳۸۵.
  • توماس اچ کورمن, Charles E. Leiserson, رونالد ریوست and کلیفورد استین (2001), "۱۵٫۴", مقدمه‌ای بر الگوریتم‌ها (به انگلیسی) (second ed.), MIT Press and McGraw-Hill, p. ۳۵۰–۳۵۵
آخرین نظرات
کلیه حقوق این تارنما متعلق به فرا دانشنامه ویکی بین است.