מבני נתונים ואלגוריתמים - מחברת קורס/גרפים/אלגוריתם Floyd-Warshall

מתוך ויקיספר, אוסף הספרים והמדריכים החופשי

דף זה הוא השלישי מבין שלושה דפים העוסקים באלגוריתמים למציאת המסלול הזול ביותר בגרף מכוון בין כל שני צמתים.


כדאי לדעת:

כל האלגוריתמים (שאותם נלמד) למציאת מסלולים זולים לכלל הזוגות מתבססים על אותו הרעיון.
  1. ראשית, מניחים שהקלט של הבעיה מתאר את הבעיה בצורה נוחה (לעיתים יש צורך בהוספת עוד קשתות לגרף. מסלולים זולים לכלל הזוגות עוסק בכך).
  2. כעת עובדים בשיטת הפרד ומשול - מוצאים את פתרונן של בעיות "גדולות" יותר בעזרת פתרונן של בעיות "קטנות" יותר. השאלה היא כיצד מגדירים מהן בעיות "קטנות" ו"גדולות":
    • באלגוריתם "הכפלת מטריצות" נראה שיטה לפיה פותרים בעיות קטנות יותר, בהן המסלול הזול ביותר מוגבל באורך.
    • באלגוריתם Floyd-Warshall נראה שיטה לפיה פותרים בעיות קטנות יותר, בהן המסלול הזול ביותר מוגבל בקבוצות צמתי הביניים של המסלול.



מימוש C++

boost::floyd_warshall_all_pairs_shortest_paths



הרעיון הכללי[עריכה]

באלגוריתם "הכפלת מטריצות" מצאנו אפיון רקורסיבי למסלול הזול ביותר כפונקציה של ארכו. כאן, לעומת זאת, נמצא אפיון רקורסיבי למסלולים הזולים ביותר כפונקציה של צמתי הביניים שלהם.


הגדרה:

נניח מסלול מ ל:‏

נאמר שהקבוצה היא קבוצת צמתי הביניים של המסלול .


שימו לב:

להלן נקודה שמבלבלת חלק מהסטודנטים מדי שנה.

אנו עוסקים בגרפים בהם הצמתים הם מספרים שלמים. לכן, כל אחד מ הוא פשוט מספר שלם מהקבוצה .



דוגמה:

להלן הגרף המקורי לבעיה:

בעיית המסלולים הזוגים לכלל הזוגות.
בעיית המסלולים הזוגים לכלל הזוגות.

(נזכר שהגרף ה"מוסף" נוצר על ידי הוספת קשתות במחירים 0 ואינסוף.)

  1. במסלול , קבוצת צמתי הביניים היא .
  2. במסלול , קבוצת צמתי הביניים היא .
  3. במסלול , קבוצת צמתי הביניים היא (הקבוצה הריקה).
  4. במסלול , קבוצת צמתי הביניים היא .


הגדרה:

נגדיר כ את מחיר המסלול הזול ביותר מ ל מבין אלה שכל צומת ביניים שלהם שייך ל. במילים אחרות, הוא מחיר המסלול הזול ביותר מבין אלה מהצורה
כשכל שייך ל.


שימו לב:

גם באלגוריתם "הכפלת מטריצות" הגדרנו . אין קשר בין ההגדרה כאן להגדרה שם.




דוגמה:

בגרף שראינו, שני מסלולים (מתוך מספר מסלולים) מ ל:

  1. המסלול שעלותו , ושאינו עובר דרך אף צומת ביניים.
  2. המסלול שעלותו , ושעובר דרך צומת הביניים .

קל לראות, לכן:


לפי הגדרה זו, קל מאד לאפיין את המסלולים הזולים ביותר בגרף. המשפט הבא מראה זאת.



משפט:

נקבע את כמספר הצמתים בגרף. אז מחיר המסלול הזול ביותר מ ל הוא בדיוק .


כל שנותר כדי לפתור את הבעיה, הוא להבחין שאפשר לחשב את המסלולים הזולים (עפ"י צמתי-הביניים בהם הם משתמשים) בצורה רקורסיבית. המשפט הבא מראה זאת.



משפט:

הוא:

  1. Edge-Costs[u][v], אם .
  2. , אם .


הוכחה: נזכר ש משמעו המסלול הזול ביותר מ ל שכל אחד מצמתי הביניים שלו לקוח מהקבוצה

אם , אז אינו כולל צמתי ביניים. מדובר לכן במחיר הזול ביותר הישיר מ ל.

אם , אז יש שתי אפשרויות:

  1. אינו מופיע בקבוצת צמתי הביניים (כלומר, המסלול הזול ביותר אינו עובר דרך ).
  2. מופיע בקבוצת צמתי הביניים (כלומר, המסלול הזול ביותר אכן עובר דרך ).

במקרה הראשון, היות שלא משתמשים ב, אז מדובר במסלול הזול ביותר מ ל שכל אחד מצמתי הביניים שלו לקוח מהקבוצה , כלומר התשובה היא פשוט .

במקרה השני, היות שמשתמשים ב, המסלול מתחלק לשני חלקים:

  • מ ל. בחלק זה אפשר להניח ש אינו צומת ביניים (מדוע?). בקטע זה, לכן, התשובה היא פשוט .
  • מ ל. גם בחלק זה אפשר להניח ש אינו צומת ביניים (מדוע?). גם בקטע זה, לכן, התשובה היא פשוט .


פסוודו-קוד[עריכה]

להלן הפסוודו-קוד לאלגוריתם:

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 הפרק הבא:
זרימה - הגדרות