אחד האלגוריתמים הפשוטים לחיפוש גרפים, ולביצוע SSSP .
בהינתן גרף וקודקוד מקור , עובר בצורה סיסטמטית על קשתות הגרף ומבקר בכל קודקוד הנגיש מ (נגיש = קיים מסלול).
תוך כדי האלגוריתם מחשב את המרחק (מספר הקשתות במסלול) מ לכל קודקוד נגיש, נוכיח בהמשך שזה המסלול הקצר ביותר. בנוסף הוא מייצר עץ רוחב Breadth-First Tree או עץ מסלולים ששורשו שמכיל את כל הקודקודים והמסלולים הפשוטים הנגישים מ .
כמה דגשים חשובים:
א) האלגוריתם עובד עם גרף מכוון ולא מכוון. ללא פונקציית משקל.
ב) האלגוריתם מבקש בקודקודים במרחק מ לפני אלה שנמצאים במרחק
בדומה ל DFS במהלך ריצת האלגוריתם, קודקוד יכול להיות צבוע באחד משלושת הצבעים:
לבן- כל הקודקודים מתחילים לבנים, וקודקוד בצבע לבן אומר שעדיין לא ביקרנו בו.
אפור- קודקוד בצבע אפור אומר שביקרנו בו ועדיין לא סיימנו את הטיפול בכל שכניו.
שחור- אומר שסיימנו לטפל בקודקוד.
עבור קשת אם שחור אז בהכרח או אפור או שחור לפי מה שאמרנו.
כמו כן, נשייך לכל קודקוד ערך המהווה קודקוד האבא בעץ הרוחב. כל הקודקודים מתחילים עם וכשנציב ערך , הוא לא ישתנה לאורך כל האלגוריתם.
בנוסף בדומה לאלגוריתמים נוספים שמחשבים את ה SSSP , לכל קודקוד משוייך ערך d שבסיום הריצה יכיל את המרחק המינימלי של הקודקוד הזה מ , ואם אין כזה הערך יישאר .
BFS(G,s)
for each u in V-{s}:
u.color = WHITE
u.d = ∞
u.𝝅 = NIL
s.color = GRAY
s.d = 0
s.𝝅 = NIL
Q= {}
Q.enqueue(s)
while Q != {}:
u = Q.dequeue()
for each v in adj(u):
if v.color == WHITE
v.color = GRAY
v.d = u.d + 1
v.𝝅 = u
Q.enqueue(v)
u.color = BLACK
האלגוריתם מניח ש מיוצג על ידי רשימת שכנויות
הוא תור FIFO.
זמן ריצה
בגלל שלאחר האתחול האלגוריתם לא צובע אף קודקוד בצבע לבן אנחנו מבטיחים שהתנאי if color == white יתקיים עבור כל קודקוד פעם אחת בידיוק.
פעולת הכנסה והוצאה הן בעלות קבועה וזאת קוראת לכל קודקוד את כל שכניו כלומר עבור כל הקודקודים. לכן הפעולה הזאת פעולת האתחול שלוקחת אומר שהאלגוריתם BFS יעלה לנו:
מסלולים קצרים ביותר
נרצה להוכיח שבסיום ריצת עבור קודקוד מקור מתקיים
ואם אין מסלול אז הערך הוא .
נוכיח כעת כמה למות שיעזרו לנו בשביל ההוכחה שBFS מביא את המסלולים הקצרים ביותר עבור גרף לא ממושקל:
אי שיוויון המשולש
יהי גרף מכוון או לא מכוון ויהי קודקוד מקור . אזי
הוכחה:
אם u לא נגיש מ s אז והטענה מתקיימת.
אם u נגיש מ s אז גם v. נניח בשלילה
נגדיר את P המסלול הקצר ביותר מ ל ולכן אורכו אזי אם נוסיף את למסלול הזה נקבל שאורכו , וזה אורך מסלול מ ל שקצר יותר מ בסתירה למינימליות של מסלול קצר ביותר.
חסם עליון
יהי גרף מכוון או לא מכוון ויהי קודקוד מקור . אזי בסיום ריצת BFS
הוכחה:
באינדוקצייה על מספר פעולות ההכנסה לתור. בסיס- הפעולה הראשונה היא הכנסת . בזמן זה מתקיים וכל שאר הקודקודים מקיימים ולכן הנ״ל מתקיים בשלב זה.
צעד - נניח שהטענה נכונה לאחר הכנסת קודקודים לתור. נוכיח שהטענה נכונה עבור הקודקוד ה . נסמן את הקודקוד הזה ב , שהוכנס תוך כדי סריקת רשימת השכנויות של .
לפי הנחת האינדוקצייה
לפי אי שיוויון המשולש ולפי הנחת האינדוקצייה
ואז נכנס לתור וצבעו אפור, צבעו לא יהפוך ללבן לאורך ריצת האלגוריתם ולכן לא יוכנס שוב לתור כלומר לא ישתנה כדרוש.
לכל היותר שני ערכי d שונים בתור בכל רגע
נניח שבמהלך ריצת BFS על הגרף , התור מכיל את הקודקודים כאשר הוא ראש התור. אזי:
א)
ב)
נוכיח באינדוקצייה על מספר פעולות התור. בסיס:
פעולת ההכנסה הראשונה = הכנסת לתור, הטענה כמובן מתקיימת.
פעולת ההוצאה הראשונה = הוצאת מהתור, לא נשארו קודקודים, הטענה מתקיימת.
צעד:
נוכיח נכונות אחרי הוצאת או הכנסת קודקוד כאשר מניחים נכונות לכל אלה שנמצאים לפני הפעולה.
הוצאה = נוציא את ראש התור עליו מתקיימת הטענה, לכן אם התור ריק סיימנו. אם לא אז כעת נמצא בראש. לפי הנחת האינדוקצייה
וגם מתקיים
זה הוכחה של סעיף א.
נכונות של סעיף ב נובעת מכך שלפי הנחת האינדוקצייה גם כ היה בתור והוצאתו לא שינתה ערכים ולכן היחס סדר הזה עדיין נשמר.
הכנסה = נסמן כקודקוד שנכנס לסוף התור. כדי להגיע להכנסה הזאת הוצאנו קודקוד שהוא אבא של . מהנחת האינדוקצייה יתקיים עבור הראש החדש של התור ש
וזה בלי קשר למתי נכנס לתור, כלומר לא משנה האם הוא היה בתור בזמן הוצאת או שהוא נכנס לתור כתוצאה מסריקת השכנים.
מהאלגוריתם מתקיים
וזה ההוכחה של סעיף א.
על פי הנחת האינדוקצייה לפני הוצאתו מהתור מתקיים (כמובן שמתקיים גם לאחר ההוצאה)
ולכן
כיוון שמהנחת האינדוקצייה אנחנו יודעים שלכל מתקיים אז סיימנו כעת גם להוכיח את סעיף ב.
מסקנה נניח ש נכנס לפני לתור במהלך BFS אזי
זה מסתדר עם העובדה שאנחנו סורקים לרוחב את הגרף כלומר מתחילים מהשכנים המיידים של ומעלים את d עבורם ב 1 וכן הלאה ממשיכים לשכנים שלהם עד שמגיעים לקודקודים ה״רחוקים״ ביותר מ .
נכונות BFS
נרצה להוכיח כמובן שבסיום ריצת BFS על גרף עם קודקוד מקור יתקיים
כמו כן נוכיח שאחד המסלולים הקצרים ביותר מ ל הוא המסלול הקצר ביותר מ ל עם הוספת הקשת .
נב״שׁ שקיים קודקוד כך ש בסוף האלגוריתם.
בדומה להוכחה ב SSSP אנחנו יודעים להגיד ש וש , כלומר , קיים מסלול בין ל (אחרת הערך היה ומהלמות שהוכחנו למעלה ערך זה לא משתנה בסתירה להנחה שלנו).
נסמן את הקודקוד הקודם של במסלול הקצר ביותר שאורכו ולכן
כעת נבין מה קורה כשמוציאים את מהתור. בזמן הזה יכול להיות צבוע באחד משלושת הצבעים
א) אם הוא לבן: אז נקבל שבבדיקת שכני נעשה השמה בסתירה.
ב) אם הוא שחור: זאת אומרת שהוצאנו מהתור לפני שהוצאנו את ולכן מהמסקנה שהוכחנו, בסתירה.
ג) אם הוא אפור: אז קיים קודקוד כך ש ואז ולפי אותה מסקנה שהראנו כלומר בסתירה.
סך הכל הוכחנו שלכל קודקוד יתקיים בסוף האלגוריתם כדרוש.
כמו כן כאשר האבא של הוא אז נקבל בשורת ההשמה של האלגוריתם
כלומר שהמסלול הקצר ביותר מ ל הוא המסלול הקצר ביותר מ ל פלוס 1.
עץ הרוחב
BFS בונה עץ רוחב במהלך ריצתו ביחס לערכי .
נגדיר Predecessor subgraph של להיות תת הגרף כאשר:
תת הגרף הוא עץ הרוחב של עם שורש וכל הקודקודים הנגישים ממנו. כמו כן לכל הקודקודים ב מתקיים שתת הגרף הזה מכיל מסלול ייחודי מ ל שאורכו כלומר מסלול קצר ביותר בגרף המקורי.
ההוכחה שאכן עץ הרוחב הוא תת הגרף הזה נובעת ישירות מהאלגוריתם אנחנו יודעים שהאלגוריתם מגדיר
כלומר ההשמה הזאת מתרחשת רק אם היחס בינהם הוא שהם מרכיבים קשת וגם נגיש מ . מהשמה זאת ומהוכחת הנכונות של BFS אנחנו יודעים שבסופו של האלגוריתם כל הקודקודים שיסומנו בשחור ירכיבו את המסלולים הקצרים ביותר מ לכל הקודקודים האחרים ומהגדרת הוא עץ ולכן מכיל מסלול פשוט ייחודי מ לכל הקודקודים הנגישים.
הדפסת מסלול קצר ביותר
הפרוצדורה הבאה מדפיסה את הקודקודים הנמצאים על המסלול מ ל :
print_path(G,s,v)
if v==s
print s
else if v.𝜋 == Nil
no path from s to v exists
else print_path(G,s,v.𝜋)
print v