מבני נתונים ואלגוריתמים - מחברת קורס/גרפים/אלגוריתם Floyd-Warshall
דף זה הוא השלישי מבין שלושה דפים העוסקים באלגוריתמים למציאת המסלול הזול ביותר בגרף מכוון בין כל שני צמתים.
כדאי לדעת: כל האלגוריתמים (שאותם נלמד) למציאת מסלולים זולים לכלל הזוגות מתבססים על אותו הרעיון.
|
מימוש C++ |
הרעיון הכללי
עריכהבאלגוריתם "הכפלת מטריצות" מצאנו אפיון רקורסיבי למסלול הזול ביותר כפונקציה של ארכו. כאן, לעומת זאת, נמצא אפיון רקורסיבי למסלולים הזולים ביותר כפונקציה של צמתי הביניים שלהם.
הגדרה: נניח מסלול מ ל :
נאמר שהקבוצה היא קבוצת צמתי הביניים של המסלול . |
שימו לב: להלן נקודה שמבלבלת חלק מהסטודנטים מדי שנה.אנו עוסקים בגרפים בהם הצמתים הם מספרים שלמים. לכן, כל אחד מ הוא פשוט מספר שלם מהקבוצה . |
דוגמה: להלן הגרף המקורי לבעיה: (נזכר שהגרף ה"מוסף" נוצר על ידי הוספת קשתות במחירים 0 ואינסוף.)
|
הגדרה: נגדיר כ את מחיר המסלול הזול ביותר מ ל מבין אלה שכל צומת ביניים שלהם שייך ל . במילים אחרות, הוא מחיר המסלול הזול ביותר מבין אלה מהצורה |
שימו לב: גם באלגוריתם "הכפלת מטריצות" הגדרנו . אין קשר בין ההגדרה כאן להגדרה שם. |
דוגמה: בגרף שראינו, שני מסלולים (מתוך מספר מסלולים) מ ל :
קל לראות, לכן: |
לפי הגדרה זו, קל מאד לאפיין את המסלולים הזולים ביותר בגרף. המשפט הבא מראה זאת.
משפט: נקבע את כמספר הצמתים בגרף. אז מחיר המסלול הזול ביותר מ ל הוא בדיוק . |
כל שנותר כדי לפתור את הבעיה, הוא להבחין שאפשר לחשב את המסלולים הזולים (עפ"י צמתי-הביניים בהם הם משתמשים) בצורה רקורסיבית. המשפט הבא מראה זאת.
משפט: הוא:
|
הוכחה: נזכר ש משמעו המסלול הזול ביותר מ ל שכל אחד מצמתי הביניים שלו לקוח מהקבוצה
אם , אז אינו כולל צמתי ביניים. מדובר לכן במחיר הזול ביותר הישיר מ ל .
אם , אז יש שתי אפשרויות:
- אינו מופיע בקבוצת צמתי הביניים (כלומר, המסלול הזול ביותר אינו עובר דרך ).
- מופיע בקבוצת צמתי הביניים (כלומר, המסלול הזול ביותר אכן עובר דרך ).
במקרה הראשון, היות שלא משתמשים ב , אז מדובר במסלול הזול ביותר מ ל שכל אחד מצמתי הביניים שלו לקוח מהקבוצה , כלומר התשובה היא פשוט .
במקרה השני, היות שמשתמשים ב , המסלול מתחלק לשני חלקים:
- מ ל . בחלק זה אפשר להניח ש אינו צומת ביניים (מדוע?). בקטע זה, לכן, התשובה היא פשוט .
- מ ל . גם בחלק זה אפשר להניח ש אינו צומת ביניים (מדוע?). גם בקטע זה, לכן, התשובה היא פשוט .
פסוודו-קוד
עריכהלהלן הפסוודו-קוד לאלגוריתם:
Floyd-Warshall(G, Edge-Costs, m)
1 if m == 0
2 return Edge-Costs
3 D' = Floyd-Warshall(G, Edge-Costs, m - 1)
4 n = Length( V(G) )
5 D = Make-Matrix(n, n)
6 for u in V(G)
7 for v in V(G)
8 D[u][v] = D'[u][v]
9 if D[u][v] > D'[u][m] + D'[m][v]
10 D[u][v] = D'[u][m] + D'[m][v]
11 return D
ולהלן דוגמה לשימוש בו:
1 n = Length( V(G) )
2 D = Floyd-Warshall(G, Edge-Costs, n)
# Prints 2.
3 Print( D[1][2] )
# Prints ∞.
4 Print( D[2][1] )
ניתוח סיבוכיות
עריכהראשית, קל לראות כי 6-10 הן , והן למעשה קובעות את זמן הריצה של קריאה יחידה של הפונקציה.
נגדיר כ את מספר צמתי הגרף, וכ את זמן הריצה של Floyd-Warshall(G, Edge-Costs, m)
.
אז
, ולכן
.
הפרק הקודם: אלגוריתם "הכפלת מטריצות" |
אלגוריתם Floyd-Warshall | הפרק הבא: זרימה - הגדרות |