כאשר דיברנו על אלגוריתמים מהסגנון של תכנות דינמי, ראינו שאם ניתן להביע את פתרון הבעיה כפונקציה ריקורסיבית (עם כמה קריאות ריקורסיביות) כאשר ישנה חזרה על קריאות שהופיעו קודם (במצב של אלגוריתם פיבונאצ׳י אין חזרות כאלה) אז ניתן להקטין את זמן הריצה על ידי שימוש בזכרון נוסף.
אבל מה אם היינו יכולים להבטיח שמספיק להסתכל על קריאה ריקורסיבית אחת? זה יאפשר לעץ הריקורסיה להראות יותר כמו ״מסלול״ מובטח לפתרון הנכון ואולי אפילו לייעל את זמן הריצה של התוכנית.
אינטואיבית זה המשמעות של אלגוריתמים חמדנים- אלגוריתם שמכיל קריאה ריקורסיבית אחת בלבד לתת מופע שעליו נפתור את אותה הבעיה.
השימוש במילה חמדן בא לידי ביטוי בגלל הבחירה החמדנית שהאלגוריתם מבצע: האלגוריתם מבצע החלטה מקומית (החלטה שמבוצעת כחלק מהאלגוריתם עצמו) אחרי אבחון מסויים לגבי הפלט, והוא דבק בהחלטה הזאת לכל אורך האלגוריתם שבעקבותה הוא יודע לאיזה תת מופע ללכת כדי להמשיך את הריקורסיה.
דוגמה חיפוש בינארי :
הקלט הוא מערך ממויין של 𝑛 מספרים ומספר 𝑥 , ורוצים להחליט אם 𝑥 נמצא במערך . האלגוריתם הקלאסי פותר את הבעיה בזמן
נרצה להוכיח את ה greedy choice של האלגוריתם על ידי כך שנראה שההחלטה החמדנית המקומית שהאגוריתם החמדני עושה , לא מונע האלגוריתם מלהגיע לפתרון נכון (או אופטימלי). למשל בחיפוש הבינארי נוכל להגיד שהבחירה החמדנית היא ״אם האוסף שלנו ממויין והאיבר שאנחנו מחפשים גדול מהחציון, אז בוודאות הוא נמצא בתת המופע הימני של האוסף או שהוא לא נמצא בכלל, אחרת, בשמאלי או שהוא לא נמצא בכלל .״
צריכים להוכיח שהקומבינציה של הבחירה החמדנית והפתרון של הקריאה הרקורסיבית על תת-המופע שהוגדר ע"י הבחירה החמדנית נותנת הפתרון הנכון (אופטימלי). תת המבנה האופטימלי זאת תכונה ידועה במדעי המחשב שמגדירה מבנה כאופטימלי לבעיה אם ניתן להגיע לפתרון אופטימלי של אותה הבעיה על ידי חיבור של הפתרונות האופטימלים של תתי המופעים של הבעיה.
א. Prefix Code - Huffman Code
ב. Minimum Spanning Tree
פוליטקיאי מתמודד בבחירות ורוצה להספיק להגיע להופיע בכמה שיותר אירועים. ישנה חפיפה בין אירועים, והמתמודד לא יכול להגיע לאירוע באופן חלקי, כיוון שבכך הוא יביך את מארחיו ויפסיד מצביעים. מנגד, למתמודד יש כח על של התעתקות ממקום למקום, כך שהמעבר בין מקומות אינו לוקח לו זמן.
באופן פורמלי נגדיר את הבעיה ככה
בהינתן שתי פעילויות שמיוצגות על ידי זמן התחלה וסיום
זה מתאר מצב של חוסר חפיפה דוגמה לכך על ציר הזמן תיראה ככה

