الگوریتم تپهنوردی
الگوریتم تپهنوردی (Hill climbing) الگوریتمی است که برای یافتن بهترین پاسخ یک مسئله یا برای پیدا کردن پاسخی از مسئله که به اندازهٔ کافی مناسب و بهینه باشد، استفاده میشود.
پیمایش گراف و پیمایش درخت |
---|
هرس آلفا بتا |
جستار وابسته |
برنامهریزی پویا |
بررسی الگوریتم تپهنوردی
در اینجا بیشتر مسئلههایی مورد بحث قرار میگیرند که چندین پاسخ با ارزش برابر دارند و هدف ما یافتن یکی از آن هاست.
برای بررسی الگوریتم تپهنوردی مسئلهٔ زیر را بررسی میکنیم:
- تابع f به هر یک از اعضای مجموعهٔ متناهی S یک عدد حقیقی نسبت میدهد. هدف ما یافتن عضوی از S است که بیشترین (یا کمترین) مقدار به آن نسبت داده شدهاست.
همان گونه که گفته شد در اینجا فرض بر این است که مقدار تابع f برای چندین عضو مختلف S بیشینه است و هدف ما یافتن تنها یکی از آن هاست. (در غیر این صورت معمولاً الگوریتم تپهنوردی، الگوریتم کار آمدی نیست)
الگوریتم:
ابتدا یکی از اعضای مجموعهٔ S را (به صورت تصادفی) انتخاب میکنیم و آن را A مینامیم. سپس در میان اعضایی از S که به A نزدیکند به دنبال پاسخ مناسب تری برای مسئله میگردیم. به بیانی دیگر نلاش میکنیم در میان اعضایی که به A نزدیکند عضوی بیابیم که مقدار تابع f برای آن بیشتر از باشد. سپس این روند را ادامه میدهیم تا به یک بیشینهٔ نسبی برای f دست یابیم.
بدیهی است که بیشینهٔ نسبی به دست آمده لزوماً پاسخ خواسته شده نیست؛ ولی اگر الگوریتم گفته شده چندین بار تکرار شود میتوان به یافتن پاسخی مطلوب امیدوار بود. (در هر بار اجرای الگوریتم نخستین عضو به صورت تصادفی انتخاب میشود) بدی این الگوریتم این است که در یک تابع که مینیمم ویا ماکزیمم محلی زیادی دارد (مانند تابع Ackley)به راحتی در ان گیر میافتد و قدرت خروج از آنجا را ندارد چون این الگوریتم فقط نقاط اطراف خود را میبیند که در لحظه حضور در یک ماکزیمم محلی این احساس را دارد که از همه نقاط بالاتر است در حالی که چنین نیست و ان در یک ماکزیمم محلی اسیر شدهاست.
سورس برنامه تپهنوردی در محیط دو بعدی (که با f سه بعدی میشود)
Hill Climbing Algorithm
bestEval = -INF;
currentNode = startNode;
bestNode = NULL;
for MAX times
if (EVAL(currentNode)> bestEval)
bestEval = EVAL(currentNode);
bestNode = currentNode;
L = NEIGHBORS(currentNode);
nextEval = -INF;
for all x in L
if (EVAL(x)> nextEval)
currentNode = x;
nextEval = EVAL(x);
return currentNode
چند مثال
فروشندهٔ دورهگرد
یک گراف ساده همبند را که یالهای آن وزن دار است، در نظر بگیرید. هدف یافتن مسیری همیلتونی است که در آن مجموع وزن یالها کمینه (یا به اندازهٔ کافی کم) باشد. (مسیر هامیلتونی مسیری است که شامل همهٔ رأسهای گراف باشد)
برای یافتن چنین مسیری میتوان ابتدا یک مسیر هامیلتونی تصادفی انتخاب نمود و سپس در هر گام با ایجاد تغییری اندک در مسیر، مجموع وزن یالهای آن مسیر را بهینه نمود.
مسئلهٔ n وزیر
میخواهیم در یک صفحهٔ n * n شطرنج، n وزیر شطرنج را به گونهای قرار دهیم که هیچ دو وزیری یکدیگر را تهدید نکنند. (دو وزیر شطرنج در صورتی یکدیگر را تهدید میکنند که در یک سطر یا یک ستون یا یک قطر باشند)
برای یافتن چیدمانی از n وزیر که در آن هیچ دو وزیری یکدیگر را تهدید نمیکنند ابتدا n وزیر را به صورتی تصادفی روی یک صفحهٔ شطرنج n * n قرار میدهیم. (بهتر است این چیدمان به گونهای باشد که هیچ دو مهرهای هم سطر یا هم ستون نباشند) سپس در هر گام با ایجاد تغییراتی اندک در چیدمان مهرهها نلاش میکنیم تعداد زوج مهرههایی که یکدیگر را تهدید میکنند کاهش دهیم. روش عقبگرد تمامی جوابهای مسئله را تولید کرده، و با صرف نظر کردن از گرههای ناامیدکنندهٔ بهینگی قابل توجهی در زمان اجرای الگوریتم ایجاد میکند. اما با افزایش مقدار n و افزایش عمق درخت و تعداد فرزندان هر گره، سرعت اجرا نیز به صورت شبه نمایی افزایش یافته، و کارایی روش پایین میآید.
اگر هدف از حل مسئله تنها رسیدن به یک جواب باشد، روشهای دیگری وجود دارد که کارایی بهتری دارند. این روشها عموماً از چیدمان تصادفی یا شبهتصادفی (تصادفی هوشمند) برای رسیدن به یک جواب استفاده میکنند. اکثر الگوریتمهای تکاملی و مکاشفهای - مانند الگوریتم ژنتیک - در این حالت جوابگوی نیازها هستند.
الگوریتم تپهنوردی تعمیم یافته
همانطور که در الگوریتم تپهنوردی بررسی کردیم، این روش جستجوی محلی دارای مشکل قرار گرفتن در بهینگی محلی است. این مشکل تا حدی است که حتی در مورد مسائل سادهای همچون مسئله ۸ وزیر نیز این روش جستجو از درصد موفقیت بسیار پایینی برخوردار بود.
با این حال میتوان تغییر کوچکی در این الگوریتم داد و تا حدودی میزان موقعیت الگوریتم تپهنوردی را در حل مسائله بهینهسازی بالا برد. مشکل الگوریتم تپهنوردی این بود که هنگام قادر به تغییر حالت از یک محل بهینگی به محل بهینگی دیگر نبود. اما برای گریز از بهینگیهای محلی میتوانیم ترتیبی اتخاذ کنیم که هنگام قرار گرفتن در بهینگیهای محلی به بخش دیگری از فضای حالت جهش کنیم و سپس در آنجا به جستجوی جواب با روش تپهنوردی بپردازیم و این عمل را تا رسیدن به یک ماکزیمم سراسری تکرار کنیم. شبه کد زیر نحوه اجرای این الگوریتم را نشان میدهد:
Procedure HillClimbing
Generate a solution (s') Best = S' Loop S = Best S' = Neighbors (S) Best = SelectBest (S') IF there is no changes in Best solution THEN Jump to new state in state space Until stop criterion satisfied
End
در این روش علاوه بر دو تابع Objective و Neighbor، تابعی با نام Mutate نیز خواهیم داشت که وظیفه این تابع جهش در فضای حالت مسئله خواهد بود. همچنین باید بتوان نقطه بهینگی سراسری را تشخیص داد. به عنوان مثال بهینهترین جواب برای مسئله ۸ وزیر، نقطهای است که در آن تعداد گاردهای جفت وزیرها صفر عدد باشد. اما مسائلی نیز وجود دارند که از ابتدا نمیتوان مقدار بهینگی سراسری مسئله را در آنها تشخیص داد. لازم است ذکر شود که برای مسائل مختلف میتوانیم شرط پایان حلقه و نحوه اجرای الگوریتم را تغییر دهیم. لازم است ذکر شود که تابع Mutate را بهطور کلی میتوان با دو استراتژی مختلف پیادهسازی کرد :
۱. جهش در فضای حالت کوتاه باشد ۲. جهشهای بزرگ در فضای حالت انجام دهد
جهش کوتاه جهشی است که تعداد حالتهای میان حالت فعلی و حالت جهش یافته کم باشد. نقطه مقابل جهش کوتاه نیز جهش بلند میباشد. استفاده از هرکدام از این روشها بستگی به مسئله دارد و نمیتوان در مورد ارجحیت یکی به دیگری اظهار نظر کرد. ما در این برنامه از جهشهای کوتاه برای گریز از بهینگیهای محلی استفاده کردهایم.
الگوریتمهای مشابه
در بسیاری از موارد یافت بیشینهٔ نسبی یا کمینهٔ نسبی با استفاده از الگوریتم تپهنوردی کارساز نیست. در این گونه موارد از الگوریتمهای کارآمد تری چون simulated annealing استفاده میشود. در این الگوریتمها، پیمایش روی اعضای S همواره در جهت افزایش مقدار تابع f نیست. حالت از یک محل بهینگی به محل بهینگی دیگر نبود. اما برای گریز از بهینگیهای محلی میتوانیم ترتیبی اتخاذ کنیم که هنگام قرار گرفتن در بهینگیهای محلی به بخش دیگری از فضای حالت جهش کنیم و سپس در آنجا به جستجوی جواب با روش تپهنوردی بپردازیم و این عمل را تا رسیدن به یک ماکزیمم سراسری تکرار کنیم. شبه کد زیر نحوه اجرای این الگوریتم را نشان میدهد: