APSP

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

קלט: גרף G=(V,E) ופונקציית משקל w:ER . לשם הנוחות בהגדרות נסמן את קבוצות הקודקודים על ידי

V={1,2,3,n}

פשוט Vi=i ...

פלט: נרצה להחזיר שתי מטריצות -

א) מטריצת מרחקים D|V|×|V| כך ש di,j=δ(i,j)
ב) מטריצת קודמים Π|V|×|V| כך ש i=j or δi,j=:πi,j=NULL ואחרת πi,j זה הקודקוד הקודם ל j במסלול הקצר ביותר בין j ל i.

קו מנחה:

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

פתרון נאיבי באמצעות sssp

ניתן להריץ את האלגוריתם של בלמן-פורד מכל קודקוד כלומר בזמן

O(|V| bellman ford runtime)=O(|V|2|E|)

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

א) מערך: O(|V|3)
ב) ערימה בינארית: O(V2logV+VElogV)
ג) ערימת פיבונאצי : O(VE+V2logV)

חסם על מספר הקשתות

לעתים נרצה לבטא את זמן הריצה ככזה שתלוי רק במשתנה אחד למשל מספר הקודקודים, נזכר שישנו חסם על מספר הקשתות בגרף.
א) אם הגרף דליל sparse- |E|=Θ(|V|)
ב) אם הגרף צפוף dense- |E|=Θ(|V|2)

Screenshot 2023-02-04 at 15.31.30.png

הבחנה

אם הגרף לא ממושקל נוכל להריץ BFS על כל קודקודי הגרף ולפתור את בעיית APSP בזמן ש לO(V2+VE)

אלגוריתם פלויד וורשאל

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

קודקודי ביניים: יהי p=(v1,v2vk) מסלול. נגדיר את {v2,,vk1} כקודקודי הביניים של p .

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

{1,2,,k}

כעת נתבונן בכל המסלולים משתי קודקודים i,j כך שכל קודקודי הביניים שלהם הם מהקבוצה {1,,k}
יהי p המסלול הקצר ביותר (שאנחנו יודעים שבגלל שאין מעגלים שליליים הוא מסלול פשוט) מבין כל המסלולים הנ״ל שלקחנו, p הוא מהצורה:

Pasted image 20230204163707.png|300

נשים לב לשתי מקרים חשובים שיכולים להיות עבור k :

p=ip1kp2j

Pasted image 20230204164708.png|350

כעת יש לנו 2 מסלולים p1,2 ש k אינו קודקוד ביניים בהם אבל הם מכילים רק קודקודים מהקבוצה {1,2,,k1} שהיא גם כן קבוצת קודקודי ביניים. אנחנו יודעים שתת מסלול של מסלול קצר ביותר הוא גם כן מסלול קצר ביותר כלומר p1 הוא המסלול הקצר ביותר מ i ל k שמשתמש בקבוצת קודקודי הביניים הנ״ל. ובאופן דומה p2 הוא המסלול הקצר ביותר מ k ל j שמשתמש בקבוצת קודקודי הביניים הנ״ל.

פתרון ריקורסיבי

נגדיר di,j(k) להיות אורך המסלול הקצר ביותר מ i ל j מבין המסלולים שקבוצת קודקודי הביניים שלהם היא {1,2,k} . נבחין: ש di,j(k) הוא איבר מהמטריצה D(k) שהיא חלק מקבוצת המטריצות

{D(i)  |  i[0,n]}

כל המטריצות הן מטריצות מרחקים עבור המסלול עם קבוצת קודקודי הביניים {1i} . כאשר i=0 המשמעות היא שאין קודקודי ביניים כלומר היא תכיל את המרחקים בין כל הקודקודים i,j שיש בינהן קשת ישירה או שאין ואז הערך הוא .

איך מאכלסים את di,jk ?
נוכל לאכלס כל איבר במטריצות הנ״ל באופן הבא, בזכות למת תת המסלול...

