מבני נתונים ואלגוריתמים - מחברת קורס/גרפים/אלגוריתם Dijkstra/תרגילים
דוגמה למסלולים זולים לא-ייחודיים
עריכהשאלה
עריכהנתון גרף מכוון , צומת ,וטבלת מחירים חיוביים לקשתות . בשאלה זו אנו מתעניינים במסלול הזול ביותר מ-s לכל צומת .
א'
עריכהאנא הראי מקרה שבו קיים צומת , כך שאין מסלול יחיד זול ביותר מ ל .
ב'
עריכהאנא מצאי אלגוריתם יעיל המקבל את הגרף, צומת המוצא, וטבלת העלויות, ומחזיר מערך בוליאני Unique
. Unique[u]
יכיל את הערך True
אמ"ם יש מסלול יחידי מ ל . אנא הוכיחי את נכונות האלגוריתם ונתחי סיבוכיותו.
ג'
עריכהאנא מצאי אלגוריתם יעיל המקבל את הגרף, צומת המוצא, וטבלת העלויות, ומחזיר מערך Shortest-Cheapest
.
ּShortest-Cheapest[u]
יכיל את מספר הצמתים במסלול הקצר ביותר מבין המסלולים הזולים ביותר מ ל . אנא הוכיח את נכונות האלגוריתם ונתחי סיבוכיותו.
תשובה
עריכהקביעת מסלולים זולים לא-ייחודיים
עריכהשאלה
עריכהתשובה
עריכהמסלולים זולים קצרים ביותר
עריכהשאלה
עריכהמבני נתונים ואלגוריתמים - מחברת קורס/גרפים/אלגוריתם Dijkstra/תרגילים/מסלולים זולים קצרים ביותר/שאלה
תשובה
עריכהמבני נתונים ואלגוריתמים - מחברת קורס/גרפים/אלגוריתם Dijkstra/תרגילים/מסלולים זולים קצרים ביותר/תשובה
מציאת מסלול זול בגרף בעל קשתות בתחום שלם קטן
עריכהשאלה
עריכההוא קבוע שלם חיובי כלשהו (לדוגמה 3), שאינו גדל יחד עם שאר הנתונים בשאלה (כגון מספר הצמתים או הקשתות).
נתונים גרף מכוון ברשימת שכנויות, טבלת עלויות לקשתות , וצומת מוצא כלשהו. ידוע שעלות כל קשת היא מספר שלם חיובי כלשהו הלקוח מהתחום .
בהינתן צומת כלשהו, רוצים לדעת מהו המסלול הזול ביותר מ ל . אנא כתוב אלגוריתם יעיל לצורך כך.
תשובה
עריכהנבנה גרף חדש ברשימת שכנויות.
נגדיר את הגרף החדש מתוך כלהלן. ראשית, אם , אז נקבע . כעת נעבור על כל . אם מחיר קשת הוא 1, נוסיף קשת זו ל .
לחלופין, נניח שמחיר הקשת הוא . במקרה זה נוסיף עוד צמתים חדשים ל ועוד קשתות ל , כך שיש מסלול באורך מ ל של קשתות במחיר 1 ב . נקבל ב גרף בעל מסלולים במחיר 1 בלבד.
דוגמה: בתרשים הבא, הגרף העליון הוא , הגרף המקורי. הגרף התחתון הוא הגרף החדש (הצמתים המקוריים מ מסומנים בהדגשה). |
המשפט הבא טריביאלי:
משפט: לכל , מחיר המסלול הזול מ ל ב הוא מרחק המסלול הקצר מ ל ב . |
קל גם להווכח שסיבוכיות בניית היא .
כעת נריץ את אלגוריתם [[מבני נתונים ואלגוריתמים - מחברת קורס/#|]] על . הסיבוכיות הכוללת (של הבניה ושל ההרצה) במקרה הגרוע היא .
מציאת מספר מסלולים שונים
עריכהשאלה
עריכהנתון גרף מכוון וקשיר מצומת כלשהו. אנא תאר אלגוריתם יעיל המוצא את מספר המסלולים השונים הכולל מ לכל .
שימו לב: הפלט של האלגוריתם הוא פשוט מספר שלם לא-שלילי. |
תשובה
עריכהמבני נתונים ואלגוריתמים - מחברת קורס/גרפים/אלגוריתם Dijkstra/תרגילים/מציאת מספר מסלולים שונים/תשובה
Dijkstra עם המסלולים הזולים ביותר
עריכהשאלה
עריכהבהרצאה ראינו את אלגוריתם Dijkstra בגרסה המחזירה רק את מחירי המסלולים הזולים ביותר. נזכר בממשק ובשימוש בו. נניח שהגרף הוא המתואר בתרשים הבא:
אז קטע הקוד הבא מדגים שימוש אפשרי:
1 Min-Costs = Dijkstra(G, Edge-Costs, 1)
# Prints 0.
2 Print( Min-Costs[1] )
# Prints 6.
3 Print( Min-Costs[3] )
כעת רוצים לשנות את האלגוריתם Dijkstra
כך שיחזיר זוג מערכים: הראשון ממפה כל צומת לעלותו המינימלית מs
(כמו שעושה המערך היחיד המוחזר עד כה), והשני ממפה כל צומת למסלול עלות מינמלי מs
אליו.
אם הגרף הוא הגרף שראינו לעיל, אז קטע הקוד הבא מדגים שימוש אפשרי בגרסה החדשה:
Print-Path(Path)
1 for i in [1, ..., Length(Path)]
2 Print Path[i]
1 (Min-Costs, Min-Paths) = Dijkstra(G, 1)
# Prints 0
2 Print( Min-Costs[1] )
# Prints nothing
3 Print-Path( Min-Paths[1] )
# Prints 6
4 Print( Min-Costs[3] )
# Prints (1, 6), (6, 7), (7, 3)
5 Print-Path( Min-Paths[3] )
# Prints 9
6 Print( Min-Costs[4] )
# Prints (1, 6), (6, 7), (7, 3), (3, 4)
7 Print-Path( Min-Paths[4] )
אנא כתוב את השינויים המתאימים ונתח את הסיבוכיות.
תשובה
עריכהראשית נכתוב פונקציה דומה למה שהתבקשנו לעשות, Dijkstra'
. פונקציה זו תחזיר זוג
מערכים:
- המערך הראשון הוא
Min-Costs
, שממפה כל צומתu
לעלות המסלול הזול ביותר מs
לu
. - המערך הראשון הוא
Parents
, שממפה כל צומתu
לצומת הקודם במסלול הזול ביותר מs
לu
.להלן הפסוודו-קוד המתאים (השינויים מודגשים כך):
Dijkstra'(G, s)
1 pq = Make-Priority-Queue()
2 '''Parents = Make-Array( Length(V(G)) )'''
3 Min-Costs = Make-Array( Length(V(G)) )
4 for u in V(G)
5 Min-Costs[u] = u == s? 0 : ∞
6 '''Parents[u] = Nil'''
7 Insert(pq, u)
8 while Size(pq) > 0
9 u = Delete(pq)
10 for v in A(g, u)
11 if Min-Costs[v] > Min-Costs[u] + Edge-Costs[ (u, v) ]
12 Min-Costs[v] = Min-Costs[u] + Edge-Costs[ (u, v) ]
13 '''Parents[v] = u'''
14 Decrease-Key(pq, v)
15 return '''(Min-Dists, Parents)'''
התוספות פשוטות למדי. מייצרים מערך Parents
, בו כל צומת שומר את "אביו" במסלול מs
אליו. בכל פעם שצומת מעדכן את שכניו, מעדכנים שהוא אביהם של הצמתים המעודכנים.
נכתוב גם פונקציה Parents-To-Paths
ההופכת את מערך האבות Parents
למערך מסלולים.
Parents-To-Path(s, v, Parents)
1 stk = Make-Stack()
2 while Parents[v] != Nil
3 u = Parents[v]
4 Push(stk, (u, v))
5 v = u
6 Path = Make-Array( Size(stk) )
7 i = 1
8 while Size(stk) > 0
9 Path[i++] = Pop(stk)
10 return Path
הפונקציה מקבלת את צומת המוצא s
, צומת כלשהו v
, והמערך Parents
; היא מחזירה מערך המתאר הת המסלול מs
לv
. 1-6 "מטיילות" אחורה על מסלול האבות, ומכניסות קשתות למחסנית. 7-11 ממירות את המחסנית למערך, ומחזירות את המערך.
כעת, בעזרת Dijkstra'
וParents-To-Paths
, נוכל לכתוב את הפונקציה המבוקשת:
Dijkstra(G, s)
1 (Min-Costs, Parents) = Dijkstra'(G, s)
2 Min-Paths = Make-Array( Length(V(G)) )
3 for v in V(G)
4 Min-Paths[u] = Parents-To-Paths(s, v, Parents)
5 return (Min-Costs, Min-Paths)
כעת ננתח את הסיבוכיות:
- מהתבוננות ב
Dijkstra'
, קל לראות שהסיבוכיות שלה הנה זו של אלגוריתם Dijkstra. Parents-To-Paths
היא .הסיבוכיות הכוללת, לכן, היא .
Dijkstra על מסלולים אדומים-שחורים
עריכהשאלה
עריכהנתון גרף מכוון וקשיר מצומת כלשהו. לכל קשת יש שתי תכונות: מחיר הקשת (שכבר ראינו), וצבע הקשת, שהוא שחור או אדום. רוצים למצוא את המסלול הזול ביותר מs
לכל צומת u
כלשהו, אך תחת המגבלה שצבעי קשתות המסלול הם אדום, שחור, אדום, שחור, וכו'. במילים אחרות, חייבים למצוא את המסלול הזול ביותר מבין המסלולים שצבע הקשתות האי-זוגיות בהן הוא אדום, וצבע הקשתות הזוגיות בהן הוא שחור.
אנא תאר אלגוריתם כזה, ונתח את סיבוכיותו.
דוגמה: בגרף הבא, הקשתות המודגשות שחורות, וצומת המוצא הוא 1.
|
תשובה
עריכהנבנה גרף חדש כך.
ראשית, נקח את צמתי הגרף , ונשכפל אותם 3 פעמים, בקבוצות , , ו . לכל צומת בקבוצה , יש צומת ב , וצומת ב . התרשים הבא מראה זאת.
כעת נוסיף גם קשתות:
- כל קשת אדומה בין ל (בגרף המקורי) - נמתח אותה בגרף החדש בין ב לבין הצומת המתאים ל ב .
- כל קשת שחורה בין ל (בגרף המקורי) - נמתח אותה בגרף החדש בין הצומת המתאים ל ב לבין הצומת המתאים ל ב .
התרשים הבא מראה זאת.
בנוסף נמתח עוד שני סוגי קשתות:
- לכל צומת ב , נמתח קשת במחיר 1 בין ב לבין ב .
- לכל צומת ב , נמתח קשת במחיר 1 בין ב לבין ב .(לא נצייר קשתות אלו מטעמי נוחות.)
נשים לב שאם כל הגרפים הנ"ל מיוצגים ברשימת שכנויות, אז סיבוכיות הבניה היא .
המשפט הבא מסביר את חשיבות הגרף החדש.
משפט: מחיר המסלול הקביל הזול ביותר מ ל בגרף המקורי, קטן בדיוק ב1 ממחיר המסלול הזול ביותר מ ל ב בגרף החדש. |
הוכחה: מבניית הגרף החדש, ברור שכל מסלול מ ל ב בגרף החדש, מורכב משני חלקים:
- מסלול זהה לחלוטין למסלול קביל מ ל בגרף המקורי
- מסלול בעלות 1 ל ב
לכן, כל שנותר הוא להריץ את אלגוריתם Dijkstra.
קל לראות שהסיבוכיות הכוללת היא זו של אלגוריתם Dijkstra.
Dijkstra עם העדפה למסלולים שארכם זוגי
עריכהשאלה
עריכהנתון גרף מכוון וקשיר מצומת כלשהו. לכל קשת יש מחיר. רוצים למצוא את המסלול הזול ביותר מs
לכל צומת u
כלשהו, אך תחת העדפה למסלולים שארכם זוגי. מציין גודל קנס כלשהו. אם מסלול כלשהו מ ל הוא אי-זוגי, יש להוסיף למחיר מסלול זה קנס בגובה .
אנא תאר אלגוריתם המקבל כקלט את הגרף, טבלת עלויות לקשתות, וגודל הקנס, ומוצא את מחיר המסלול הזול ביותר (במובן שאותו הסברנו כרגע) לכל צומת . אנא הוכח נכונות ונתח סיבוכיות.
תשובה
עריכהנניח שזהו הגרף המקורי:
נבנה גרף חדש כך.
ראשית, נקח את צמתי הגרף , ונשכפל אותם 3 פעמים, בקבוצות , , ו . לכל צומת בקבוצה , יש צומת ב , וצומת ב . התרשים הבא מראה זאת.
כעת נוסיף גם קשתות:
- כל קשת בין ל (בגרף המקורי) - נמתח אותה בגרף החדש בין ב לבין הצומת המתאים ל ב .
- כל קשת בין ל (בגרף המקורי) - נמתח אותה בגרף החדש בין הצומת המתאים ל ב לבין הצומת המתאים ל ב .
התרשים הבא מראה זאת (רק חלק מהקשתות מצויירות).
בנוסף נמתח עוד שני סוגי קשתות:
- לכל צומת ב , נמתח קשת במחיר בין ב לבין ב .
- לכל צומת ב , נמתח קשת במחיר בין ב לבין ב .(לא נצייר קשתות אלו מטעמי נוחות.)
נשים לב שאם כל הגרפים הנ"ל מיוצגים ברשימת שכנויות, אז סיבוכיות הבניה היא .
המשפט הבא מסביר את חשיבות הגרף החדש.
משפט: מחיר המסלול הזול ביותר מ ל בגרף המקורי (כולל הקנס, אם יש), קטן בדיוק ב1 ממחיר המסלול הזול ביותר מ ל ב בגרף החדש. |
הוכחה: מבניית הגרף החדש, ברור שכל מסלול מ ל ב בגרף החדש, מורכב משני חלקים:
- מסלול זהה לחלוטין למסלול מ ל בגרף המקורי
- מסלול בעלות 1 ל ב , אם החלק הראשון זוגי, ומסלול בעלות , אם החלק הראשון אי-זוגי.
לכן, כל שנותר הוא להריץ את אלגוריתם Dijkstra.
קל לראות שהסיבוכיות הכוללת היא זו של אלגוריתם Dijkstra.
מסלולים זולים עם קשת יחידה שלילית
עריכהשאלה
עריכהנתונה רשת כבישי אגרה. לכל כביש יש מספר המתאר את עלות הנסיעה בכביש.
דוגמה: בתרשים הבא, עלות הנסיעה בכביש מ1 ל2 היא . |
חברת "קדמה לתורה" פתחה כביש אגרה חדש. כדי למשוך לקוחות פוטנציאליים, למשך החודש הקרוב, תשלם החברה לכל מי שיסע בכביש. נניח לכן שעלות הנסיעה בכביש זה שלילית.
דוגמה: בתרשים הקודם, עלות הנסיעה בכביש מ7 ל3 היא . |
שימו לב: בתרגום לגרפים, מדובר בגרף מכוון וטבלת עלויות לקשתות, כך שיש בדיוק עלות שלילית אחת. הנח שאין מעגלים שליליים בגרף. |
אנא מצא אלגוריתם יעיל המקבל את הגרף, טבלת עלויות לקשתות, וצומת מוצא , ומחזיר, עבור כל צומת , את עלות המסלול הזול ביותר מ ל .
דוגמה: בתרשים הקודם, נניח שצומת המוצא הוא :
|
תשובה
עריכהשימו לב: אלגוריתם Dijkstra אינו פועל בצורה נכונה על גרפים בעלי קשתות שליליות (אפילו אם אין מעגל שלילי). לא למדנו אחרת, וקל לבנות דוגמה המראה זאת. |
נניח שזהו הגרף המקורי:
נבנה גרף חדש כך.
ראשית, נקח את צמתי הגרף , ונשכפל אותם 3 פעמים, בקבוצות , , ו . לכל צומת בקבוצה , יש צומת ב , וצומת ב . התרשים הבא מראה זאת.
כעת נוסיף גם קשתות:
- במקום כל קשת בגרף המקורי, למעט הקשת השלילית, נמתח קשת מהצומת המתאים ל ב לצומת המתאים ל ב .
- במקום כל קשת בגרף המקורי, למעט הקשת השלילית, נמתח קשת מהצומת המתאים ל ב לצומת המתאים ל ב .
- במקום הקשת השלילית (בגרף המקורי), נמתח קשת מהצומת המתאים ל ב לצומת המתאים ל ב במחיר 0. התרשים הבא מראה זאת.
נניח שלקשת השלילית היה מחיר . בנוסף נמתח עוד שני סוגי קשתות:
- לכל צומת ב , נמתח קשת במחיר בין ב לבין ב .
- לכל צומת ב , נמתח קשת במחיר 0 בין ב לבין ב .(לא נצייר קשתות אלו מטעמי נוחות.)
נשים לב שאם כל הגרפים הנ"ל מיוצגים ברשימת שכנויות, אז סיבוכיות הבניה היא .
המשפט הבא מסביר את חשיבות הגרף החדש.
משפט: מחיר המסלול הזול ביותר מ ל בגרף המקורי, קטן בדיוק ב ממחיר המסלול הזול ביותר מ ל ב בגרף החדש. |
הוכחה: בגרף המקורי, ייתכן שהמסלול הזול ביותר מ ל לא השתמש בקשת השלילילת, וייתכן שהשתשמש בו. במקרה הראשון, תהיה קפיצה מ ישירות ל , ובמקרה השני, תהיה קפיצה מ ל ומ ל . הקפיצה מ ישירות ל עולה יותר מאשר המסלול המקורי. הקפיצות מ ל ומ ל גם כן עולות יותר מאשר המסלול המקורי, שכן עלות כל אחת משתי הקשתות היא 0, והנוסע לא קיבל את התשלום מהחברה.
כעת כל הקשתות אינן שליליות, ומותר להריץ את Dijkstra.
קל לראות שהסיבוכיות הכוללת היא זו של Dijkstra.