מומלץ לקרוא את הגדרות בגרפים לפני שנכנסים לסיכום זה.
יהי
נגדיר את המשקל של
עץ פורש מינימלי MST של
נרצה לתכנן אלגוריתם שמקבל
ישנם שני אלגוריתמים חמדניים שמבצעים זאת ומבוססים על אותו אלגוריתם גנרי ובחירה חמדנית.
אומנם אנחנו מבקשים מולטי גרף קשיר אבל האלגוריתמים שנציג כאן הם אלגוריתמים שמבקשים גם גרף לא מכוון אחרת הם לא יעבדו
תהי קבוצת קודקודים לא ריקה
יהי
הוכחה
ניקח קבוצת קודקודים
יהי
כעת, נבנה גרף חדש
נרצה להראות ש
עץ פורש
מהקשר של
כלומר אם נוכיח ש
נוכיח שקיים מסלול בין כל שתי קודקודים
אם הקשת שהורדנו מ
אחרת,
הסיבה שהוא בטוח יופיע שם היא שעכשיו הורדנו את
נסתכל על
כאשר
כלומר מה שעשינו הוא ויתרנו על הקשת
אם כן, יש מסלול מכל נקודה לנקודה ולכן הגרף קשיר וסך הכל הוא גם עץ. מהגדרת עץ פורש מתקיים שהוא פורש את הגרף
המשקל של
נשווה בין המשקל של
מתקיים
סך הכל יתקיים
ואמרנו ש
סך הכל מחוקי אי שיוויון יתקיים
ולכן הבחירה החמדנית שאומרת שישנו עץ פורש מינימום שמכיל את הקשת הקלה בחתך נכונה.
הצגנו את תכונת בחירה חמדנית של עצים פורשים מינימליים שנראת בסיס טוב לאלגוריתם חמדן שמוסיף ל MST בכל פעם את קשת החתך המינימלית עבור חתך כלשהו . למרות זאת , ייתכנו כאן שני קשיים :
הראשון , ההגדרה הרקורסיבית שנרצה להשתמש בה , טיפה לא ברורה , אופציה ראשונה היא למחוק קשת החתך המינימלית בחתך כלשהו מהגרף ופותרים באופן רקורסיבי עבור כל אחד משני החתכים , אבל אנחנו לא מבטיחים שאין עוד קשתות חתך שצריכות להיות בעץ הפורש המינימלי .
אופציה שניה למחוק את קשת החתך המינימלית מהגרף , אבל אחרי זה אנו צריכים שיטה להימנע ממעגלים בקשתות שבוחרים ל 𝑇𝑆𝑀.
עבור מולטי גרף
והוא הגרף המתקבל מ
כעת נגדיר פונקצייה שממירה קודקוד גרף לקודקוד בגרף המצוצמם באופן הבא
סך הכל נוכל להגדיר את קבוצת הקשתות של הגרף המצומצם כך
נשים לב שבגלל שאנחנו עובדים עם גרף לא מכוון אז אין לולאות עצמיות. כמו כן תכונת הצמצום מאפשרת לנו לדעת בוודאות שלא יווצר מעגל כתוצאה מהצמצום בגלל שאין לולאות עצמיות.
כעת התכונה הזאת מאפשרת לנו לבנות את האלגוריתם הגנרי הבא
MST-Generic(G=(V,E),w)
while |V|>1
let e be the minimum weight edge of some cut in G
add e to E_t
conrtract e
return E_t
נשים לב שזה אלגוריתם גנרי ולא מימוש מלא. ישנם שתי אלגוריתמים שהם מימוש של הנ״ל והם נבדלים באיך שהם בוחרים את החתך. בחירה זו משנה את זמן הריצה.
יהי
ויהי
תהי
אזי,
עץ פורש
ראשית נרצה להוכיח
אבל $$|E_{T}|=|E_{T^{\prime}}|+1= |V|-2+1= |V|-1$$
כעת הוכחנו שבהינתן שהוא גרף קשיר אז הוא עץ.
נוכיח שהוא קשיר, בהינתן שתי קודקודים
נפעיל את פונקציית הצמצום
אחרת הפתרון לבעיה הוא פשוט. כל מסלול שעובר בקודקוד
משקל מינימלי
נב״ש ש
ניקח
כעת ניקח
נשים לב ש
קיבלנו סך הכל
קיבלנו
אבל
סה״כ קיבלנו שבנייה של עפ״מ באופן ריקורסיבי על ידי הוספת קשת חתך מינימלי לתת עץ פורש מינימלי משאירה אותו מינימלי ולכן תכונת תת המבנה האופטימלית מתקיימת.
הרעיון באלגוריתם פרים הוא להסתכל על החתך
על מנת לשלוט ביעילות האלגוריתם, הקודקוד ב
ז״א שאם יש קשת חתך שלא הצטמצמה , אז הקשת תהיה החתך הבא.
בגלל שמתסכלים רק על קשתות החתך, אנחנו נמנעים מסריקת כל הקשתות בכל איטרציה. אף על פי כן, צמצום קשת יכול להוסיף קשתות חדשות לחתך הבא.
כיוון שהגרף הוא מולטי גרף ייתכנו כמה קשתות בין אותם קודקודים כאשר לכל קשת מששקל שונה, אבל כיוון שמחפשים קשת חתך מינימלית בכל פעם, נוכל פשוט לשמור מידע עבור הקשת המינימלית בין זוג קודקודים.
באופן סכימתי האלגוריתם ייראה ככה:
נשים לב לשתי דברים חשובים
ההצטמצמיות מבוצעות ע"י תור 𝑄 כאשר ערך ה- keys לכל קודקוד 𝑢 הוא המשקל של הקשת עם משקל מינימלי שמקשרת בין 𝑢 לקודקוים שנמחקו מ- 𝑄 . באתחול , כל ה- keys להיות ∞ . חוץ מהקודקוד השרירותו r שנבחר לראשונה , המפתח שלו הוא 0 בהתחלה . בנוסף מאתחלים Q להכיל את כל 𝑉 . וכך , הקודקוד הראשון שמוחקים מ- 𝑄 הוא 𝑟 , ואז מעדכנים את המפתחות לכל השכנים של 𝑟 להיות המשקל של הקשת ביניהם לבין 𝑟 . מהאיטרציה השניה והלאה , האינטרפרטציה צריכה להיות שכל הקודקודים שנמחקו מ- Q הצמצמו לקודקוד אחד. וכך , המפתח הקטן ביותר לקודקודים שעדיין ב- 𝑄 מייצגים את הקשת המינימלית שמקשרת אותם לקודקוד היחיד שהוצמצם . ולכן , האלגוריתם מוציא את הקודקוד 𝑢 עם המפתח עם המפתח הקטן ביותר ומוחק 𝑢 מהתור 𝑄. האינטרפרטציה היא שקשת חתך מינימלית מתווספת ל- 𝑇𝑆𝑀. על מנת לבצע צמצום של קשת חדשה שבחרנו להכנה לאיטרציה הקודמת , האלגוריתם סורק את כל השכנים של 𝑢 לבדוק אם המפתח שלו צריך הפחתה , עקב כניסת קשת חדשה לחתך.
MST-PRIM(G=(V,E),w)
E_T = empty_set
for each u in V
u.key = INFINITY
u.p = NULL
set r.key = 0 for some arbitrary r in V
Q.init(V)
while |Q| >= 1
u = Q.extract_min()
if u.p != NULL
E_T.add(u,u.p)
for each v in u.ADJ()
if Q.contains(v) and weight(u,v)< v.key
v.key = weight(u,v)
v.p = u
return E_T
זמן ריצה
זמן הריצה תלוי גם באימפלמנטצייה של
operation | amount of times | Heaps using array | Binomial heap or Binary Heaps | Fibonacci Heap |
---|---|---|---|---|
Init | O(1) | O(V) | O(V) | O(V) |
extract_min | V | O(V) | O(log(V)) | O(log(V)) |
decrease_key | 2E | O(E) | O(log(V)) | O(1) amortized |
total: | O(V^2) | O(E log(V) | O(Vlog(V)+E) |
כדי להבין את האלגוריתם של prim מומלץ לעבור קודם על Union find .
הרעיון של האלגוריתם הזה הוא למצוא את הקשת המינימלית בגרף המצומצם ולהוסיף את הקשת הזו ל MST.
המימוש יעבוד כך:
נשים לב שעל מנת לבדוק האם
נזכיר שהפעולות שמבנה נתונים זה תומך הן make_set ו find_sey ו union על קבוצות מופרדות.
find_set(u)=find_set(v)
.
MIST-Kruskal(G=(V,E),w)
E_T = empty_set
for each u in V
make set(u)
sort(E by w)
for each e = (u,v) in E
if find_set(u) != find_set(v)
E_T.add(e = (u,v))
union(u,v)
return E_T
זמן ריצה
במבנה נתונים union_find פעולת make_set לוקחת
כלומר זה תלוי כמה זמן לוקח למיין את E.