חגי כהן , hagaycohen2@gmail.com , #####6180
מתחילת המטלה ולאורך העבודה עליה תמיד חזרתי להוראה המרכזית
ליצור מחלקה שמייצגת גרף ולממש אלגוריתמים על הגרפים
התכונות המתארות גרף והמטודות הישירות עליהן יושבות יחד במחלקה "גרף"
במקביל כתבתי מחלקת אלגוריתמים המחזיקה אלגוריתמים ופונקציות עזר המאבחנים את הגרפים מהבטים שונים
לצד מחלקות אלה הוספתי מקרי קצה לקובץ הטסטים כדי לוודא את נכונות האלגוריתמים
אציין שהשתמשתי בקובץ הדמו המצורף רק בכדי לבדוק שהקוד שלי עובד ונתתי את הדגש שלי בעיקר במימושי המחלקות
מבנה המחלקות מפורט בהמשך.
בקובץ זה מוגדר המבנה הבסיסי של גרף במחלקה Graph
וחתימות המטודות שלו,
התכונות מוגדרות פרטיות והן :
מטריצת שכנויות, מספר הקודקודים , מספר הצלעות
vector<vector<int>> matrix;
size_t vertices;
size_t edges;
כל המטודות מוגדרות ציבוריות ונגישות למשתמש והן:
// constructor
Graph();
void loadGraph(vector<vector<int>> graph);
void printGraph();
vector<vector<int>> getMatrix();
size_t getVertices();
על המטרה ואופן המימוש אפרט בחלק השני של הקובץ.
קובץ זה מכיל את מימושי המטודות והבנאי של גרפים
קובץ זה אינו נגיש למשתמש חשוף רק לקובץ Graph.hpp
בנוסף נעשה שימוש גם ב namespace std
בקובץ זה מוגדרים קבועים וכן מוגדרות שתי מחלקות, מחלקת Algorithms
, והמחלקה negativeCycleException
המחלקהAlgorithms
היא אוסף מטודות סטטיות אשר מנתחות גרפים מהבטים שונים ומחזירות מסקנות בהתאם
המחלקה מחולקת לאוסף מטודות סטטיות ציבוריות אשר כל אחת מיצגת "שאלה אחרת על גרף"
static int isConnected(Graph &graph);
static string shortestPath(Graph &graph, size_t src, size_t dest);
static string isContainsCycle(Graph &graph);
static string isBipartite(Graph &graph);
static string negativeCycle(Graph &graph);
ובנוסף אליהן אוסף מטודות סטטיות פרטיות המשמשות עבורן כפונקציות עזר
static vector<vector<int>> DFS(Graph &graph, size_t src);
static vector<int> DFS_visit(Graph &graph, size_t src, vector<int> &visited);
static void DFS_parents_visit(Graph &graph, size_t src, vector<int> &visited, vector<int> &parents);
static vector<int> DFS_backEdge(Graph &graph, size_t src, vector<int> &visited, vector<int> &parents);
static vector<int> BellmanFord(Graph &graph, size_t src);
כאמור מימוש האלגוריתמים ואופן השימוש בכל מתודה יפורט בחלקת השני של קובץ זה.
מחלקה זו מייצגת שגיאה מיוחדת אשר זורקת פונקציית העזר BellmanFord
השגיאה מכילה בתוכה את המעגל השלילי שזוהה.
קובץ זה מכיל תחילה את המימושים של פונקציות העזר מ Algorithms.hpp
לאחר מכל בעזרת פונקציות אלה והמתודות במחלקה Graph
ממומשים כל האלגוריתמים הציבוריים של המחלקה
קובץ בדיקות כפי שניתן עם המטלה שאיליו הוספתי עוד מקרי קצה
הקובץ מחולק לפי סוגי בדיקות בהתאם לאלגוריתמים השונים
המשכתי את הקובץ הקיים כדי "להרוויח" גם את הבדיקות הנתונות בנוסף לבדיקות שלי
עשיתי מספר שינויים בקובץ המייק שמטרתם לפצל בין הרצת תוכנית על אוסף גרפים (במקרה הזה demo)
לבין הרצת בדיקות על האלגוריתמים
פיצול זה נעשה מתוך חשיבה שהמחלקות שלי נועדו לשמש משתמשים אשר ירצו לקבל מסקנות לגבי הגרפים שלהם
ולא נרצה שבכל שימוש במערכת האלגוריתמים יבדקו מחדש אלא נריץ בדיקה רק כשנבצע שינוי כלשהו במימוש
- כל המשקלים של הצלעות הם ערכים שלמים
- מעגל מכיל לפחות שלושה קודקודים
- אין צלעות מקודקוד לעצמו
בנאי יחיד שמאתחל גרף עם 0 צלעות ו 0 קודקודים
מאתחלת מטריצת שכנויות חדשה לגרף ומעדכן בהתאם את מספר הצלעות והקודקודים בגרף
הפונקציה תחזיר שגיאה אם המטריצה לא סימטרית או שהוכנס ערך לא חוקי (למשל, צלע מקודקוד לעצמו)
מציינת את כמות הצלעות והקודקודים בגרף ומדפיסה אותו בייצוג של מטריצת שכנויות
כמצופה מחזירות את מספר הצלעות ומספר הקודקודים
const int INT_MAX = 2147483647;
const int WHITE = 0;
const int GRAY = 1;
const int BLACK = 2;
לצורך נוחות בצביעת קודקודים ואיתחול משקלי מסלולים
פונקציית עזר העוברת על הגרף בשיטת DFS ומחזירה יער DFS (כפי שלמדנו באלגוריתמים 1)
פונקציית עזר הנקראת באופן רקורסיבי ב DFS
פונקציית עזר העוברת על הגרף באותה שיטה כמו DFS
אך מחזירה את רשימת ההורים של כל קודקוד ביער DFS
פונקציית עזר אשר עוברת על הגרף בשיטת DFS ומחזירה צלע מסוג back edge אם יש כזו
פונקציית עזר שבדיוק כמו אלגוריתם בלמן פורד שלמדנו מחזירה מערך של הורים במסלול הקצר ביותר עבור כל קודקוד ל src מסויים.
ההבדל המרכזי הוא שהפונקצייה שלי זורקת שגיאה מיוחדת כאשר מזוהה מעגל שלילי כך שהמעגל השלילי נמצא בשגיאה עצמה
מחלקה המייצגת שגיאה הנזרקת כאשר מזוהה מעגל שלילי באלגוריתם BellmanFord
הבנאי של השגיאה מקבל את מערך ההורים ואת הקודקוד שאליו זוהה relax לאחר N מעברים על כל הצלעות
בהפעלת הבנאי הוא עוקב אחרי מצבעי ההורים ומזהה את המעגל ושומר אותו כתכונה של השגיאה
התכונה מוגדרת public וניתנת לגישה ע"י הפונקציה שתפסה אותה
פונקצייה ציבורית אשר מקבלת גרף ומחזירה האם הוא קשיר
הפונקצייה קוראת ל DFS
ומבצעת קריאה נוספת מהשורש של העץ האחרון ביער
לפי שיטה זו אם יהיה עץ יחיד היער שמתקבל מהקריאה השנייה נדע שהגרף קשיר
שיטה זו מוכחת בקורס אלגוריתמים אחד
פונקצייה ציבורית אשר מחזירה את המסלול הקצר ביותר (הקל ביותר אם הגרף ממושקל) בין שני קודקודים
אם מדובר באותו הקודקוד הפונקצייה תחזיר אותו
אם לא, הפונקצייה תקרא BellmanFord
אם פונקציית העזר תזרוק שגיאה אזי יש מעגל שלילי והפונקצייה תחזיר -1
גם אם אין מעגל שלילי אבל גם אין מסלול הפונקצייה תחזיר -1
במידה ויש מסלול הפונקציה תחלץ אותו ע"י מעבר על התוצאה המתקבלת מפונקציית העזר
הפונקצייה קוראת ל DFS_parents_visit
ושולחת את המערך המתקבל ל - DFS_backEdge
backEdgeפונקציית העזר תחזיר וקטור ריק אם אין מעגל ואם יש היא תחזיר את ה שבעזרתה נשחזר את המעגל ונחזיר אותו
הפונקצייה בודקת אם הגרף הוא 2 צביע על ידי מעבר BFS אם הגרף אכן ניתן לצביעה בשני צבעים הפונקצייה תחזיר את הקבוצות ואם לא הפונקציה תחזיר 0
הפונקצייה תקרא ל BellmanFord
ואם תזרק שגיאה היא תחלץ מתוכה את המעגל השלילי, במידה ואין מעגל שלילי יוחזר 0
בקובץ Test.cpp
מאותחלים גרפים מסוגים שונים המייצגים מקרי קצה ומקים גנרים עבור האלגוריתמים השונים
התוצאה הנוודקת בהשוואה היא התוצאה הצפוייה מהאלגוריתמים כפי שמימשתי אותם
קובץ מייק סטנדרטי כאשר ברירת המחדל שלו היא לקמפל את הפרוייקט ולהריץ את הדמו
בנוסף קיימת הרצת טסטים ע"י הפקודה make run_test
קיימות אפשרויות גם להריץ tidy ןכן גם valgrind
בסוף הקובץ מוגדרת הפעולה clean וגם .PHONY
כדי לקמפל תמיד את הקבצים שנרצה להתעלם מחתימת הזמן שלהם
יש לציין שהוספתי namespace משלי ולכן נעשו שינויים בקובץ כדי שירוץ
לר שיניתי את הטעויות בתיעוד שהוזכרו בפורום כי את הבדיקות שלי עשיתי מול test
ולא מול demo