الگوریتم حریصانه

روش حریصانه

نام این روش از شخصیت معروف اسکروج گرفته شده‌است. اسکروج به جای آن که به گذشته و آینده فکر کند، تنها انگیزه هر روز او به دست آوردن طلای بیشتر بود. الگوریتم حریصانه(Greedy) نیز مانند شیوه اسکروج می‌باشد.
الگوریتم حریصانه یا آزمند شبیه روش‌های پویا اغلب برای حل مسائل بهینه سازی استفاده می‌شوند. این الگوریتم بهترین انتخاب را با توجه به شرایط مسئله انجام می‌دهد به امید آنکه با ادامهٔ همین روش بهینه‌سازی انجام شود. البته این نکته که بهترین انتخاب فعلی ما را به جواب بهینه می‌رساند را باید اثبات کرد . در شیوه حریصانه در هر مرحله عنصری که بر مبنای معیاری معین ((بهترین))به نظر می‌رسد، بدون توجه به انتخاب‌هایی که قبلاً انجام شده یا در آینده انجام خواهد شد، انتخاب می‌شود. الگوریتم‌های حریصانه اغلب راه حل‌های ساده‌ای هستند. در روش حریصانه برخلاف روش پویا، مسئله به نمونه‌های کوچک‌تر تقسیم نمی‌شود.
الگوریتم حریصانه کمترین تعداد سکه‌هایی که باعث شود مسئله تغییر نماید را تعیین می نماید . این‌ها مراحل یک انسان هستند که سکه با مقدار ۳۶ را با استفاده از سکه‌های با مقادیر ۱و۵و۱۰و۲۰ تعیین می‌کند. سکه با بزرگترین مقدار و کوچکتر از تغییرات باقی‌مانده به عنوان بهینه محلی انتخاب می‌گردد(توجه کنید که در حالت معمول مسئله پیدا کردن تغییرات بااستفاده از برنامه‌ریزی پویا یا Linear programming Integer unknowns جواب بهینه را پیدا می‌کند). پول آمریکا و دیگر کشورهانمونه‌های خاصی هستند که با استفاده از الگوریتم حریصانه می‌توان جواب بهینه آنهارا پیدا کرد).]]
در بسیاری از موارد مسئله از ما می‌خواهد که یک متغیر را کمینه یا بیشینه کنیم یا اینکه صورت مسئله مستقیماً از ما نمی‌خواهد که چیزی را کمینه یا بیشینه کنیم اما می‌توان این مسئله را تبدیل به یک چنین مسئله‌ای کرد. در اکثر مسائل از این دست ما باید تعدادی انتخاب انجام دهیم و مسئله در تعدادی مرحله حل شود.
بازهٔ وسیعی از این مسائل توسط الگوریتم‌های حریصانه قابل حل‌اند. الگوریتم‌های حریصانه در هر مرحله در نظر می‌گیرند که کدام انتخاب در حال حاضر بهترین وضعیت را برای ما رقم می‌زند و آن را انجام می‌دهند بدون در نظر گرفتن این که انجام این حرکت در آینده ممکن است به ضرر ما تمام شود. بنابراین الگوریتم‌های حریصانه همیشه جواب درست را به ما نمی‌دهند اما خیلی وقت‌ها هم اینطور نیست این دسته از الگوریتم‌ها در علوم رایانه کاربرد وسیعی دارند.
حال مثالی برای این‌گونه الگوریتم‌ها می‌اوریم:
فرض کنید می‌خواهیم n تومان را با سکه های۲۵ تومانی۱۰ تومانی۵ تومانی۱ تومانی پرداخت کنیم به‌طوری‌که کمترین تعداد سکه را بپردازیم. ما می‌توانیم یک الگوریتم حریصانه برای حل این مسئله ارائه دهیم که تعداد سکه بهینه را در هر مرحله بدست آورد برای این کار ما در هر مرحله بزرگترین سکه‌ای را که از پول باقی‌مانده تجاوز نکند انتخاب می‌کنیم در این صورت تعداد سکه‌ها بهینه خواهدبود.
        prededure change(
                                                                    for i:=۱ to r    
                                                                        while n≤
                                                                            begin 
                                       add a coin with value  to change
                                                                  n:=n-
                                                                              end
مثال دیگری که می‌توان عنوان کرد مسئلهٔ زمان بندی چندین فعالیت در حال رقابت است که نیاز به استقادهٔ منحصربه‌فرد از یک منبع مشترک با هدف انتخاب مجموعه‌ای با ماکزیمم اندازه از فعالیت‌هایی که متقابلاً با یکدیگر سازگارند، دارند.
فرض کنید یک مجموعهٔ s={a۱،a۲،….an} از n فعالیت پیشنهادی داریم که می خواهند از یک منبع استفاده نمایند، مانند یک تالار سخنرانی که در یک زمان می‌تواند تنها مورد استفادهٔ یک فعالیت قرار گیرد. هر فعالیت ai دارای زمان شروع si و زمان خاتمهٔ fi است به‌طوری‌که ۰≤si<fi<∞ . اگر فعالیت ai انتخاب شود، می‌تواند در طول بازهٔ [si،fi) رخ دهد.
تعریف: فعالیت‌های ai و aj سازگار هستند، اگر بازه‌های [si،fi)و [sj،fj) همپوشانی نداشته باشند.
مسئله انتخاب فعالیت عبارت است از انتخاب یک زیرمجموعه با ماکزیمم اندازه از فعالیت‌های متقابلاً سازگار. برای مثال مجموعهٔ s زیر از فعالیت‌ها را در نظر بگیرید که آن‌ها را به ترتیب صعودی یکنواخت از زمان‌های خاتمه مرتب کرده‌ایم:
                                                                                      i -> ۱  ۲  ۳  ۴  ۵  ۶   ۷  ۸    ۹  ۱۰  ۱۱          
                                                                                      si -> 1  3  0  5  3  5   6  8    8   2  12      
                                                                                      fi -> 4  5  6  7  8  9  10  11  12  13  14
برای این مثال زیر مجموعهٔ {a۳،a۹،a۱۱} شامل فعالیت‌های متقابلاً سازگار است.
اگرچه این زیرمجموعه ماکزیمم نیست، چون زیرمجموعهٔ {a۱،a۴،a۹،a۱۱} بزرگ‌تر از آن است. در حقیقت {a۱،a۴،a۹،a۱۱} بزرگ‌ترین زیرمجموعه از فعالیت‌های متقابلاً سازگار است.
حال یک الگوریتم حریصانه بازگشتی برای حل مسئلهٔ زمان‌بندی فعالیت‌ها ارائه خواهیم نمود.
با تعریف مجموعهٔ sij={ak € s: fi≤sk<fk≤sj} شروع می‌کنیم، به‌طوری‌که sij زیر مجموعه‌ای از فعالیت‌ها در S است که می‌توانند بعد از خاتمه فعالیت ai شروع شده و قبل از شروع فعالیت aj خاتمه یابند. در حقیقت sij شامل همه فعالیت‌هایی است که با ai و aj و همچنین با همه فعالیت‌هایی که بعد از خاتمه ai خاتمه نیافته و همه فعالیت‌هایی که قبل از شروع aj شروع نمی‌شوند، سازگارند. به منظور بیان کل مسئله، فعالیت‌های ساختگی a۰ و an+۱ را اضافه کرده و توافق می‌کنیم که f0=0 و sn+۱=∞ پس S=S۰،n+۱ و محدودهٔ تغییر iو j به صورت ۰≤I،j≤n+۱ خواهد بود.
دامنهٔ تغییر I و j را به صورت زیر می‌توانیم بیشتر محدود کنیم. فرض می‌کنیم که فعالیت‌ها در یک ترتیب یکنواخت صعودی از زمان‌های خاتمه مرتب شده‌اند:
F۰≤f۱≤f۲≤…≤fn≤fn+۱ 
ادعا می‌کنیم که sij=ᴓ هرگاه i≥j باشد. فرض کنید یک فعالیت ak€sij برای i≥j وجود دارد به‌طوری‌که ai بعد از aj در ترتیب مرتب قرار دارد. پس خواهیم داشت fi≤sk<fk≤sj<fj.
بنابراین fi<fj که با این فرض که ai بعد از aj در ترتیب مرتب قرار دارد در تناقض است. می‌توانیم نتیجه بگیریم با این فرض که فعالیت‌ها را در یک ترتیب صعودی یکنواخت از زمان‌های خاتمه مرتب کرده ایم، فضای زیر مسائل انتخاب زیر مجموعه با ماکزیمم اندازه شامل فعالیت‌های متقابلاً سازگاراز sij است، که ۰≤i<j≤n+۱ با آگاهی از این مطلب که تمام sijهای دیگر تهی هستند.
برای مشاهده زیر ساختار مسئله انتخاب فعالیت، تعدادی زیرمسئله غیر تهی sij را در نظر بگیرید. و فرض کنید که یک جواب برای sij شامل تعدادی فعالیت ak است. به‌طوری‌که fi ≤ sk <fk ≤ sj. استفاده از فعالیت ak سبب ایجاد دو زیر مسئله می‌شود، sik(فعالیت‌های که پس از خاتمه فعالیت aiشروع شده و قبل از شروع فعالیت akخاتمه می‌یابند)وskj(فعالیت‌های که پس از خاتمه akشروع شده و قبل از شروع فعالیت ajخاتمه می‌یابند)، که هرکدام از یک زیر مجموعه شامل فعالیت‌های داخل sij تشکیل شده‌اند. جواب sij اجتماع جواب‌های مربوط به sik و skj است، همراه با فعالیت ak. بنابراین تعداد فعالیت‌ها در جواب sij برابر اندازه جواب skj به علاوه یک (برای ak) می‌باشد.
اکنون فرض کنید جواب Aij برای sij شامل فعالیت ak می‌باشد. پس جواب‌های Aik برای sik و Akjبرای skj که در این جواب بهینه برای sij استفاده می‌شوند نیز باید بهینه باشند. بحث برش – و – الصاق معمول در این مورد بکار می‌رود. اگر یک جواب Áik برای sik می‌داشتیم که شامل فعالیت‌های بیشتری از Aik می‌بود، می‌توانستیم Aik را از داخل Aij برش داده و به داخل Áik الصاق نماییم، بنابراین یک جواب دیگر برای Sij با تعداد فعالیت‌های بیشتری از Aij تولید می‌شود. از آنجا که فرض کردیم Aij یک جواب بهینه است، به یک تناقض رسیده ایم. به‌طور مشابه اگر جواب Ákj برای skj را با فعالیت‌های بیشتر از Akj می‌داشتیم، می‌توانستیم Akj را با Ákj جایگزین کنیم تا یک جواب با فعالیت‌های بیشتری از Aij تولید نماییم.
اکنون از زیر ساختار بههینه خود استفاده کرده تا نشان دهیم که می‌توانیم یک جواب بهینه برای مسئله از روی جواب‌های بهینه زیر مسائل بسازیم. مشاهده کردم که هر جواب برای یک زیر مسئله غیر تهی sij شامل فعالیت ak است و آنکه هر جواب بهنه در درون خود شامل جواب‌های بهینه نمونه زیر مسئله‌های sik و skj می‌باشد. بنابراین، می‌توانیم یک زیر مجموعه با ماکزیمم اندازه از فعالیت‌های متقابلاً سازگار درSijبسازیم. با تقسیم مسئله به دو زیرمسئله (یافتن زیر مجموعه‌های با ماکزیمم اندازه از فعالیت‌های متقابلاً سازگار در sikو skj) و پیدا کردن زیرمجموعه‌های با ماکزیمم اندازه Aik و Akjاز فعالیت‌های متقابلاً سازگار برای این زیر مسائل و سپس تشکیل زیر مجموعه Aij با ماکزیمم اندازه شامل فعالیت‌های متقابلاً سازگار به صورت 
Aik υ {ak } υ Akj=Aij
یک جواب بهینه برای کل مسئله یک جواب برای S۰،n+۱ است.

الگوریتم حریص برای حل مسئله پول خرد

مسئله:پس دادن بقیه پول مشتری با استفاده از سکه‌های موجود.
ورودی:انواع سکه‌های موجود(1 سنتی، 5سنتی ,10سنتی،20سنتی،25سنتی و نیم دلاری).
خروجی:پس دادن بقیه پول با حداقل تعداد سکه ها.
                                                                                                          (set Greedy-Applying(c
                                                                                                                               }
                                                                                                        ;{1,5,10,20,25,50}=C
                                                                                                                      ;[0]=S
                                                                                                  (∅=!While(!solution(s)&&c
                                                                                                                           }
                                                                                                            ;(X=SELECT(C
                                                                                                                ;{C=C-{x
                                                                                                       ((IF(FEASIBLE(S,X
                                                                                                            ;{S=S+{X
                                                                                                                           {
                                                                                                             ((if(solution(s
                                                                                                               ;return S
                                                                                                                        else
                                                                                                              (∅)return
                                                                                                                               {

مثال1

در روش حریصانه، رسیدن به هدف در هر گام مستقل از گام قبلی و بعدی است. یعنی در هر مرحله برای رسیدن به هدف نهایی، مستقل از این که در مراحل قبلی چه انتخاب‌هایی صورت گرفته، و انتخاب فعلی ممکن است چه انتخاب‌هایی در پی داشته باشد، انتخابی که در ظاهر بهترین انتخاب ممکن است صورت می‌پذیرد. به همین دلیل است که به این روش، روش حریصانه گفته می‌شود. زمانی که یک دزد عجول و حریص وارد خانه‌ای می‌شود، در مسیر حرکت خود هر وسیله و کالای با ارزشی را داخل کیسه می‌اندازد. وی در این حالت چندان توجهی نمی‌کند که چه اشیائی را قبلاً برداشته، و ممکن است در آینده چه اشیاء گرانبهاتری به دست آورد. او در هر گام تنها از بین اشیاء دم دست خود با ارزش‌ترین آن را انتخاب کرده و به وسایل قبلی اضافه می‌کند.
این روش کاربردهای عمومی دیگری نیز دارد. زمانی که در مقابل خرید از یک فروشگاه یک اسکناس تحویل فروشنده داده می‌شود، وی با یک حساب سرانگشتی سعی می‌کند با حداقل اسکناس‌ها و سکه‌های ممکن باقی‌مانده پول را تولید کرده و به خریدار تحویل دهد. این حساب سرانگشتی ممکن است روش حریصانه باشد.
خریداری از یک فروشگاه یک جنس 64تومانی می خرد و 100 تومان به فروشنده می‌دهد و فروشنده باید 36 تومان به او برگرداند. اگر فروشنده سکه‌های 50،25،10،5،1تومانی (از هرکدام حداقل یک نمونه)داشته باشد چگونه می‌تواند بقیه پول خریدار را برگرداند به نحوی که تعداد سکه ها(در کل) کمترین مقدار ممکن باشد؟
یک راه حل حریصانه به این صورت است:
در ابتدا هیچ سکه در مجموعه جواب نداریم. از بین سکه‌های موجود بزرگ‌ترین ممکن یعنی 25 تومانی را انتخاب می‌کنیم. این مرحله از الگوریتم حریصانه راروال انتخاب(select procedure) گویند. اگر یک سکه 25 تومانی دیگر را انتخاب کنیم حاصل از 36 تومان بیشتر شده، لذا آن را کنار گذاشته به سراغ سکه 10 تومانی می‌رویم. حال بررسی می‌کنیم اگر این سکه 10 تومانی را به مجموعه انتخابی قبلی اضافه کنیم حاصل از 36 تومان بیشتر می‌شود یا خیر. این مرحله را تحقیق عملی بودن(feasibililty check) می نامند. حال اگر این 10 تومان را به25 تومان جمع مجموعه انتخاب شده 35 می‌شود که هنوز به 36 نرسیده‌است. این مرحله را تحقیق حل شدن(Solution check) می‌گوییم. در ادامه سکه‌های دیگر را به ترتیب مقایسه می‌کنیم و در نهایت با انتخاب سکه یک تومانی در کل با 3 سکه (25 تومانی و 10 تومانی و یک تومانی) 36 تومان به دست می‌آید و این حداقل تعداد سکه ممکن است.
توجه کنید در انتخاب فوق ملاک انتخاب ، برای انتخاب بهترین سکه در هر مرحله(بهینه محلی) سکه است و در انتخاب سکه در هر مرحله به انتخاب‌های قبلی و بعدی کاری نداریم. در این شیوه اجازه فکر کردن دربارهٔ یک انتخاب انجام شده را نداریم یعنی هنگامی که سکه‌ای پذیرفته شد به‌طور دائم جزو حل به حساب می‌آید و هنگامی هم که سکه‌ای رد می‌شود به‌طور دائم از حل کنار گذاشته می‌شود.
همان طور که مشاهده کردید این روش بسیار ساده است، ولی اصلی‌ترین نکنه این است که آیا این روش الزاماً به یک حل بهینه می‌رسد؟ در رابطه با مسئله خاص فوق می‌توان اثبات کرد که جواب بهینه است ولی با مثال زیر نشان می‌دهیم که ممکن است این گونه نباشد.

مثال2

فرض کنید قرار است به فردی 16 تومان پس بدهیم. سکه‌های موجود 1،5،10،12،25تومانی است( با این که ما در ایران سکه 12 تومانی نداریم ولی این فرض را بکنید که داریم).
با الگوریتم حریصانه فوق به این نتیجه می‌رسیم که باید به این فرد یک سکه 12 تومانی و 4 سکه 1 تومانی بدهیم یعنی جمعاً 5 سکه. در حالی که حل بهینه مسئله فوق یک سکه 10 تومانی، یک سکه 5 تومانی و یک سکه یک تومانی است یعنی در کل 3 سکه.
از مثال فوق نتیجه می‌گیریم که هر الگوریتم حریصانه الزاماً حل بهینه را نمی‌دهد و برای هر مسئله خاص باید اثبات کنیم که آیا الگوریتم حریصانه برای آن، جواب بهینه می‌دهدیا خیر و این موضوع اغلب سخت‌ترین مرحله کار است .
با توجه به مثال‌های فوق می‌توان مراحل روش حریصانه را به این صورت بیان کرد:
کار با یک مجموعه تهی شروع شده و به ترتیبی خاص عناصری به مجموعه اضافه می‌شوند. هر دور تکرار الگوریتم شامل مراحل زیر است:
روال انتخاب: این روال عنصر بعدی را طبق یک معیار حریصانه انتخاب می‌کند. این انتخاب یک شرط بهینه را در همان برهه بر آورده می‌سازد.
تحقیق عملی بودن: در این مرحله مشخص می‌شود که آیا مجموعه جدید به دست آمده، برای رسیدن به حل عملی است یا خیر.
تحقیق حل: مشخص می‌سازد که آیا مجموعه جدید، نمونه مورد نظر را حل کرده‌است یا خیر
مثال
روش حریصانه در حل مسئله کوله پشتی صفر ویک :مثالی از این مسئله مربوط به دزدی می‌شود که با کوله پشتی وارد یک جواهر فروشی می‌شود اگر وزن قطعات از یک حد بیشینه Wفراتر رود کوله پشتی پاره خواهد شد . هر قطعه دارای ارزش و وزن معینی خواهد بود . مسئله‌ای که دزد با آن مواجه است ، تعیین حداکثر ارزش قطعات است در حالی که وزن کل آن‌ها از حد معین Wفراتر نرود . این مسئله را کوله پشتی صفر ویک می‌گویند . میتوان مسئله را به صورت زیر بیان کرد. فرض کنید nقطعه وجود داشته باشد ، به‌طوری که:
s=[item1,item2,item3,...,itemn]
Wi=item1وزن Pi=itemiارزش W=حداکثر وزنی که کوله پشتی قادر به تحمل آن است. وwi,pi,wهمگی اعداد صحیحی هستند . راه حل جستجوی جامع این است که همهٔ زیر مجموعه‌های این nقطعه را در نظر بگیریم ، زیرمجموعه‌هایی را که وزن کل آن‌ها از wفراتر نرود ، کنار میگذاریم و از میان آن‌هایی که باقی می‌ماند، آن را که بیشترین ارزش را دارد،انتخاب می‌کنیم