روش پَس‌گرد (به انگلیسی: Backtracking) یکی از شیوه‌های کلی جستجوی فضای حالت برای حل مسائل ترکیبیاتی است. این شیوه، تمام ترکیب‌های ممکن را بررسی می‌کند تا یک جواب پیدا کند یا تمام جواب‌های ممکن را شمارش کند. تنها مزیت روش پسگرد در این است که می‌توان حالت‌هایی را بدون آنکه صریحاً بررسی شوند، با در نظر گرفتن ویژگی‌های مسئله، کنار گذاشت. واژهٔ BackTrack به وسیلهٔ یک ریاضی‌دان آمریکایی به نام D.H. lehmer در سال ۱۹۵۰ ابداع شد.

در بسیاری از مسائل می‌توان بدون نیاز به بررسی تمام ترکیبات، از اینکه جواب مسئله توسط چه ترکیبی از مقادیر قابل تحقق نیست اطمینان بدست آورد و بدین ترتیب بخش‌های بزرگی از فضای جستجو را کنار گذاشت. اگر حل مسئله با انتساب مقادیر به n متغیر محقق شود، در بسیار مسائل با توجه به ویژگی‌های مسئله، می‌توان پیش از کامل شدن انتساب مقادیر، از اینکه انتساب جزئی برای رسیدن به جواب، «امیدبخش» نیست اطمینان بدست آورد و از این طریق از بررسی حالت‌هایی که از توسعه این انتساب به دست می‌آیند صرفنظر کرد.

روش پس‌گرد شیوه‌ای در حل مسائل است که از علامت‌های خاصی برای بیان اینکه راه حل کاندیدی به حل مسئله می‌انجامد یا خیر استفاده می‌کند. این رویکرد برای حل مسائل درخت فضای آن مسئله را ایجاد کرده و تعیین می‌کند کدام گره امید بخش است. الگوریتم‌های عقب‌گردی از تابلوها یا علامت‌هایی برای بیان اینکه یک راه حل کاندید به حل مسئله نمی‌انجامد استفاده می‌کند. عقبگرد، حالت اصلاح شدهٔ جستجوی عمقی یک درخت می‌باشد.[۱]

مثال اول در روش پس‌گرد مسئله قفل رمزی می‌باشد. یک قفل رمزی شامل n کلید دیجیتالی است که هر کلید فقط دو حالت بسته(۱) یا حالت باز (۰) دارد. می‌دانیم که n کلید دیجیتالی تعداد 2nحالت مختلف را پدیدمی‌آورند و این قفل تنها با یک حالت که همان رمز است باز می‌شود.

مثال دوم در روش پس‌گرد مسئله چند وزیر می‌باشد. هدف این مسئله چیدن n وزیر در یک صفحه شطرنج n*n است به گونه‌ای که هیچ دو وزیری یکدیگر را تهدید نکنند.

مثال سوم در حل مسئله رنگ‌آمیزی گراف می‌باشد. در این مسئله می‌خواهیم تمام راه‌های ممکن جهت رنگ آمیزی گره‌های یک گراف بدون جهت را با استفاده از حداکثر m رنگ متفاوت پیدا کنیم، به گونه‌ای که هیچ دو رأس مجاوری هم رنگ نباشند.

مثال چهارم مسئله حاصل جمع زیر مجموعه‌ها می‌باشد. در این مسئله، n عدد صحیح مثبت wi و یک عدد صحیح مثبت w داریم. هدف پیدا کردن تمامی زیر مجموعه‌هایی از این اعداد صحیح می‌باشد که حاصل جمع آن‌ها بربر w است.

پیاده‌سازی

ویرایش

پسگرد همه حالت‌های ممکن را برای جواب بررسی می‌کند تا حالت درست را بیابد. این یک جستجوی عمق اول بین جواب‌های ممکن است. هنگام جستجو اگر راهی که طی می‌شود نتیجه نداد (به جواب نرسید) به نقطهٔ قبلی بازمی‌گردد و راه بعدی را امتحان می‌کند. اگر همه راه‌ها را امتحان کرد و به جواب نرسید، جستجو نا موفق بوده‌است. این الگوریتم معمولاً در قالب توابع بازگشتی پیاده‌سازی می‌شود. به این صورت که در هر بار فراخوانی تابع، با اضافه شدن یک متغیر به‌طور متناوب همهٔ مقادیر ممکن را به آن نسبت می‌دهد و آن مقداری که با فراخوانی‌های بازگشتی بعدی سازگار است را ذخیره می‌کند. روش پسگرد را می‌توان یک پیاده‌سازی بازگشتی از جستجوی عمق-اول دانست.[۲][۳]