di,j(k)={wi,jk=0min(di,j(k1),dik(k1)+dkj(k1))k1

כלומר המינימום בין המקרה ש k הוא קודקוד ביניים למקרה שהוא לא קודקוד ביניים, מהנ״ל אחד מהם חייב להתקיים. כך ממשיכים עד שנוכל לקחת באופן ישיר את משקל הקשת.

הבחנה:

כיוון שלכל מסלול קודקודי הביניים הם מהקבוצה V={1,,n} המטריצה D(n) תכיל את הפתרון הסופי:

dij(n)=δ(i,j)

תכנות דינמי

הקלט יהיה מטריצת שכנויות W שתקיים

D(0)=W

כלומר במקום לשים ערך בינארי במטריצה , היכן שיש קשת נשים את משקלה. החישוב אם כן, יהיה מההתחלה לסוף, על כל D(i) נחשב את D(i+1) עד שנגיע ל D(n) .

floyd_warshall(W):
	n = W.rows
	D[0] = W
	for k = 1 to n 
		D[K] = new matrix[n,n]
		for i = 1 to n
			for j = 1 to n
				d_ij[k] = min(d_ij[k-1], d_ik[k-1]+ d_kj[j-1])

זמן ריצה O(|V|3) .

שחזור פתרון:
ניתן לחשב את מטריצת הקודמים Π תוך כדי חישוב האלגוריתם של D(k) .
יתקיים

Π=Π(n)

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

Π(0)Π(n)

נגדיר πij(k) להיות האבא של קודקוד j במסלול קצר מ i כאשר כל קודקודי הביניים הם מהקבוצה [k] .
מקרה הבסיס הוא k=0 כלומר אין קודקודי ביניים ויתקיים

πij(0)={nulli=j or wij=iij and wij<

בכל מצב אחר :

1kn:πij(k)={πij(k1)dij(k1)dik(k1)+dkj(k1)πkj(k1)else

נשלב את הפונקצייה בפתרון הנ״ל וזה לא יפריע לזמן הריצה.

Screenshot 2023-02-04 at 18.22.38.png|400

האלגוריתם של ג'ונסון

האלגוריתם של גונסון פותר את בעיית APSP בזמן O(|V|2log|V|+|V||E|) שזה נחשב שיפור משמעותי אם הגרף דליל..

דליל - O(V2logV)
צפוף - O(V2logV+V3)=O(V3)

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

הבחנה:

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

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

באופן פורמלי נרצה להגדיר w^ שתקיים שתי תנאים:
א) w^:ER+ - כלומר פונקצייה אי שלילית.
ב) w(uv)=δ(u,v)w^(uv)=δ^(u,v) כלומר, מסלול קצר ביותר לפי פונקציית המשקל המקורית חייב להיות מסלול קצר ביותר לפי פונקציית המשקל החדשה שנגדיר.

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

למה 1 שקלול מחדש לא משנה את המסלולים הקצרים:
יהי G=(V,E) גרף מכוון ממושקל עם w:ER ותהי h:VR פונקצייה כלשהי שממפה קודקודים למספרים ממשיים.
לכל קשת (u,v)E נגדיר

w^(u,v)=w(u,v)+h(u)h(v)

יהי p=(v0,v1,vk) מסלול כלשהו מ v0 ל vk אז:

w(uv)=δ(u,v)w^(uv)=δ^(u,v)

בנוסף, G יכיל מעגלים שליליים לפי w אמ״מ G מכיל מעגלים שליליים לפי w^ .

הוכחה:

w^(p)=i=1kw^(vi1,vi)=i=1kw(vi1,vi)+h(vi1)h(vi)=i=1kw(vi1,vi)+h(v0)h(vk)=w(p)+h(v0)h(vk)

הראנו תכונה מעניינת על הפונקצייה החדשה שאנחנו מגדירים כאשר מפעילים אותה על המסלול. בגלל שיש מעין טלסקופיות בטור הנ״ל. לא משנה מיהו המסלול p כל עוד הוא מתחיל בקודקודים v0 ונגמר ב vk תמיד נסכום ונחסיר את הפעלת h על הקודקודים האלו. בפרט זה נכון על המסלול הקצר ביותר מ v0 ל vk וכיוון שאלו לא תלויים במסלול אזי יתקיים שאם p הוא המסלול הקל ביותר בין v0 ל vk לפי w אז ברור שגם p יהיה המסלול הקל ביותר לפי w^ .

נשים לב שאם c=(v0,,vk) מעגל, אזי :

w^(c)=w(c)+h(v0)h(vk)=w(c)+h(v0)h(v0)=w(c)

כלומר המשקלים של כל המעגלים זהים ובפרט אלו השליליים.

למה 2 יצירת משקלים אי שליליים באמצעות שקלול מחדש:
בהינתן גרף מכוון G=(V,E) משוקלל עם w:ER , נבנה גרף חדש G=(V,E) כאשר

V=V{s}     where: sVE=E{(s,v)  |  vV}vV:w(s,v)=0

כעת נניח ש G,G לא מכילים מעגלים שליליים, נגדיר h(v)=δ(s,v) לכל vV .
נרצה להוכיח : w^0 כלומר זאת פונקצייה אי שלילית.

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

לפי אי שיוויון המשולש עבור קשת (u,v)E יתקיים:

δ(s,v)δ(s,u)+w(u,v)0w(u,v)+δ(s,u)δ(s,v)0w(u,v)+h(u)h(v)0w^(u,v)

שלב ראשון, הוספת הקודקוד s כקודקוד מקור
Pasted image 20230204221937.png|550
שלב שני, חישוב מחדש של המשקלים לפי w^
Pasted image 20230204222053.png|550

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

johnson(G=(V,E), w):
	initialize G'
	if bellman-ford(G',w ,s) == false 
		error : negative cycle
	for each u in G.V:
		h(u)= 𝜹(s,u)
	for each (u,v) in G.E:
		w'(u,v) = w(u,v) + h(u) - h(v)
	D = new matrix[n,n]
	for each v in V 
		dikstra(G, v, w')
		for each u in V
			\\the reverse function of w' to get the original w value
			d_uv = 𝜹'(u,v) - h(u) + h(v) 

	return D

זמן הריצה של האלגוריתם שקול ללהריץ פעם אחת בלמן פורד ו |V| פעמים דייקסטרה כלומר:

O(|V||E|+|V|2log|V|)