Professional Documents
Culture Documents
צלע ,ו־ אם לא .בגרף לא מכוון המטריצה הזו היא כמובן סימטרית ,כי
][3
ייצוג של גרף באמצעות רשימת שכנו ּיוֹ ת
רשימות מקושרות ,כאשר כל היא רשימה של רשימת שכנויות של גרף
רשימה מקושרת מתארת את השכנים של קדקוד )בגרף מכוון :כל הקודקודים שיש
אליהם ,קרי רק הצלעות היוצאות מ־ ( ואורכה הוא ,על־כן, צלע מהקודקוד
. בגרף מכוון( .גודל הייצוג הוא )או
][4
[5]
[6]
תכונות והגדרות שימושיות מדיסקרטית
דרגה של קדקוד היא מספר השכנים שלו,
∈
∈ ∈
][8
,קיים מסלול מ־ ל־ גרף ייקרא קשיר חזק אם לכל שני קדקודים שונים
וגם ההיפך.
אם הוא גרף בעצמו )כלומר ייקרא תת־גרף של
. .מסמנים: וכן ( וגם מתקיים
קשיר חזק והוא מקסימלי ייקרא רכיב קשירות )חזקה( אם תת־גרף
,הגרף בתכונה זו ,כלומר לכל קדקוד
][9
גרפים ממושקלים
פונקציית גרף ו־ כך ש־ גרף ממושקל הוא שלשה
משקל על הצלעות .לעיתים קרובות נניח ש־ נותנת משקלים חיוביים בלבד )אבל
לא תמיד נוכל להניח כך; חלק מהאלגוריתמים ידעו לטפל בצלעות עם משקל שלילי,
וחלק לא(.
, ,או באופן כללי של קבוצת צלעות מגדירים גם משקל של מסלול,
כסכום המשקלים:
−
+
=
∈
][10
אלגוריתם ה־BFS
נכתוב אלגוריתם חדש ואיטרטיבי שעובר על כל צלעות גרף .הוא אלגוריתם סורק
שמחפש לרוחב הגרף )כלומר ,סורק אותו לפי מרחק הולך וגדל מקדקוד הבסיס(.
בתרגיל ראיתם מימוש חלופי ל־ DFSבלי רקורסיה ,עם שימוש במחסנית ,ונשאלתם
מה היה קורה אילו היה משתמש בתור ) (Queueולא במחסנית .ובכן ,האלגוריתם
,BFSכפי שניתן היה לחשוד ,משתמש בתור .להלן פסודו־קוד:
][11
def BFS(G, start_vert, ): # start_vert V
Q = new queue
for v in V:
dist[v] = # mark as unvisited. No known path to it yet!
dist[start_vert] = 0
Q.enqueue(start_vert)
while Q is not empty:
u = Q.dequeue()
for every w so that (u, w) in E:
if dist[w] == : # if it is unvisited
[12]
שרטוט : .דוגמה לריצת ה־ ,BFSמשמאל למעלה לימין למטה .האלגוריתם מתחיל מהקדקוד 𝑠 ומחפש אחר
קדקוד המטרה ,Ѯהאות הקירילית העתיקה .Ksiבאדום :המרחק לכל קדקוד בכל איטרציה .נשים לב שלתור
מכניסים איברים משמאל ומוציאם מימין )וכן לפי סדר נומרי( .קדקודים שבוקרו מסומנים בכחול.
][13
הוכחת הנכונות של BFS
) מקצר טענה : .האלגוריתם BFSמבקר בכל הצמתים ברכיב הקשירות של
.(start_vert
.נראה מסלול מ־ ל־ : הוכחת טענה : .יהא
באינדוקציה על שהאלגוריתם מבקר ב־ .
(. הטענה ברורה )נובע ישירות מהשורה בסיס האינדוקציה :עבור
לא מבוקר ,ומאחר שאנו צעד האינדוקציה :אילו ביקרנו ב־ ,סיימנו .אחרת,
מהנחת האינדוקציה ,נקבל שהאלגוריתם יבצע את השורה − מבקרים ב־
,כלומר יבקר ב־ . −
][15
,ומכאן שהמרחק של הקדקוד של שכן זה מורה על האינדוקציה ,שדה ה־
יירשם כ־ .
? חלק זה נובע ישירות מהנחת האינדוקציה עבור מדוע אחרי כל הצמתים במרחק
. ,מאחר שאין לו שכנים ממרחק של פחות מ־
ייכנס על־ידי צומת מדוע לפני כל הצמתים במרחק גדול מ־ ? כי צומת במרחק
.כלומר, בלבד ,שנמצא תמיד בתור אחרי כל הצמתים במרחק במרחק
מהנחת האינדוקציה ,לא נטפל בקדקוד ממרחק לפני כל הקדקודים ממרחק
,ואלה יכניסו לתור את כל הצמתים ממרחק .
[18]
[19]
אלגוריתם ה־DFS
ננסה כעת לכתוב אלגוריתם שייבחן אם גרף הוא קשיר .נתחיל בגרפים לא מכוונים.
האלגוריתם הראשון שנעסוק בו הוא .(Depth First Search) DFSנגדיר את הפונקציה
:Explore
def Explore(v):
visited[v] = 1
)for all w so that (v, w E:
if visited[w] == 0:
)Explore(w
טענה Explore : .מגיעה לכל הקודקודים בגרף אליהם ניתן להגיע מ־ )תקף גם
למסלול מכוון(.
][20
.אם הוכחת טענה : .נניח שקיים מסלול בגרף
, + במסלול שלא המשיך ל־ Exploreלא הגיעה ל־ ,אז קיים קדקוד
. בסתירה לכך ש־ Exploreסוקרת את כל שכני
נכליל את האלגוריתם Exploreלאלגוריתם האב :DFS
def DFS(G):
for all v V:
visited[v] = 0
for all v V:
if visited[v] == 0:
)explore(v
][21
שימוש למציאת רכיבי קשירות:DFSאלגוריתם ה־
def Explore2(v):
visited[v] = 1
previsit(v)
for all w so that (v, w) E:
if visited[w] == 0:
Explore2(w)
postvisit(v)
def DFS2(G):
for all v in V:
visited[v] = 0
[22]
for all v in V:
if visited[v] == 0:
CC_num += 1
Explore2(v)
def previsit(v):
CC[v] = CC_num
[23]
def postvisit(v): # For now, it does nothing.
return
def CC_main_algorithm(G):
CC_num = 0
)DFS2(G
,ועוברים על כל מה הסיבוכיות? הוא עובר על כל הקדקודים ולכן היא לפחות
הצלעות לפחות פעם אחת )בגרף לא מכוון – בדיוק פעם אחת( ,אז הסיבוכיות היא
. ,ו־ ,כאשר
][24
מיון טופולוגי
נניח שיש לנו רשימה של משימות לבצע ,כך שיש בין חלקן תלות :נניח ,עלינו למלא
טופס לבקשת דרכון ,אך לשם כך עלינו לגשת לדואר ולקחת חבילה ,ולשם ביצוע
משימה זו עלינו לחדש את הדרכון ,אך לשם כך עלינו להצטלם ,ולשם כך עלינו
להסתפר ,ולשם כך עלינו להוציא תו ירוק ,אולם לשם כך עלינו להצטלם ,וכן
הלאה 1.נרצה לתת הגדרה עבור סידור שיאפשר לנו לבצע את המשימה )אם יש כזה(.
][25
שרטוט : .גרף מכוון שניתן להגדיר
עליו מיון טופולוגי.
הערה" :כל-הנחלים הולכים אל-הים,
שרטוט : .גרף מכוון שלא ניתן להגדיר
והים איננו מלא; אל-מקום,
עליו מיון טופולוגי
שהנחלים הולכים--שם הם שבים,
ללכת".
][26
אולם בגרף .לא ניתן להגדיר מיון טופולוגי ,שכן צריך ניסיון מעשי כדי להתקבל
לעבודה ,אבל צריך לעבוד כדי לצבור ניסיון מעשי ,ולכן תואר במדמ"ח אינו באמת
שימושי.
נבצע שינוי באלגוריתם :נגדיר משתנה חדש ,clock ,שבאמצעותו אנו ממספרים את
הכניסות והיציאות לכל קדקוד .אנו שומרים ומעדכנים את ערך המשתנה ,clock
כך שהוא ממלא מעין פונקציה הקוראת ל"שעה" הנוכחית ,כלומר ממספרת את
הדברים לפי סדר התרחשותם .הפונקציות המעודכנות previsit ,ו־
postvisitנראות כך:
def previsit(v):
pre[v] = clock
clock += 1
][27
def postvisit(v):
post[v] = clock
clock += 1
][28
תרגיל
][30
[31]
[32]
[33]
[34]
כשנריץ BFSעל הגרף מהקודקוד ,Sקודם כל נכניס לתור את הקודקודים . c,aנניח בלי
הגבלת הכלליות שהכנסנו את aקודם .לכן ,גם נוציא את aמהתור קודם ,ואז נמשיך
][35
בתהליך . BFSכעת a ,יסומן בתור הקודם של bושל ) dהרי אפשר להגיע לשניהם
ממנו בצעד אחד( – אך זה לא המצב בתת־גרף המתואר .לכן הוא לא יכול להיות תת־
גרף הקודמים מריצת BFSמהקודקוד .sעם זאת כל המסלולים בו הם אכן מסלולים
מאורך המסלול הקצר ביותר בגרף המקורי.
][36
[37]