Remove ads
מוויקיפדיה, האנציקלופדיה החופשית
בעיית תרמיל הגב (באנגלית: Knapsack problem) היא בעיית מיטוב קומבינטורית הנחקרת בתחום מדעי המחשב. בבעיה זו נתונה קבוצת עצמים שלכל אחד מהם משקל ומחיר, ובנוסף נתון חסם על המשקל. המטרה היא למצוא אוסף של העצמים הנתונים שסכום משקליהם אינו עולה על החסם הנתון, ומחירו מרבי.
הבעיה נחשבת לבעיה חשובה במדעי המחשב בגלל הערך התאורטי שלה (הבעיה היא NP-שלמה[1]), ועקב השימושים שלה בתחומים כגון הקצאת משאבים וקריפטוגרפיה[2].
נתונים עצמים ולכל עצם נתונים מחירו ומשקלו . לכל עצם נסמן ב- את מספר העצמים מהחפץ שנכניס לתרמיל. המטרה היא למקסם את הביטוי (המחיר הכולל של העצמים בתרמיל) בכפוף לאילוץ (משקלם הכולל של החפצים אינו עולה על החסם).
הגרסה הנפוצה והפשוטה ביותר היא בעיית תרמיל הגב הבינארי: בבעיה זו לכל חפץ יש עותק אחד בלבד ולכן (כלומר, אם חפץ מסוים נכנס לתרמיל אזי יש רק אחד מסוגו בתרמיל).
בבעיית תרמיל הגב החסומה, לכל עצם יש כמות ומותר להכניס לתרמיל מספר עותקים של העצם: .
בבעיית תרמיל הגב הלא חסומה, מותר לקחת מספר עותקים בלתי מוגבל מכל עצם: .
גרסת ההכרעה של בעיית תרמיל הגב היא: בהינתן עצמים עם מחירים ומשקלים , ובהינתן מחיר כולל וחסם על המשקל W יש להכריע האם קיימת בחירה של העצמים כך ש- תחת האילוץ . בעיה זו היא NP-שלמה:
כיוון שהבעיה היא NP-שלמה, לא ידועים אלגוריתמים פולינומיים אשר מוצאים פתרון מיטבי. עם זאת, בגרסת הבעיה בה מובטח שכל משקלי הפריטים הם מספרים שלמים, קיים אלגוריתם המבוסס על תכנון דינמי אשר פותר באופן מיטבי את הבעיה בסיבוכיות זמן פסאודו-פולינומית (אם משקלי הפריטים לא שלמים מספר תתי הבעיות עלול להיות מעריכי ביחס לקלט).
האלגוריתם הבא פותר את בעיית תרמיל הגב הבינארית בסיבוכיות זמן .
בהינתן מופע של הבעיה כמתואר בחלק "הגדרת הבעיה", נגדיר להיות מחיר הפתרון המיטבי שניתן להשיג באמצעות עצמים שמספר הסידורי שלהם אינו עולה על ושסכום משקלם אינו עולה על . הגדרה זו מאפשרת פיתוח נוסחת נסיגה ל-:
כעת ניתן, באמצעות חישוב ערכי הטבלה , לחשב ברקורסיה את , הלוא הוא המחיר המיטבי לבעיה.
הקוד הבא בשפת Python מממש את האלגוריתם המתואר[6]:
def knapsack01_dp(items, limit):
table = [[0 for w in range(limit + 1)] for j in range(len(items) + 1)]
for j in range(1, len(items) + 1):
item, wt, val = items[j-1]
for w in range(1, limit + 1):
if wt > w:
table[j][w] = table[j-1][w]
else:
table[j][w] = max(table[j-1][w],
table[j-1][w-wt] + val)
result = []
w = limit
for j in range(len(items), 0, -1):
was_added = table[j][w] != table[j-1][w]
if was_added:
item, wt, val = items[j-1]
result.append(items[j-1])
w -= wt
return result
גישה אחרת לפתרון הבעיה היא שימוש באלגוריתמי קירוב. לבעיה זו קיימים מספר אלגוריתמי קירוב כולל סכמת קירוב פולינומית מלאה (FPTAS)[7].
האלגוריתם החמדן הבא מוצא תרמיל שמחירו לכל הפחות מחצית המחיר של הפתרון המיטבי (2-קירוב)[8]:
נמיין את העצמים (שמשקלם אינו גדול מהחסם; עצם שמשקלו גדול מהחסם אינו רלוונטי לבעיה) לפי יחס המחיר-משקל שלהם . נוסיף לתרמיל, כל עוד זה אפשרי, את העצם הבא ברשימה הממוינת לפי יחס מחיר-משקל (מתחילים עם העצם בעל היחס הגבוה ביותר). נסמן ב- את מספר העצמים שהצלחנו להכניס בצורה זו, ולכן ניתן לתאר את המחיר של העצמים שהכנסנו על ידי: . כעת נתבונן על המחיר של העצם ה-: . אם , נשאיר את העצמים בתיק, ואם , נוציא את העצמים מהתיק, ונכניס במקומם את העצם ה-.
הוכחה שאלגוריתם זה נותן פתרון 2-קירוב: נסמן ב- את הערך של הפתרון האופטימלי. נתבונן בבעיה דומה בה מותר לנו להכניס חלקי עצמים (נניח חצי עצם), ולא רק עצמים שלמים, ונסמן ב- את הערך של הפתרון האופטימלי עבור בעיה זו. הפתרון האופטימלי עבור בעיה בה מותר להכניס חלקי עצמים, הוא גדול-שווה לפתרון האופטימלי של הבעיה המקורית בה ניתן להכניס רק עצמים שלמים (שכן כל פתרון עבור הבעיה המקורית, הוא גם פתרון אפשרי של הבעיה עם חלקי עצמים).
אם יכולנו להכניס לתרמיל חלקי עצמים, אזי ברור שהפתרון האופטימלי היה להכניס את j העצמים הראשונים, ולהכניס חלק מהעצם ה-j+1 שבדיוק ממלא את המשקל שניתן עוד להכניס לתרמיל (המשקל שניתן עוד להכניס לתרמיל לאחר הכנסת j העצמים הראשונים הוא , ולכן החלק של העצם ה-j+1 שיוכנס לתרמיל הוא ). לכן:
ובמילים: המחיר של העצמים 1 עד j+1 גדול מהמחיר של עצמים 1 עד j + המחיר של חלק מעצם j+1, ששווה ל-M (הפתרון של הבעיה עם חלקי עצמים), שגדול-שווה ל-m (הפתרון של הבעיה שלנו).
לכן, אם נחלק את הסכום ל- ול-, בהכרח לפחות אחד משני הביטויים הללו חייב להיות בעל מחיר של לפחות , כיוון שסכום המשקלים של שניהם חייב להיות גדול-שווה ל-.
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.