כמו שמידלנו מקודם מפת דרכים לגרף מכוון כדי למצוא מסלול קצר ביותר מעיר מוצא לעיר יעד ופתרנו בעיה זאת עם APSP ו SSSP , ניתן להסתכל על הגרף המכוון כ-"רשת זרימה" ולפתור בעיות שמערבות מעבר של חומר בין מקומות.
רשת זרימה מאופיינת על ידי גרף מכוון שמכיל שני קודקודים מיוחדים - האחד משמש כמקור -המקום שממנו נובעת הזרימה, והשני משמש כבור -המקום שאליו מתנקזת הזרימה.
ניתן להסתכל על כל קשת מכוונת ברשת הזרימה כצינור שהחומר יכול לעבור דרכו , כך שלכל צינור (קשת) יש קיבולת מוגדרת מראש שאומרת מה הקצב המקסימלי שבו החומר יכול לזרום דרך הצינור , למשל 200 וואטס עבור ה״כבל״ .
עבור כל הקודקודים , חוץ מהמקור והיעד , הקצב שבו החומר נכנס לקודוקד שווה לקצב שבו יוצא מהקודקוד , באופן אינטואיבי זה מהנחה ששום דבר לא פגום ב״חומרה״ שמעבירה את המידע. אם כן אנחנו רוצים לתאר באופן תיאורטי מערכת לזרימה של מידע, חומר או כל דבר אחר מנקודה אחת לאחר תחת ההנחה שבעת מעבר לא יכולים לקרות שגיאות ולענות על השאלה המרכזית ששואלת מהי כמות הגדולה ביותר של זרימה שניתן להעביר בפרק זמן מסויים. בעיה זאת מכונה זרימת מקסימום.
רשת זרימה
רשת זרימה היא גרף מכוון ללא לולאות עצמיות, עם פונקציית קיבול .
לכל קשת מוגדרת קיבולת חיובית ואם זוג קודקודים אינו קשת אז .
נניח שאם אז .
נסמן שני קודקודים מיוחדים קודקוד מקור ו קודקוד יעד, נניח שהגרף קשיר, כלומר לכל קיים מסלול ברשת .
כיוון שלכל קודקוד פרט לקודקודים המיוחדים יש לפחות כניסה אחת ויציאה אחת אז .
להלן רשת זרימה לדוגמה כאשר המספר על הקשת הוא הקיבולת.
זרימה
ישנן שתי הגדרות שקולות לזרימה.
הגדרה 1
תהי רשת זרימה עם קיבול וקודקודי מקור ויעד .
זרימה ב היא פונקצייה ממשית המקיימת
א) אילוץ הקיבול- לכל זוג קודקודים
ב) שימור הזרימה- לכל
ביחס להגדרה זו יתקיים שערך הזרימה הוא
במקרה שלנו רשתות זרימה לא יכילו קשתות נכנסות ל ולכן
מסקנה מיידית מההגדרה:
אם , אין זרימה מ ל ולכן .
הגדרה 2
תהי רשת זרימה עם קיבול וקודקודי מקור ויעד .
זרימה ב היא פונקצייה ממשית המקיימת
א) אילוץ הקיבול-
ב) סימטריה-
ג) שימור זרימה- לכל
כלומר עבור כל קודקוד שניקח הסכימה של כל הזרימה שנכנסת אליו בכל רגע נתון הוא .
הערך של זרימה הוא הזרימה שיוצאת מ בכל רגע נתון כלומר
כלומר כמות הזרימה נטו שיוצאת מן המקור . ניתן להראות בהתבסס על תכונות הזרימה כי כמות זו שווה לכמות הזרימה נטו שנכנסת אל הבור. על כן, ניתן לחשוב על ערך הזרימה בתור כמות הזרימה שעוברת ברשת הזרימה ביחידת זמן.
מסקנה מהגדרה זו:
בניגוד להגדרה הקודמת כאן אם אז כלומר הוא יכול להיות שלילי.
דוגמה - עבור רשת הזרימה הבאה עם פונקציית הקיבולת
זרימה אפשרית תהיה
זאת אכן זרימה תקנית עבור רשת זרימה תקנית שכן ברור משתי הטבלאות שהחוקים מתקיימים. אפשר לראות גם ששימור הזרימה מתקיים שכן מהטבלה של אם נסכום כל עמודה נראה שהסכום הוא .
ערך הזרימה הוא (סכימה של השורה של בטבלה ).
זרימת ה0
בדומה למה שעשינו למעלה, ניתן להוכיח גם ש כלומר פונקציית זרימת ה היא אכן פונקציית זרימה תקינה לפי ההגדרות
הבחנות
שימור הזרימה אומר שכמות הזרימה שנכנס מהקודקודים ה״רגילים״ צריכה להיות שווה לכמות הזרימה היוצאת מאותם קודקודים , המשמעות של זה היא שנקודות ביניים בדרך ליעד אינן ״אוגרות״ זרימה. הנקודות היחידות שיכולת להוסיף או להפחית זרימה הם . כמו כן ערך הזרימה הוא למעשה כמות הזרימה ש יוצר בפרק זמן כלשהו.
בפתרון הבעיה אתמקד בהגדרה 2 של זרימה, למרות שהן שקולות אז אין באמת חשיבות באיזה מהם משתמשים
Max Flow
בהינתן רשת זרימה עם פונקציית קיבול . נרצה למצוא פונקציית זרימה עם ערך זרימה מקסימלי .
כדי להתחיל לפתור את הבעיה נרצה להרחיב את מונח הזרימה כדי שיתאפשר לנו להוכיח כמה טענות חשובות.
זרימה בין קבוצות:
יהיו ופונקציית זרימה אזי
נשים לב
דוגמה:
עבור רשת הזרימה הקודמת נסמן
מתקיים:
באופן דומה
טענה
תהי רשת זרימה עם פונקציית קיבלת ו זרימה ברשת. אזי עבור כל מתקיים:
א)
ב)
ג) אם :
הוכחה
א - נובע ישירות מהעובדה שזרימה בין שתי קודקודים היא סימטרית ומהגדרת זרימה על קבוצות אנחנו סוכמים בעצם לכל גם את ולכן נקבל ב .
ב-
ג-
בגלל שהקבוצות זרות אז לא סוכמים איברים פעמיים.
החלק השני קל יותר:
למה 1
תהי רשת זרימה ו זרימה ברשת אזי
הוכחה
המעבר האחרון נובע מכך שהפעלנו את תכונה שימור הזרימה על כל הקודקודים ובעצם לא סכמנו כלום. המשמעות של זה היא כמות הזרימה היוצאת מ בכל רגע נתון שווה לכמות הזרימה שנכנסת ל בכל רגע נתון שזה מאוד הגיוני בהתחשב בהגדרות שלנו ובעובדה שאמרנו שרק אלו הם הקודקודים שיכולים לאגור או לפזר זרימה וכל האחרים רק מטווחים.
למה 2
תהי רשת זרימה ותהי זרימה ברשת.
יהי חתך של הרשת, כך ש ו אזי
הוכחה
המעבר האחרון נובע משימור זרימה.
דוגמה 1 על אותה זרימה ורשת זרימה שהשתמשנו בה מההתחלה
דוגמה 2
שיטת פורד־פולקרסון
נראה עכשיו שיטת FORD-FULKERSON שפותרת את הבעייה של זרימת מקסימום. נשים לב שמדובר ב״שיטה״ ולא באלגוריתם של ממש כי יש כמה מימושים שונים עם זמני ריצה שונים.
הרעיון הבסיסי
מתחילים עם פונקציית זרימה לכל זוג קודקודים ולאחר מכן מחפשים דרכים לשפר את הזרימה על ידי בדיקת מסלולים מהקודקוד ליעד, אם מתגלה מסלול שבו ניתן להעביר עוד זרם, הפונקצייה מתוקנת בהתאם, חוזרים על התהליך עד שמתקבלת פונקצייה זרימה עם ערך אופטימלי. טכניקה זאת נקראת שיפור איטרטיבי
נשים לב שהבחירה החמדנית של בחירת הקשת שיכולה לקבל הכי הרבה זרם לא בהכרח תעבוד כי אנחנו יכולים להתקע בשיטה הזאת ולכן נרצה לחשוב על דרך אחרת למציאת המסלולים .
על פניו נשמע פשוט אבל צריך להוכיח את זה ולכן נגדיר מספר דברים נוספים.
Augmenting path - מסלול השיפור
מתחילים עם פונקציית זרימת האפס ונרצה לעדכן את כך ש יגדל.
אינטואיטיבית, נמצא מסלול מ ל ונעביר דרכו כמה שיותר זרם.
כמה ניתן להעביר?
ניתן להעביר זרם בידיוק ככמות הקיבולת המינימלית מבין כל הקשתות במסלול, זה כדי לשמור על תנאי אילוץ הקיבול לזרימה!
תהי קשת עם הקיבולת המינימלית, לשם הנוחות נגדיר
להיות הערך של הקיבולת המינימלית במסלול.
כדי להעביר את הזרם, צריכים לעדכן את הזרימה בכל הקשתות שנמצאות על המסלול, כלומר
כמובן שכעת יש לעדכן את הקיבולת הנוכחית לאחר העברת הזרם כלומר
כמו כן צריך לעדכן בהתאם לחוק הסימטריה גם את הזרם והקיבולת בקודקודים
לאחר התהליך, הקשת עם הקיבלות המינימלית תהיה בעלת קיבולת נוכחית קשת כזאת נקראת ״רוויה״ או Saturated בלעז. כלומר לא ניתן להעביר עוד זרם דרכה.
וכך יכולים לחזור על התהליך עבור כל המסלולים הזרים ולעדכן את הזרימה בהתאם, מסלולים כאלה נקראים Augmenting Paths.
השאלה המתבקשת כעת היא: מה אם יש מסלול מ ל עם קשת שכבר ראינו באחד מהמסלולים הקודמים?
אם נגיע לקשת כזאת והיא רווייה אז היא יכולה לחסום עוד קשתות וכך נפספס מסלולים אפשריים.
כלומר נרצה לייעל את השיטה שלנו לשיטה שבה נוכל להתחרט על העדכון שעשינו, כלומר לחזור דרך הזרם ההופכי (שיהיה שווה למקורי) וכך לחזור צעד אחד אחורה.
אם כן אז לתמצות הרעיון, אנחנו מבינים שסכימה של ערכי הצוואר בקבוק שמובילים ל t (כלומר הרווייתם עד כמה שניתן) תוביל אותנו לזרם המקסימלי. הבעיה היא שאנחנו צריכים את היכולת להגיע לצווארי הבקבוק האלה מכל מיני מקומות ולשם כך חסרה לנו יכולות שמאפשרת לנו להתחרט על הזרמות לא טובות שעשינו, כדי לאפשר זאת ננצל את העובדה שלכל קשת שאנחנו מזרימים בי תמיד נוצרת זרימה נגדית לכיוון השני כדי לעקוב אחרי השינויים ברשת ולהתחרט אם גילינו שיש מסלול טוב יותר שיאפשר להזרים יותר לצוואר בקבוק
הרשת השיורית
תהי רשת זרימה עם פונקציית קיבול וזרימה .
הקיבולת השיורית של ו מוגדרת להיות (תמיד אי שלילית)
כלומר ״כמה עוד זרם ניתן להעביר בנוסף לזרם הנוכחי דרך הקשת הזו״.
רשת הזרימה השיורית היא רשת זרימה עם פונקציית קיבול כאשר
כאן נכנס הכוח של הזרימה השלילית שהגדנו שבזכותה אנחנו תמיד נקבל קשת לכיוון הנגדי מהכיוון שבו הזרמתי כל עוד אני מזרים ברשת המקורית, כלומר יהיו שתי קשתות אם הזרמתי ברשת השיורית אבל לא הרוותי את הקשת וזה מידע שימושי מאוד.
בעצם הרשת השיורית נותנת לנו את היכולת לדעת, כמה הזרמתי לכיוון מסויים על ידי בנייה של קשתות שנכנסות לקשתות שמהן הוצאנו את הזרימה הזאת (יכולות גם להכנס ל ) אבל הוא גם מראה לנו קשתות שמהן ניתן עוד להזרים מידע ובכך למקסם את הזרם.
הרעיון הוא שעל כל זרימה שנבנה אם קיים מסלול ברשת השיורית מ ל אז אני יכול למקסם את הזרימה ברשת המקורית.
למשל אם נסתכל על הרשת זרימה הזאת (כמובן שאמורות להיות גם זרימות שליליות לכיוון השני אבל נזניח את זה לרגע)
הרשת השיורית תיראה ככה
ישנו מסלול ברשת השיורית מ ל ומשמעות הדבר היא שאני יכול עוד למקסם את הזרם, לא בעיה להראות למה זה בא ביחד הרי שאם יש עוד מסלול לאחר העברת הזרם בגרף משמעות הדרך היא שאני יכול לעדכן את הקודקודים הרלוונטים בגרף המקורי ולהוסיף להם את של אותו מסלול שיפור ומהגדרת ערך הזרימה היא תגדל גם כן. נשים לב שאנחנו כל הזמן מעדכנים את הזרימה בהתאם למסלול השיפור בפועל מה שיקרה בגרף המקורי הוא שאם הקשת בשיורי נמצאת גם במקורי , נוסיף לה את ואחרת אנחנו ״מחזירים״ זרימה שכבר הזרמנו למשל בגרף הנ״ל אנחנו נחזיר זרימה של כי זה הקיבולת המינימלית במסלול השיפור, במקום להזרים אנחנו נזרים שם .
השיטה
ford_fulkerson(G=(V,E),s,t,c)
create f = 0
Gf = G , cf = c
while exists path P from s to t in Gf:
cfp = min cf(u,v) from all (u,v) in P
for each edge (u,v) in P:
f(u,v) = f(u,v) + cfp
cf(u,v) = cf(u,v) - cfp
f(v,u) = f(v,u) - cfp
cf(v,u) = cf(v,u) + cfp
update(Gf.E)
בסוף השיטה הפונקצייה היא פונקציית הזרימה המקסימלית. נשים לב שמעדכנים את הקשתות ברשת השיורית, כל קשת שהקיבולת השיורית שלה התאפסה תוסר מהרשת השיורית, וכל קשת שהייתה מאופסת וכעת ערכה חיובי, תתווסף לרשת השיורית.
זאת, על-מנת שנדע באילו קשתות ניתן להשתמש במסלולים.
דוגמה
וערך הזרימה המקסימלית הוא הבחנה מעניינת שכדאי לשים לב אליה היא שקשתות מהגרף המקורי יורדות לאורך האלגוריתם רק אם הן רוויות (שזה הגיוני מההגדרה אבל כדאי לראות זאת בדוגמה באופן מוחשי)
נכונות פורד־פולקרסון
כדי להתחיל להוכיח את הנכונות של השיטה הנ״ל נגדיר חתך .
הגדרה
חתך הוא חתך כאשר ו . נסמן מטעמי נוחות.
הקיבולת של חתך היא
דוגמה:
למה
עבור רשת זרימה עם פונקציית קיבול וזרימה כלשהי ברשת.
לכל חתך מתקיים
הוכחה
ראשית אנחנו כבר יודעים ש
האי שיוויון נובע מאילוץ הקיבול.
הבחנה
אנחנו יודעים אם כן שערך הזרימה חסום מלמעלה על ידי קיבולת חתך כלשהו. בפרט זה נכון על החתך עם הקיבולת המינימלית. מצד שני הייתה זרימה שרירותית. כלומר גם הזרימה עם הערך המקסימלי נסמנה חסומה מלמעלה על ידי חתך עם קיבולת מינימלית. בהמשך נראה שחתך המינימום הוא שווה בערכו לערך הזרימה.
Max Flow Min Cut
תהי רשת זרימה עם פונקציית קיבול וזרימה ברשת. השלושה הבאים שקולים:
זרימה עם ערך מקסימלי.
לא מכילה מסלולי שיפור.
קיים חתך ב כך ש
הוכחה :
נניח בשלילה שקיים מסלול שיפור ב ולכן יכולים לעבור על הקשתות שלו ולעדכן את הזרימה ובפרט לעדכן את הזרימה לקשת שיוצאת מ בסתירה למקסימליות של .
:
נתבונן בחתך
כלומר החתך שנפעיל על הגרף יהיה כל הקודקודים שקיים מסלול בגרף השיפור מ אליהם (כולל ).
זה הוא אכן חתך , זה נובע ישירות כי אנחנו מניחים ש לא מכילה מסלולי שיפור.
כעת עבור קשתות החתך נוכיח כ
מתנאי אילוץ הקיבול אנחנו יודעים שהזרימה בינהם קטנה או שווה מהקיבולת. נוכיח שהקיבולת במקרה הזה גדול או שווה מהזרימה כדי שיתקיים שיוויון.
נניח בשלילה ש . מהגדרת קיבלות שיורית מתקיים כלומר במקרה הזה הקיבולת השיורית גדולה ממש מ . בגרף השיורי יתקיים אם כן ש כלומר יש קשת חתך בגרף ולכן יש מסלול מ ל כי נוכל לבנות את המסלול
בסתירה לכך ש נמצאת ב . סה״כ מתקיים
:
נניח בשלילה ש זרימה שערכה לא מקסימלי, כלומר עבור החתך שמקיים את ישנה זרימה כך ש
בסתירה לכך שקיבולת חתך היא חסם עליון של כל זרימה.
נשים לב
השקילות בין היא כל שהיינו צריכים כדי להוכיח את נכונות השיטה שלנו שכן אנחנו המשכנו להוסיף זרם עד שלא מצאנו מסלולי שיפור ברשת השיורית. את 3 אפשר להגיד שקיבלנו מתנה ונשתמש בזה לפתרון בעייה אחרת שנראה כאן.
זמן ריצה FORD-FULKERSON
אם כל המספרים שלמים האלגוריתם בהכרח עוצר : נניח שכולם שלמים , והזרימה 𝑓 פונקציית שלמים , אז הקיבולת המינימלית לקשת במסלול כשלהו היא שלמה וכך הזרימה תעודכן למספרים שלמים . אם המספרים לא-היגיוניים האלגוריתם לא תמיד עוצר אך לא אעמיק על זה.
זמן הריצה תלוי בבחירת מסלול השיפור יש לכך חשיבות קריטית ונראה שתי אלגוריתמים דיניץ ו אנמונדס-קארפ שכל אחד מהם בוחר את מסלול השיפור בצורה אחרת ויש לכך השפעה על זמן הריצה.
נשים לב שבכל איטרציה ערך הזרימה יגדל ב לפחות. כלומר זמן הריצה חסום על ידי כלומר על ידי ערך הזרימה של הזרימה האופטימלית. בכל איטרציה יש עבודה מקומית ולכן החסם העליון לאלגוריתם הוא . זה יכול להיות גרוע מאוד למשל בדוגמה הבאה , צריך 2 מיליון איטרציות כדי לסיים.
הצוואר בקבוק ש מייצר גורם לכך שתמיד יהיה מסלול מ ל כי כל איטרציה ולכן כל פעם נוסיף לזרימה של הקשתות עם קיבולת מיליון עד שנקבל מצב של רווייה עבורן. זה ייקח 2 מיליון איטרציות עד שנרווה את את קשתות אלו ולא יהיה מסלול מ ל .
אלגוריתם אדמונדס-קארפ
שיפור בזה אחר זה, ולא יעצור מעולם. אלגוריתם אדמונדס־קארפ הוא צורת מימוש אחת של שיטת פורד־פלקרסון. במימוש זה בכל שלב נמצא מסלול שיפור ברשת השיורית בעל מספר מינימלי של קשתות. מציאת המסלול מתבצעת על ידי הרצת BFS מ עד שמגיעים ל .
admonds_karp(G=(V,E),s,t,c):
create f = 0
Gf = G , cf = c
while exists path P from s to t in Gf:
find P using BFS
cfp = min cf(u,v) from all (u,v) in P
for each edge (u,v) in P:
f(u,v) = f(u,v) + cfp
cf(u,v) = cf(u,v) - cfp
f(v,u) = f(v,u) - cfp
cf(v,u) = cf(v,u) + cfp
update(Gf.E)
נוכיח שהאלגוריתם מוצא זרימת מקסימום בתוך איטרציות כלומר זמן הריצה של האלגוריתם ביחד עם על כל איטרציה יהיה
לפני כן, נגדיר כמרחק הקצר ביותר מ ל בגרף . ונשים לב שבכל איטרצייה באלגוריתם הנ״ך מוצאים מסלול שיפור באורך .
למה 1
תהי זרימה המתקבלת מזרימה על ידי שיפור על גבי מסלול באורך קצר ביותר מ ל ב . אזי
כלומר השיפורים שמבצעים לזרימה אינם לשינוי המרחק הקצר ביותר בין הגרפים.
הוכחה:
נב״ש שקיים ככה ש
כיוון שקיימים כמה כאלה נניח בלי הגבלת הכלליות שבגרף מתקיים ש הוא הקודקוד עם המרחק המינימלי מ שמקיים את הנ״ל כלומר
כלומר אם המרחק הקצר ביותר בין לקודקוד כלשהו ברשת השיורית המשופרת קטן יותר מהמרחק הקצר ביותר בין ל באותה רשת שיורית אז הוא בהכרח קודקוד שעליו מתקיים התנאים שאנחנו רוצים להוכיח.
יהי מסלול קצר ביותר מ ל לאחר שיפור, כלומר ב . ויהי הקודקוד הקודם ל ב . אם כן עבור קודקוד מתקיים
נחלק למקרים
א) אם אז הקשת קיימת גם ב והיא קיימת גם ב כי היא אינה רוויה ולכן
בסתירה להנחה.
ב. אם אז הקשת רוויה ולכן בכלל לא נמצאת ב . כלומר מסלול השיפור שלנו חייב לעבור דרך הקשת כדי שלאחר שיפור הקשת תכנס ל . המשמעות היא כעת ש מקדימה את במסלול השיפור ולכן
בסתירה.
מסקנה
תהי זרימה המתקבלת מזרימה על ידי שיפור על גבי מסלול באורך קצר ביותר מ ל ב . אזי
נרצה להראות שיש קשר בין המסלולים הקצרים ביותר מ ל ב והמסלולים הקצרים ביותר מ ל ב , אם המרחק מ ל לא השתנה.
למה 2
תהי זרימה המתקבלת מזרימה על ידי שיפור על גבי מסלול באורך קצר ביותר מ ל ב אזי אם אז כל מסלול קצר ביותר מ ל בגרף המשופר הוא גם מסלול קצר ביותר מ ל ב (יחס הכלה).
אינטואיצייה- כל איטרצייה על מסלול כלשהי אנחנו מרווים קשת ולכן שיפור של מסלול קצר ביותר חייבת לגרום ל״ניתוק״ של אותו מסלול מ ל . אם כן אם האורך של המסלולים הקצרים ביותר לא השתנה לאחר השיפור המשמעות היחידה שיכולה להיות היא שהיה מסלול אחר בגרף לפני השיפור שפשוט לא תפסנו אותו בריצת BFS..
הוכחה
יהי המסלול הקצר ביותר מ ל ב כך ש , נניח בשלילה כי לא נמצא ב . אזי בהכרח מכיל לפחות קשת חדשה אחת. נסמן קשת זו ונקרא לה קשת חדשה.
הגדרה: קשת תקרא חדשה אם הקשת הייתה ב ומסלול השיפור כלל אותה. נשים לב שקשתות שיכולות להתווסף לגרף רק במצבים שבהם הזרמתי זרם בקשת כלשהי וזה עדיין לא הרווה אותה , במצב זה הקשת המקורית לא אמורה להעלם, או שהרוותי את הקשת ואז היא יורדת והקשת הנגדית לה נכנסת .
כיוון שהמסלול ששיפרנו הוא הקצר ביותר גם כן אז נמצא על המסלול הקצר ביותר הזה ומתקיים .
על פי הטענות הקודמות שהוכחנו אנחנו יודעים ש
כיוון ש מסלול קצר ביותר מ ל ב ואנחנו יודעים ש היא קשת במסלול אזי
בסתירה לנתון.
הבחנה
כאשר זרימה מתקבלת מזרימה על ידי שיפור מסלול הקצר ביותר, אזי מסלול זה לא יכול להיות קיים ב כיוון שלפחות אחת מהקשתות שלו נהיית רוויה.
תובנה זאת תוביל אותנו ללמה הבאה:
תהי רשת זרימה ו זרימה כלשהי. נתבונן באלגוריתם שמשפר על גבי מסלולים מאורך קצר ביותר מ ל רק כל עוד אורכם הוא . אזי, מרגע שקשת נהיית רוויה על ידי האלגוריתם, קשת זאת לא תהיה בשימוש על ידי אף מסלול שיפור אחר בהמשך ריצת האלגוריתם.
הוכחה: נסתכל מה קורה לקשת כאשר הזרימה היא ואנחנו יודעים ש .
ראשית נשים לב שמשמעות הדבר היא שיש מספר מסלולים קצרים ביותר בגרף המקורי וכל פעם אנחנו משפרים אחד מהם. אם הרוונו את הקשת בריצה על המסלול הקצר ביותר הזה, מהגדרת הקיבולת השיורית היא אמורה להיות כעת ולכן לשיפור הבא הקשת הזאת לא תכנס. כמובן שלאחר שיפור כלשהו שקרה לקשת הזאת אז נכנסה כקשת חדשה אבל נשים לב שמהלמה הקודמת אנחנו יודעים שאם אורכי המסלולים הקצרים שווים לאחר שיפור אז כל מסלול קצר ביותר ברשת השיורית המשופרת היא גם מסלול קצר ביותר ברשת השיורית הקודמת לה. כיוון ש נכנסה רק לאחר שיפור כלשהו הרי שבגרף המקורי היא לא הייתה חלק מהמסלולים הקצרים ביותר ולכן גם באיטרציות הבאות לא נשתמש בה .
זמן ריצה
פאזה
נגדיר את הפאזה ה באלגוריתם של אדמונדס קארפ כסדרת האיטרציות שבהן אורך המסלול הקצר ביותר הוא קשתות.
נשים לב שכל מסלול שיפור בפאזה ה ניתן לשייך לקשת אחת לפחות שאותה הוא הפך לרוויה על פי הטענה שהוכחנו למעלה אנחנו יודעים שכל קשת רוויה תשויך למסלול אחד בפאזה ה בלבד, ולכן סה״כ בפאזה ה יכולות להיות לכל היותר איטרציות בכל פאזה.
נשים לב שיכול להיות מקרה שבו המרחק בין ל הקצר ביותר עולה ב אחרי כל שיפור כלומר בהתחלה הוא ואחרי זה עד שמגיע ל המסלול הארוך ביותר בגרף המקורי. כלומר יש לכל היותר מרחקים אפשריים של ו ולכן מספר הפאזות הוא .
סך אם כן מספר האיטרציות הוא מספר הפאזות כפול מספר האיטרציות לפאזה כלומר
בגלל הפעלת BFS בכל איטרציה שלוקחת יתקיים ש זמן הריצה הכולל הוא
אלגוריתם דיניץ
הרעיון המרכזי דיניץ הצליח לבנוח אלגוריתם מהיר יותר של אדמון קארפ עליו דיברנו. הרעיון הבסיסי הוא שכל עוד אורך המסלול הקצר ביותר ברשת השיורית לא השתנתה בין האיטרציות, האלגוריתם יכול למחזר מידע כדי למצוא את מסלול השיפור הקצר ביותר הבא, כלומר ללא BFS.
גרף השכבות
האלגוריתם של דיניץ בונה גרף שכבות בתחילת כל פאזה. בפאזה ה הגרף מכיל שכבות. השכבה ה מכילה את כל הקודקודים כך ש כש הוא הזרימה הנוכחית ו הוא על מסלול קצר ביותר כלשהו מ ל . הקשתות ב הן קשתות שנמצאות על מסלול קצר ביותר כלשהו מ ל ב .
הקודקוד היחיד שאורך המסלול הקצר ביותר מ-s אליו הוא 0 ,הוא רק s. הקודקודים בשכבה
הבאה יהיו הקודקודים שאורך המסלול הקצר ביותר מ-s אליהם הוא 1 ,והם נמצאים על מסלול
קצר ביותר כלשהו מ-s ל-t.
הקודקוד היחיד שיימצא בשכבה האחרונה כמובן הוא שכן מהגדרת פאזה יתקיים . לא יתכנו קודקודים נוספים בשכבה זו שכן אם היה אחד נוסף אז הוא לא אמור להיות חלק מהמסלול הקצר ביותר מ .
בנייה של גרף שכבות לוקחת זמן למשל גרף השכבות עבור הגרף הבא ייראה
מציאת מסלול שיפור קצר ביותר בעזרת גרף השכבות.
לפי הבנייה של כל קשת ב נמצאת על מסלול קצר ביותר מ ל , לכן בחירה של קשת שרירותית בשכבה שעליה אנחנו נמצאים בהכרח תביא אותנו ל לקודקוד בשכבה הבאה כלומר כל פעם נבחר קודקוד עד שנגיע ל וזה יהיה המסלול הקצר ביותר שנעבוד איתו. זה לוקח זמן כלומר כאורך המסלול הקצר שזה בעצם מיוצג על ידי .
find_shortest_path(L, s, t)
u = s
P = {}
while (u != t)
choose arbitrary edge (u,v)
add (u,v) to P
u = v
return P
בעיה
כעת, בנינו את בזמן ומצאנו מסלול קצר ביותר בזמן . אבל אחרי שמצאנו את מסלול, אנחנו יודעים בוודאות שנרווה קשת , כי קשת אחת מוצמדת לריצה אחת של פאזה ונצטרך למחוק אותה מ וכמובן לעדכן את השכבות שכן יכולות להתווסף עוד שכבות. כמו כן בעיה נוספת שעולה היא שקודקוד כלשהו עלול להיות לא רלוונטי לאחר רוויה של קשת , כלומר אי אפשר להגיע ממנו ל .
פתרון:
נשים לב ש צריך להיות מוסר מ אמ״מ דרגת הכניסה או היציאה שלו היא . נקרא לקודקוד זה מבוי סתום.
כלומר באופן אינטואיטיבי אחרי ביצוע שיפור נרצה להוריד את כל הקודקודים שעונים להגדרה של מבוי סתום ביחד עם הקשתות שקשורות אליהם עד שאין עוד מבוי סתום. הפתרון הפשוט ביותר הוא להחזיק שתי מונים לכל קודקודים עבור דרגת כניסה ויציאה וברגע שאלו מתאפסים להוריד את המבוי הסתום ואת הקשתות שקשורות אליו.
נשים לב, יכולים להיות מספר מבויים סתומים שצריך להסיר אחרי איטרצייה בודדת. אבל מהלמה שהוכחנו כל קודקוד יהפוך להיות מבוי סתום לכל היותר פעם אחת בפאזה ולכן הוא גם יימחק מ לכל היותר פעם אחת בפאזה. כנ״ל לגבי הקשתות. אם כן זמן הריצה הכולל לעדכון הוא .
הבחנה
קשת נמחקת לאחר שהיא נהיית רוויה ולכן ההחלטה האם למחוק היא לוקאלית כאשר מוחקים את הקשת צריך רק לבדוק את הקודקודים האלו ולהחליט האם למחוק. כמובן ש רלוונטי רק עבור דרגות היציאה ו רלוונטי רק עבור דרגות הכניסה.. עדיין נצטרך למחוק באופן ריקורסיבי פר פאזה.
האלגוריתם
Dinic(G=(V,E),s,t,c)
init f = 0
Gf = G , cf = c
while exists path P from s to t in Gf:
\\ this will occur each phase change
build L
cfp = min cf(u,v) from all (u,v) in P
where exists a path P from s to t in L:
for each (u,v) in P:
f(u,v) = f(u,v) + cfp
cf(u,v) = cf(u,v) - cfp
f(v,u) = f(v,u) - cfp
cf(v,u) = cf(v,u) + cfp
update(Gf.E)
update L by removing dead ends
נכונות האלגוריתם נובעת מנכונות פורד והתכונות שהוכחנו באדמונד קארפ.
זמן ריצה
בזכות התכונות שהוכחנו אנחנו יודעים שגרף השכבות תמיד יגדל, כלומר מקסימום פאזות.
בניית גרף השכבות לוקחת פר פאזה, נסמן כמספר האיטרציות בפאזה ו זה מספר השכבות כלומר עלות העדכון חסומה במספר השכבות. לכן כל פאזה תעשה עדכונים. אם כן זמן הריצה יהיה
מהתכונות שהוכחנו אנחנו יודעים שקשת אחת מוצמדת לריצה אחת של פאזה ולכן ו חסום באורך המסלול הארוך ביותר האפשרי שהוא וסך הכל זמן הריצה לפאזה הוא
נכפיל במספר הפאזות ונקבל
זיווגים בגרף דו צדדי
נסתכל איך באמצעות רשתות זרימה אפשר לפתור בעייה אחרת שלא הכי אינטואיטיבי להבין איך היא קשורה לרשתות זרימה.
זיווג בגרף גרף לא מכוון הוא תת קבוצה של קשתות הגרף אם היא מקיימת ש כאשר זה מסמן את הדרגה של בגרף המושרה על ידי כלומר .
קודקוד שדרגתו ייקרא מזווג וקודקוד שדרגתו בגרף המושרה ייקרא לא מסווג. ניתן להגדיר את גם כתת קבוצה של הקשתות כך שלאף מהן אין קודקוד משותף.
זיווג מקסימלי
זיווג יהיה מקסימלי אם לא ניתן להוסיף לא קשתות, באופן פורמלי המשמעות היא שלא קיים זיווג כך ש .
הבחנה
זיווג מקסימלי הוא לא בהכרח הזיווג עם מספר הקשתות הגדול ביותר אבל ברור שהזיווג עם מספר הקשתות הגדול ביותר הוא מקסימלי.
זיווג מקסימום
נאמר ש הוא זיווג מקסימום אם לכל זיווג מתקיים .
ניתן לראות משמאל זיווג מקסימום ובאמצע זיווג מקסימלי על הגרף מימין.
מציאת זיווג מקסימלי
מציאת זיווג מקסימלי היא בעיית חמדנית יחסית פשוטה כאשר נוכל לבנות אותה על ידי הבחירה החמדני שאומרת ״הקודקוד הלא מסומן עם מספר השכנים הגדול ביותר יוביל אותי לזיווג מקסימלי״ זה אומנם באופן לא פורמלי, אבל המשמעות היא שלכל גרף נוכל לקחת את הקודקוד עם מספר השכנים הגדול ביותר, לסמן אותו כקודקוד שעברנו עליו , לקחת את הקשת בינו לבין השכן עם מספר השכנים הגדול ביותר , ואת הקשתות לוקחים בשיטת שתי וערב כלומר אם לקחנו קשת אז באיטרציה הבאה לא לוקחים את הקשת אלא רק עוברים לשכן הבא.
גרף דו צדדי
נאמר ש הוא גרף דו צדדי אם ורק אם ניתן לחלק את לשני חלקים : כך שיתקיים וגם וכל הקשתות בגרף יקיימו שצד שמאל של הזוג שייך ל וצד ימין שייך ל . באופן פורמלי
נדון בזיווג מקסימום של גרף דו־צדדי. בעיה זו יכולה להוות מידול לבעיות רבות בעולם האמיתי: החל כמובן מזיווג בין גברים לנשים, עבור בהתאמה בין סטודנטים לרפואה למשרות התמחות בבתי חולים וכלה בכל התאמה בין משימות לבין מכונות או בכל צורך של הקצאות משאבים לפעילויות ועוד.
נראה כי ניתן לפתור את בעיית זיווג מקסימום בגרף הדו צדדי על ידי פתרון זרימת מקסימום בגרף אחר
יהי גרף דו צדדי (זה סימון מקובל לגרף כזה). נבנה רשת זרימה באופן הבא
בעצם חיברנו את קודקוד המקור ברשת הזרימה לכל ואת קודקוד ה״בור״ לכל . לבסוף הקיבולת של כל קשת היא 1. כמובן גם שבמצב זה אנחנו ״מכוונים״ את כי רשת זרימה היא על גרף מכוון.
למה
אם הוא זיווג ב אז קיימת ב זרימה המקיימת .
אם זרימה בערכים שלמים ב אז קיים זיווג ב כך ש
הוכחה
א) נניח הוא זיווג ב - הרעיון הוא להעביר את הזרימה רק דרך הקשתות שבזיווג . אם כן ניקח את הזיווג של ונכוון את הזרימה שם מ ל . כמו כן לכל קודקוד ב שהוא גם ב נעביר על הקשת זרימה וגם באופן דומה לכל קודקוד ב שהוא גם ב נעביר על הקשת זרימה .
נשים לב שנוכל להגדיר את הקודקודים כחתך שזה חתך ואנחנו יודעים ש
כיוון שאנחנו מזרימים רק דרך הקשתות ב וכל קשת מזרימה מהגדרת פונקציית הקיבולת אז אנחנו יודעים:
ב) נניח ש זרימה בערכים שלמים. נסתכל על הזיווג הבא
זה בעצם המקרה הסימטרי למה שעשינו קודם. מגדירים זיווג רק היכן שהזרימה היא בין קשתות גרף הדו צדדי. אם כן
נשים לב ש זה בעצם סכימת הזרימה של כל הקודקודים שיוצאים מ לכל קודקוד אחר. אנחנו יודעים שהזרימה מגיע ל רק מ והיא עוברת ל דרכם. כמו כן מסימטריה יש זרימה נגדית שעוברת מ ל כשכל קודקוד שעוברת בו זרימה מעביר לצד השני ולכן כל זרימה שעוברת ל מתבטלת כאשר סוכמים עם הזרימה הנגדית שחוזרת ל .
אם נמשיך נקבל תחת אותו ההסבר
למה
אם לכל קשת מתקיים שהוא בעל קיבולת טבעית אז קיימת זרימת מקסימום בה כך ש .
הוכחה: זרימת המקסימום שנמצאת ע״י שיטת פורד פלקרסון היא כזאת, וזאת באינדוקציה על האיטרציות, והבחנה שכל מסלול שיפור הוא בערכים שלמים ושסכום של ערכים שלמים הוא ערך שלם. השיטה מזרימה כל פעם לפי הקיבול הקטנה ביותר במסלול שהיא טבעית ולכן גם הזרימה המקסימלית תהיה טבעית והזרימה בין כל זוג קודקודים היא שלמה בגלל הסימטריה.
אם כן האלגוריתם בפסודו קוד :
max-matching(G=(L,R,E))
create G' and c
ford_fulkerson(G',s,t,c)
M = {all (u,v) in LxR where f(u,v)>0 in G'}
return M
זמן הריצה תלוי בהרצה פשוטה של פורד-פולקרסון כלומר שזה ערך זרימת המקסימום בגרף , זאת חסומה , כיוון שהקיבולת היא רק על ידי ולכן סך הכל זמן הריצה הוא .
אלגוריתם הופקרופט-קארפ
אלגוריתם זה מאפשר למצוא זיווג מקסימום בזמן .
האלגוריתם הזה הוא ניתוח הדוק יותר לאלגוריתם של דיניץ על הגרף רשת הזרימה שבנינו מגרף דו״ץ כלשהו.
כיוון שאנחנו נעבוד רק על בהסבר הזה , לשם הנוחות אסמן אותו כ .
שתי תכונות חשובות של שנעזר בהן
א) לכל זוג קודקודים מתקיים ואם אז .
ב) לכל מתקיים או .
הבחנה
לאורך כל הרצת השיטה של פורד לא משנה באיזה מימוש על , כל הרשתות השיוריות מקיימות את התכונות הנ״ל.
כלומר עבור זרימה המתקבלת מ על ידי שיפור. אם מקיים את התכונות הנ״ל אז גם .
ההוכחה מבוססת על כך שסעיף א מתקיים מהגדרת הקיבולת השיורית וגם שכל שיפור שאעשה תמיד יהיה על ידי הזרמת מ למסלול שבחרתי עד שיגיע ל ההזרמה הזאת גורמת לכל המסלול להיות רווי ולכן מה שיקרה כל המסלול ייעלם ויופיעו קשתות חדשות שהן בעצם המסלול לאחר reverse והוא כמובן עדיין מקיים את התכונה הזאת.
למה 1
ברשת זרימה המקיימת את התכונות הנ״ל אם המסלול הקצר ביותר מ ל הוא באורך אזי, ערך זרימת המקסימום הוא לכל היותר
אינטואיצייה על גרף דו צדדי כיוון שמדובר בבניית רשת זרימה לגרף דו״צ ומהגדרת איך שהוא בנוי אנחנו יודעים שערך זרימת המקסימום הוא מספר הקשתות בזיווג מקסימום . במצב הכי אידיאלי יתקיים שכל הקשתות בין ו שייכות לזיווג זה. במצב זה המשמעות היא שמזרימים מ לכל קודקודי ואלו מזרימים את זה לקודקוד המתאים ב ומשם ל . מהגדרת זיווג אנחנו יודעים שכל קשת בין שתי קודקודים היא ייחודית כלומר אם היא קשת במצב הזה אז אין קשת כלומר ערך הזרימה המקסימלי הוא בידיוק מספר הקודקודים ש מזרים אליהם שזה כל כלומר מספר הקודקודים בלי קודקודי חלקי . המסלול הארוך ביותר במצב זה הוא באורך ולכן ערך זרימת המקסימום הוא לכל היותר
למה 2
במהלך הפאזה ה של האלגוריתם של דיניץ על רשת זרימה המקיימת תכונות אלו , קבוצת מסלולי השיפור שנמצאת על ידי האלגוריתם במהלך הפאזה זרה בקודקודי הביניים.
הנכונות נובעת ממה שכבר אמרנו שקורה לאחר שיפור. בגלל שהקיבולת שווה אצל כולם אנחנו נרווה את כל קשתות המסלול ולמעשה כולן ירדו לנו וכל הקודקודים במסלול יהפוך להיות מבוי סתום.
מההבחנות שעשינו נקבל שזמן הריצה לפאזה הוא
חסום בזה בגלל שהמשמעות היא שכל שיפור אנחנו יכולים להצמיד את כל קשתות המסלול שלקחנו שבמקרה הזה חסום ב .
סך הכל לאחר פאזות נקבל זמן ריצה
זה זמן ריצה שלא כזה מעניין אותנו כי קיבלנו אותו על ידי הרצה רגילה של השיטה. ננסה להבין מה אפשר לעשות עם זה.
הבחנה
אם אז ערך זרימת המקסימום עבור רשת המקיימת את הדרוש חסום ב
אם כן נוכל להריץ את האלגוריתם של דיניץ על עד הרגע שהמרחק בין ל גדול מ כלומר על הפאזות הראשונות. כעת יש פאזות ולכן זמן הריצה הכולל לחלק הזה מסתכם בלכל היותר . נוכל להריץ איזה שיטה שנרצה על הרשת השיורית כעת כלומר עבור הפאזות הנותרות בזמן ונקבל אלגוריתם שבחלקו הראשון מריץ דיניץ ובחלקו השני מריץ איזה שיטה שנבחר והזרימה הכוללת תהיה