کاربردها

ویرایش

یکی از استفاده‌های رایج از این الگوریتم، پیاده‌سازی عبارات باقاعده است. برای مثال الگوی سادهٔ "a*a" بدون استفاده از روش پسگرد با عبارت "aa" سازگار دیده نمی‌شود (چون a دوم با * از بین می‌رود و چیزی برای تطابق با a آخر وجود ندارد) یکی دیگر از موارد استفاده از پس‌گرد، الگوریتم‌های مسیریاب است که با رفت و برگشت بر روی راس‌های یک گراف، کم هزینه‌ترین مسیر را پیدا می‌کند. همچنین استراتژی پسگرد در پیاده‌سازی زبان‌های برنامه‌نویسی و چیزهای دیگر مانند تجزیه متن‌ها کاربرد دارد.

توضیح روش

ویرایش

الگوریتم پیمایش وارونه مجموعه‌ای از زیر مسئله‌ها را می‌شمارد که می‌توانند از طریق راه‌های مختلف کامل شوند و همهٔ راه حل‌های مسئله داده شده را بدهند. کامل شدن به صورت مرحله‌ای و قدم به قدم انجام می‌گیرد. زیر مسئله‌ها گره‌های یک درخت هستند. فرزندهای هر گره زیر مسئله‌هایی هستند که یک قدم کامل تر هستند. برگ‌ها زیر مسئله‌هایی هستند که دیگر نمی‌توانند افزایش یابند. الگوریتم پیمایش وارونه این درخت را به صورت بازگشتی با شروع از ریشه به صورت جستجوی اول عمق جستجو می‌کند. در هر گره c این الگوریتم امتحان می‌کند که آیا c می‌تواند به صورت یک جواب معتبر کامل شود. اگر نتواند زیر درخت به ریشه c قطع می‌شود. در غیر این صورت امتحان می‌کند که آیا c خودش یک جواب معتبر است. اگر بود آن را به کاربر برمی‌گرداند. سپس به صورت بازگشتی زیر درخت‌های c را پیمایش می‌کند.

شبه کد

ویرایش

برای بکار بردن پیمایش وارونه برای دستهٔ خاصی از مسئله‌ها.P را برابر یک نمونه از مسئله که باید حل بشود در نظر می‌گیریم. و ۶ تابع که p را به صورت یک پارامتر می‌گیرند، اولین فرزند c را برمی‌گرداند.

  1. (next(P,s:برادر بعدی s را برمی‌گرداند.
  2. (output(P,c:این تابع c را که جوابی برای P است چاپ می‌کند.

ابتدا ((bt(root(P را صدا می‌زنیم.

procedure bt(c)

 if reject(P,c) then return
 if accept(P,c) then output(P,c)
 sfirst(P,c)
 while s ≠ Λ do
 bt(s)
 snext(P,s)

تحلیل

ویرایش

تابع reject باید boolean باشد و زمانی درست برگرداند که مطمئن باشد c به جواب نمی‌رسد. یک درست دادن اشتباه ممکن است باعث شود که bt به برخی از جواب‌ها نرسد. در عین حال کارایی پیمایش وارونه به درست برگرداندن reject برای زیر مسئله‌های نزدیک ریشه بستگی دارد. اگر همواره غلط برگرداند الگوریتم تبدیل به جست‌جوی کامل می‌شود. توابع first و next فرزندان زیر مسئله c را پیمایش می‌کند. اگر فرزند مورد نظر نبود این دو تابع باید null برگردانند.

نگارخانه

ویرایش

منابع

ویرایش
  1. مقدمه‌ای بر طراحی الگوریتم‌ها، توماس کورمن
  2. «Backtracking Algorithms». geeksforgeeks. دریافت‌شده در ۲۲ مه ۲۰۲۱.
  3. «Backtracking». geeksforgeeks. دریافت‌شده در ۲۲ مه ۲۰۲۱.
  • Gilles Brassard, Paul Bratley (1995), Fundamentals of Algorithmics (به انگلیسی), Prentice-Hall

جستارهای وابسته

ویرایش