קלט: מערך של פעילויות
פלט:תת הקבוצה גדולה ביותר
פתרון
נרצה לבנות אלגוריתם חמדני - ישנה איזושהי פעילות
נשים לב שבהינתן שניקח
מההבחנה הזו, נשים לב לכך שבבואנו לבחור את הפעילות הראשונה, הפרמטר שהכי מעניין אותנו אינו מתי הפעילות מתחילה אלא דווקא מתי היא מסתיימת.
נרצה את הפעילות שמסתיימת מוקדם ביותר, כי זאת תשאיר לנו כמה שיותר אפשרויות לבניית הקבוצה הכי גדולה. לכן הבחירה החמדנית שלנו תהיה שקיים פתרון אופטימלי לבעיית בחירת הפעילויות בו בוחרים פעילות שמסתיימת מוקדם ביותר.
בכל שלב ניקח את הפעילות שמסתיימת מוקדם ככל הניתן מבין הפעילויות שעוד אפשר לקחת, ונמחק מרשימת האפשרויות את כל אלה שחופפות לה.
Activity-Selector(S)
sort(S, by: f_i)
A = {A_1}
j=1
for i=2 to n
if s_i >= f_j
A = A union {A_i}
j = i
return A
נוכל לתאר אותו גם בצורה ריקורסיבית
תכונת הבחירה החמדנית
קיים פתרון אופטימלי לבעיית בחירת הפעילויות בו בוחרים את הפעילות שמסתיימת מוקדם ביותר
ניקח פתרון אופטימלי כלשהו נסמנו OPT
אם הפעילות שמסתיימת מוקדם נמצאת שם סיימנו.
אחרת נסתכל על הפעילות שמסתיימת ראשונה נסמן אותה כ
אנחנו יודעים שהפעילות
נוכיח כי הוא פתרון חוקי :
עבור שתי פעילויות
כי
נוכיח כי הוא פתרון אופטימלי:
ו OPT הוא פתרון אופטימלי.
בעזרת תכונת הבחירה החמדנית, נוכיח את נכונות האלגוריתם, בהוכחת תכונת תת־המבנה האופטימלי ־ זוהי הוכחת הנכונות של האלגוריתם (מקבילה לצעד האינדוקציה בהוכחת נכונות המתבססת על אינדוקציה).
תכונת תת המבנה האופטימלי
פתרון המורכב מבחירה של הפעילות שמסתיימת מוקדם ביותר, בתוספת פתרון אופטימלי לבעיית בחירת הפעילויות עם כל הפעילויות שמתחילות אחרי סיום הפעילות הנ"ל הוא פתרון אופטימלי לבעיה.
יהי פתרון
מהנתון מתקיים
אנחנו יודעים שמתקיים מאיך ש
כלומר נקבל שגם
בסתירה לכך ש
ב בעית תרמיל הגב אנו חושבים על גנב שצריך לבחור אילו פריטים לגנוב תוך אילוץ של משקל מקסימלי. בגרסת השברים, אנו מניחים שהגנב פרץ לבית מרקחת וניתן לקחת גם חלקים של מוצר, והשווי וגם המחיר של המוצר הנגנב פרופורציונאליים לחלק אותו בוחרים לגנוב.
קלט: מערך של מחירים
פלט: החפצים או החלק שאנחנו לוקחים מכל חפץ (1- לקחת את כל החפץ , 0- לא לקחת בכלל , כל מספר בין לבין- לקחת את החלק היחסי). הפלט ייראה כך
נציע אלגוריתם חמדני הפותר את הבעיה הזו.
בעוד שבבעיית תרמיל הגב הרגילה לעבוד עם יחס עלות ליחידת משקל מסויימת לא היה עובד, כאן אכן נוכל לעבוד איתו.
נסמן
כעת נבחר באופן חמדני את הערכים שם היחס הגדול ביותר כל עוד יש מקום, עד לזה שערכו הקטן ביותר.
Knapsack(V,W,B)
define A[n]
for i=1 to n
A[i] = a_i = v_i / w_i
sort V and W from biggest to smallest using A
define X[n]
for i=1 to n
x[i] = min(B,w_i)/w_i
B = B - x_i * w_i
return X
באופן ריקורסיבי נוכל להסתכל על האלגוריתם כך:
תכונת הבחירה החמדנית
קיים פתרון אופטימלי לבעיית תרמיל הגב בשברים בו לוקחים את החלק הגדול ביותר שניתן מהחפץ שמתאים ל־a1) החפץ עם השווי הגדול ביותר ליחידת משקל).
יהי פתרון אופטימלי כלשהו
אם קיים
אחרת ישנו בלי הגבלת הכלליות נניח
Balance(B)
assert B < 0
for i=n to 1
B = B + x_i * w_i
if B < 0
continue
if B = 0
return
x_i = min(B, w_i)/w_i
return
נשים לב שברור שהסכום החדש יניב יותר כסף , קל להוכיח את זה באופן הבא, נפרק את המשקל שלקחנו מ
אם לא לקחנו מ
נשים לב שבוודאות ניתן יהיה לאזן על ידי הורדת משקלים בלי שנגיד למצב ש״אין מספיק להוריד״ כי בסוף בוודאות סכום כל מי שאינו
תכונת תת המבנה האופטימלי
פתרון המורכב מבחירה של החלק הגדול ביותר שניתן מהחפץ הראשון, בתוספת בחירה אופטימלית כלשהי לתת הבעיה המוגדרת ע"פ שאר החפצים בבית, עם תרמיל שמשקלו קטן מ־B עם המשקל הגדול ביותר שניתן לקחת מהחפץ הראשון ־ הוא פתרון אופטימלי לבעיה.
נב״ש שהפתרון המתואר בלמה
אם נוריד את
ומכאן
שזה בסתירה לאי שיוויון הראשון.