Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

מבני נתונים ויעילות אלגוריתמים

Παρόμοιες παρουσιάσεις


Παρουσίαση με θέμα: "מבני נתונים ויעילות אלגוריתמים"— Μεταγράφημα παρουσίασης:

1 מבני נתונים ויעילות אלגוריתמים
מכללת אורט כפר-סבא מבני נתונים ויעילות אלגוריתמים מחסנית (Stack) כתיב תוכי (infix), תחילי (prefix), סופי (postfix) משפט לפתרון נוסחאות נסיגה אורי וולטמן

2 חידה לחימום נתון לוח שחמט ועליו צריח (rook), המונח על משבצת כלשהי.
כזכור, צריח יכול לנוע מספר משבצות כרצונו לאורך עמודה או לרוחב שורה. שני שחקנים משחקים משחק של הזזת צריח. הם מזיזים, כל אחד בתורו, את הצריח כמה משבצות שהם רוצים, בכיוונים 'למטה' ו-'שמאלה' בלבד. כלומר, לא ניתן להזיז את הצריח ימינה או למעלה כל שחקן חייב להזיז את הצריח בתורו. השחקן המנצח הוא זה שמביא את הצריח למשבצת השמאלית-התחתונה של הלוח. פתחו אלגוריתם המקבל כקלט שני מספרים טבעיים המציינים את מיקומו ההתחלתי של הצריח. האלגוריתם יבחר האם להיות השחקן הפותח או השני, והוא ישחק כך שהוא ינצח תמיד.

3 מחסנית מחסנית (Stack) היא סוג של טיפוס נתונים (data type) התומך בפעולות הכנסה והוצאה, כך שמתקיימת התכונה הבאה: הוצאת ערך ממחסנית אפשרית רק כאשר היא אינה ריקה, והיא מחזירה תמיד את הערך שהוכנס אחרון, מבין הערכים הקיימים במחסנית. לדוגמא: הכנס 9 הכנס 5 הכנס 3 הוצא 3 ► ראש המחסנית 5 ► ראש המחסנית 9 ► ראש המחסנית

4 מחסנית מחסנית הינה טיפוס נתונים המאפשר הוצאת והכנסת נתונים אך ורק מצידה האחד. לפתח זה נקרא 'ראש המחסנית'. ניתן לגשת רק לאיבר הנמצא בראש המחסנית, והוא מסתיר את כל האיברים שמתחתיו. אם נדחוף למחסנית איבר ומיד לאחר מכן נבצע שליפה, אז יתקיים ש: האיבר שנשלוף יהיה אותו איבר שזה עתה דחפנו. מצב המחסנית לאחר השליפה יהיה זהה למצבה לפני הדחיפה. המבנה המיוחד של המחסנית גורם לכך שבמהלך הכנסה והוצאה של איברים למחסנית מתקיימת בה התכונה הנקראת: Last In First Out (LIFO), כלומר – האיבר שנכנס אחרון הוא היוצא ראשון.

5 מחסנית אילו פעולות נגדיר על מחסנית?
כדי שאפשר יהיה להוסיף איברים למחסנית ולהוציא אותם ממנה, נגדיר את הפעולות דחוף-למחסנית ו-שלוף-ממחסנית. המחסנית עשויה להיות ריקה, אך אז אסור לבצע עליה פעולת שליפה. לכן, נגדיר פעולה בשם מחסנית-ריקה?, המחזירה 'אמת' אם המחסנית ריקה, ו-'שקר' אם יש במחסנית איברים. בפעולה זו נשתמש לפני שנשלוף מהמחסנית, כדי למנוע מצב שבו אנחנו מנסים לשלוף איבר ממחסנית שהיא ריקה. לעיתים נרצה להציץ לראש המחסנית, כדי לדעת את ערכו של האיבר שנמצא שם, אך מבלי לשלוף אותו. איך אפשר לעשות זאת? דרך אחת היא לבצע פעולת שליפה, ואז לדחוף בחזרה למחסנית את האיבר ששלפנו. אפשרות אחרת, היא להגדיר את הפעולה הצץ-למחסנית שמחזירה לנו את האיבר שבראש המחסנית, אך מבלי לשלוף אותו (מבלי להוציא אותו החוצה). לבסוף, נגדיר פעולה בשם אתחל-מחסנית, שיוצרת מחסנית ריקה.

6 מחסנית הממשק של טיפוס הנתונים מחסנית:

7 מחסנית תארו את תכולת המחסניות S1 ו-S2 במהלך סדרת הפעולות הבאה:
שלוף-ממחסנית (S1)  i דחוף-למחסנית (S2,i) דחוף-למחסנית (S1,6) שלוף-ממחסנית (S2)  i דחוף-למחסנית (S1,8)

8 מחסנית תארו את תכולת המחסניות S1 ו-S2 במהלך סדרת הפעולות הבאה:
דחוף-למחסנית (S1,’a’) דחוף-למחסנית (S1,’b’) דחוף-למחסנית (S2,’c’) שלוף-ממחסנית (S1)  ch דחוף-למחסנית (S2,ch) אם לא מחסנית-ריקה? (S1) אזי הצג כפלט הצץ-למחסנית (S1) אם לא מחסנית-ריקה? (S2) אזי הצג כפלט הצץ-למחסנית (S2)

9 Undo דוגמא לשימוש במחסנית, ניתן למצוא בתוכנות עריכה גראפיות, שמאפשרות למעצב לבטל את הפעולה האחרונה (Undo). פעולת Undo מבטלת את הפעולה האחרונה שבוצעה (פרט לפעולת Undo עצמה). המשתמש יכול לחזור ולהפעיל פעולה זו ללא הגבלה עד לביטול הפעולה הראשונה שעשה. איך נממש פעולה זו? בכל פעם שהמשתמש יבצע איזושהי פעולת עיצוב (למשל, ציור קו או נקודה), נשמור את פרטי הפעולה (למשל: מהו צבע הנקודה, מאיפה לאיפה מותחים את הקו, וכו'). את התיאורים הללו יש לשמור כך שניתן יהיה לגשת אליהם בסדר הפוך לסדר בו הפעולות התבצעו. נשתמש במחסנית: בכל פעם שמתבצעת פעולה (פרט לפעולת Undo עצמה), נדחוף למחסנית את תיאורה ואת הפרמטרים שלה. כאשר המשתמש ירצה לבטל פעולה, נשלוף את הפעולה שבראש המחסנית, ונבצע פעולה המבטלת אותה.

10 Undo כיצד תיראה המחסנית במהלך ביצוע סדרת הפעולות הבאה: צייר-קו (...)
צייר-נקודה (...) Undo צייר-מצולע (...)

11 Undo נכתוב את האלגוריתם הפותר את הבעיה: (1) אתחל-מחסנית  S
(2) כל עוד המשתמש מבצע פעולות, בצע: (2.1) קרא פעולה x (2.2) אם x איננה הפעולה Undo, אזי: (2.2.1) דחוף-למחסנית (S,x) (2.2.2) בצע את הפעולה x (2.3) אחרת: (2.3.1) אם מחסנית-ריקה? (S) אזי: ( ) הצג כפלט: "אין פעולות בזיכרון" (2.3.2) אחרת: ( ) שלוף-ממחסנית (S)  y ( ) בצע את הפעולה המבטלת את y

12 מחסנית זמן ריצה דוגמא נוספת לשימוש במחסנית, זהו רכיב הקיים במערכת ההפעלה, הנקרא מחסנית זמן הריצה (run time stack), או מחסנית הקריאות (call stack). בכל פעם שמזמנים פונקציה, במהלך ריצת התוכנית, מערכת ההפעלה דוחפת לתוך מחסנית זמן הריצה נתונים שישמשו אותה כדי לדעת לאיזו נקודה בתכנית עליה לחזור, לאחר שהפונקציה תסיים את ריצתה. נתונים אלו כוללים את הפרמטרים שאיתם זימנו את הפונקציה, וכן את הכתובת אליה יש לחזור בסופה. לאחר סיום ביצוע הפונקציה, שולפים מהמחסנית את הרשומה שבראש המחסנית, וביצוע התכנית ממשיך לפי הנתונים המופיעים שם. אם מחסנית הקריאות מתמלאת, בעקבות קינון עמוק מדי של פונקציה-בתוך-פונקציה, מתרחשת שגיאה הנקראת 'גלישת מחסנית' (Stack overflow), המביאה בדרך כלל לשגיאת זמן ריצה, ולסיום התכנית. שגיאה כזו מתרחשת, למשל, אם כותבים פונקציה רקורסיבית, המזמנת את עצמה שוב ושוב, ללא תנאי עצירה.

13 בדיקת תקינות סוגריים נגדיר את המושג 'ביטוי חשבוני תקין מבחינת סוגריים': זהו ביטוי שיכול להכיל סוגריים במספר לא מוגבל, ובלבד שיהיו מאוזנים. איזון הסוגריים מחייב שמספר הפותחים והסוגרים יהיה שווה בדיוק. לדוגמא, הביטויים האלה תקינים: ( ( a ) ) ( b + a – 2 * 7) ( + 32 * ( 37 * ) / ( ) ) – 4 נשים לב כי הביטוי האחרון תקין מבחינת הסוגריים, על אף שכביטוי חשבוני הוא אינו תקין. ואילו הביטויים האלה אינם תקינים: a + ( ( c ( ( x + y ) z + ) t פתחו אלגוריתם המקבל כקלט ביטוי חשבוני, ומחזיר 'אמת' אם הוא תקין מבחינת סוגריים, ו-'שקר' אם לא. נסו לפתור את הבעיה ללא שימוש במחסנית...

14 בדיקת תקינות סוגריים נכתוב את האלגוריתם הבא, הפותר את הבעיה, תוך שימוש במונה: 0 counter  כל עוד יש עדיין תווים בקלט, בצע: קרא תו ch מהקלט אם ch שווה לתו ‘(’ , אזי: הגדל ב-1 את counter אם ch שווה לתו ‘)’ , אזי: הקטן ב-1 את counter אם counter < 0, אזי: החזר 'שקר' אם counter > 0, אזי: אחרת: החזר 'אמת'

15 בדיקת תקינות סוגריים כעת, נגדיר מחדש את המושג 'ביטוי חשבוני תקין מבחינת סוגריים', כך שיכלול גם ביטויים המכילים סוגריים מסוגים שונים: כעת הביטוי יכול להכיל, בנוסף לסוגריים רגילים, גם סוגריים מסולסלים, מרובעים, וכו', במספר לא מוגבל, ובלבד שיהיו מאוזנים. איזון הסוגריים מחייב שמספר הפותחים והסוגרים יהיה שווה בדיוק, וכן שכנגד כל פותח יימצא הסוגר המתאים מאותו סוג במקום המתאים. לדוגמא, הביטויים האלה תקינים: ( ( a ) ) [ b + a – 2 * 7 ] { + 32 * ( 37 * ) / [ ] } – 4 נשים לב כי הביטוי האחרון תקין מבחינת הסוגריים, על אף שכביטוי חשבוני הוא אינו תקין. ואילו הביטויים האלה אינם תקינים: a + ( ( c ( [ 3 + a ) + 4 ] [ ) ( 5 – 3 ] * [ 2 – 3] האם לדעתכם ניתן יהיה להשתמש באותו הרעיון האלגוריתמי כמקודם? ואם נגדיר כמה מונים?

16 בדיקת תקינות סוגריים כדי לפתור את הבעיה נשתמש במחסנית, ונסרוק את הביטוי משמאל לימין. כיוון שאנו בודקים את תקינות הביטוי רק מבחינת הסוגריים, נתעלם מכל התווים שאינם סוגריים. בזמן סריקת הביטוי, נדחוף למחסנית כל פותח שניתקל בו. כשניתקל בסוגר, אז נשלוף מהמחסנית את הפותח הנמצא בראשה, ונשווה ביניהם. אם הם לא מאותו סוג – אז הביטוי אינו תקין. ניסיון לשלוף פותח ממחסנית ריקה מציין גם הוא שהביטוי שאינו תקין, שכן אז קיימים יותר סוגרים מפותחים. אם הגענו לסוף הקלט והמחסנית טרם התרוקנה, סימן שהיו פותחים שאין להם סוגרים, ואז הביטוי אינו תקין. אם לעומת זאת המחסנית ריקה בסוף הקלט, אז הביטוי תקין.

17 בדיקת תקינות סוגריים נכתוב את האלגוריתם:
אתחל-מחסנית  S כל עוד יש עדיין תווים בקלט, בצע: קרא תו ch מהקלט אם ch הוא פותח, אזי: דחוף-למחסנית (S,ch) אם ch הוא סוגר, בצע: אם מחסנית-ריקה? (S) , אזי: החזר 'שקר' אחרת: שלוף-ממחסנית (S)  old_ch אם ch ו-old_ch אינם מתאימים, אזי: אם לא מחסנית-ריקה? (S) , אזי: החזר 'אמת'

18 בדיקת תקינות סוגריים 5 + ( [ ] / { } )

19 בדיקת תקינות סוגריים 5 + ( [ ] / { } ) (

20 בדיקת תקינות סוגריים 5 + ( [ ] / { } ) [ (

21 בדיקת תקינות סוגריים 5 + ( [ ] / { } ) [ (

22 בדיקת תקינות סוגריים 5 + ( [ ] / { } ) { (

23 בדיקת תקינות סוגריים 5 + ( [ ] / { } ) { (

24 בדיקת תקינות סוגריים 5 + ( [ ] / { } ) (

25 בדיקת תקינות סוגריים 5 + ( [ ] / { } ) הביטוי תקין

26 בדיקת תקינות סוגריים ( 2 + { 5 * 2 ) ) + ( 3 / 5 )

27 בדיקת תקינות סוגריים ( 2 + { 5 * 2 ) ) + ( 3 / 5 ) (

28 בדיקת תקינות סוגריים ( 2 + { 5 * 2 ) ) + ( 3 / 5 ) { (

29 בדיקת תקינות סוגריים ( 2 + { 5 * 2 ) ) + ( 3 / 5 ) {
אין התאמה בין הסוגר הנוכחי לבין הפותח שבראש המחסנית ( !הביטוי אינו תקין

30 בדיקת תקינות סוגריים ( ( 2 + 5) + 3

31 בדיקת תקינות סוגריים ( ( 2 + 5) + 3 (

32 בדיקת תקינות סוגריים ( ( 2 + 5) + 3 ( (

33 בדיקת תקינות סוגריים ( ( 2 + 5) + 3 ( (

34 המחסנית אינה ריקה בסיום המעבר על הביטוי
בדיקת תקינות סוגריים ( ( 2 + 5) + 3 המחסנית אינה ריקה בסיום המעבר על הביטוי ( !הביטוי אינו תקין

35 מחסנית במהלך לימודינו הכרנו כל מיני טיפוסי נתונים (data types): מספר שלם (int), מספר ממשי (float), תו (char), וכו'. גם מחסנית (stack), כפי שהגדרנו אותה, היא סוג של טיפוס נתונים, אם כי להבדיל מטיפוסי נתונים מוחשיים, הקיימים בשפת C ואנו יודעים כיצד הם ממומשים בזיכרון המחשב, כגון int, double, long, וכו', הרי שהמחסנית היא טיפוס נתונים מופשט (טנ"מ), או באנגלית - Abstract Data Type (ADT). מה הכוונה? מחסנית היא טיפוס נתונים לכל דבר: אפשר להגדיר משתנה מטיפוס מחסנית, לכתוב פונקציה שמקבלת מחסנית כפרמטר, להגדיר מערך של מחסניות, לכתוב פונקציה שמחזירה מחסנית, להגדיר משתנה שהוא מצביע למחסנית, וכו'. מצד שני, מחסנית היא טיפוס נתונים מופשט, במובן זה שאנחנו יכולים לעשות את כל הדברים המפורטים מעלה, מבלי לדעת כיצד היא ממומשת בזיכרון המחשב. כל העבודה שלנו עם משתנה מטיפוס מחסנית נעשתה דרך פעולות ממשק (interface), מבלי שהתעניינו כלל בשאלה כיצד נעשה בפועל המימוש (implementation). הפרדה זו בין ממשק למימוש, וההסתרה של פרטי המימוש מהמתכנת, היא מהמאפיינים של טנ"מ (ADT).

36 מחסנית איך תיעשה ההפרדה בין ממשק למימוש בסביבת העבודה של C?
נבנה בעצמנו יחידת ספרייה stack.h, שתכלול את הכותרות של כל הפונקציות הפועלות על מחסנית (דחיפה, שליפה, בדיקה האם ריק, איתחול, הצצה). נכתוב בקובץ stack.c מימוש לכל הפעולות השונות על מחסנית. כשמתכנת אחר ירצה להשתמש במחסנית, הוא יצרף את יחידת הספרייה שלנו באמצעות ההוראה #include “stack.h”, ויזמן את הפונקציות הכלולות בה, מבלי לדעת כיצד הן מומשו. במהלך הקורס, נממש את טיפוס הנתונים המופשט 'מחסנית' בצורות שונות: באמצעות מערך סטטי באמצעות מערך דינאמי באמצעות רשימה מקושרת וכו' האם המתכנת שישתמש ביחידת הספרייה stack.h יהיה מודע למבנה הנתונים בו אנחנו משתמשים מאחורי הקלעים? לא, ובכך תישמר ההפרדה בין ממשק למימוש.

37 מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית S לא ריקה, ומחזיר את הערך הגדול ביותר הנמצא בה. הניחו ש-S מועבר כפרמטר לפי ערך. מצא-מקסימום (S) אתחל-מחסנית  S1 שלוף-ממחסנית (S)  max דחוף-למחסנית (S1,max) כל עוד לא מחסנית-ריקה? (S), בצע: שלוף-ממחסנית (S)  x דחוף-למחסנית (S1,x) אם x > max, אזי: x  max החזר max האם הכרחי היה להשתמש במחסנית עזר?

38 מחסנית דחוף-למחסנית (שלוף-ממחסנית (S)S1,)
שלוף-ממחסנית (S)  x דחוף-למחסנית (S1,x) החזר S1 במקום שתי הוראות אלו, אפשר היה לרשום: דחוף-למחסנית (שלוף-ממחסנית (S)S1,)

39 מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית S לא ריקה, אשר אין בה איבר המופיע יותר מפעם אחת. האלגוריתם יחזיר מחסנית חדשה, שזהה למחסנית S, פרט לכך שהערך הגדול ביותר מבין איברי המחסנית S נמצא בראש המחסנית החדשה. הגדול-בראש (S) אתחל-מחסנית  S1 מצא-מקסימום (S)  max כל עוד לא מחסנית-ריקה? (S), בצע: שלוף-ממחסנית (S)  x אם x < max, אזי: דחוף-למחסנית (S1,x) הפוך-מחסנית (S1)  S1 דחוף-למחסנית (S1,max) החזר S1

40 מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית S לא ריקה. האלגוריתם יחזיר מחסנית חדשה, שזהה למחסנית S, פרט לכך שהערך הגדול ביותר מבין איברי המחסנית S נמצא בראש המחסנית החדשה. במידה והוא מופיע יותר מפעם אחת, יש להעביר לראש המחסנית את כל הערכים האלה. הגדולים-בראש (S) אתחל-מחסנית  S1 אתחל-מחסנית  S2 מצא-מקסימום (S)  max כל עוד לא מחסנית-ריקה? (S), בצע: שלוף-ממחסנית (S)  x אם x < max, אזי: דחוף-למחסנית (S1,x) אחרת: דחוף-למחסנית (S2,x) הפוך-מחסנית (S1)  S1 כל עוד לא מחסנית-ריקה? (S2), בצע: דחוף-למחסנית (שלוף-ממחסנית (S2)S1,) החזר S1 האם היה הכרחי להשתמש בשתי מחסניות לצורך פתרון הבעיה?

41 מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית S לא ריקה. האלגוריתם יחזיר מחסנית חדשה, שזהה למחסנית S, פרט לכך שהערך הגדול ביותר מבין איברי המחסנית S נמצא בראש המחסנית החדשה. במידה והוא מופיע יותר מפעם אחת, יש להעביר לראש המחסנית את כל הערכים האלה. הגדולים-בראש (S) אתחל-מחסנית  S1 count_max  0 מצא-מקסימום (S)  max כל עוד לא מחסנית-ריקה? (S), בצע: שלוף-ממחסנית (S)  x אם x < max, אזי: דחוף-למחסנית (S1,x) אחרת: הגדל ב-1 את count_max הפוך-מחסנית (S1)  S1 בצע count_max פעמים: דחוף-למחסנית (S1,max) החזר S1

42 כתיב תוכי (infix) כאשר אנחנו כותבים ביטויים חשבוניים, אנחנו בדרך כלל נוהגים לכתוב את האופרטור (operator) בין שני האופרנדים (operands). לדוגמא: בביטוי 2 + 3, האופרטור '+' נמצא בין שני האופרנדים '2' ו-'3'. בביטוי A + (B * C), האופרטור '*' נמצא בין שני האופרנדים 'B' ו-'C', והאופרטור '+' נמצא בין שני האופרנדים ‘A’ ו-'(B * C)'. בביטוי (A + B) * C, האופרטור '+' נמצא בין שני האופרנדים 'A' ו-'B', והאופרטור '*' נמצא בין שני האופרנדים 'C' ו-'(A + B)'. צורת כתיבה כזו נקראת כתיב תוכי (infix notation), כיוון שהאופרטור נמצא "בתוך" הביטוי, בין שני האופרנדים. לביטויים הכתובים בייצוג תוכי קוראים ביטויים תוכיים (infix experssions). אנחנו רגילים להשתמש בכתיב תוכי כשאנחנו כותבים ביטויים מתמטיים, אבל יש לצורת כתיבה זו חולשה בולטת – היא דורשת שימוש בסוגריים כדי לכפות סדר של ביצוע פעולות בתוך הביטוי. נכיר כעת שתי צורות כתיב אחרות - כתיב תחילי (prefix notation) וכתיב סופי (postfix notation) - שאינן מחייבות שימוש בסוגריים.

43 כתיב תחילי (prefix) ב-1920 המציא המתמטיקאי הפולני Jan Łukasiewicz צורת כתיב של ביטויים אלגבריים ולוגיים, שבה האופרטור מופיע לפני האופרנדים, במקום בין שני האופרנדים כמקובל. צורת כתיבה זו נקראת כתיב תחילי (prefix notation), וביטויים המופיעים בייצוג זה נקראים ביטויים תחיליים (prefix expressions). לדוגמא: הביטוי התוכי ייכתב בכתיב תחילי בתור הביטוי התוכי A + (B * C) ייכתב בכתיב תחילי בתור + A * B C . הביטוי התוכי (A + B) * C ייכתב בכתיב תחילי בתור * + A B C. נשים לב שבדוגמא השלישית, בכתיב תוכי היה צורך להשתמש בסוגריים על מנת לדעת מהי הפעולה המתבצעת קודם (לא ניתן היה להשמיט את הסוגריים, שכן לכפל קדימות יותר גבוהה מאשר לחיבור), ואילו בכתיב התחילי לא היה צורך בסוגריים כלל. זהו אחד המאפיינים של צורת ייצוג זו. בשפת התכנות אסמבלי, הביטויים כתובים בכתיב תחילי: ADD AX,BX SUB AX, BX וכו' יש המכנים צורת כתיבה זו בשם כתיב פולני (Polish notation), לזכר Łukasiewicz הפולני, שהכניס לראשונה את הייצוג הזה לשימוש.

44 כתיב סופי (postfix) צורת כתיבה אחרת היא זו שבה האופרטור מופיע אחרי שני האופרנדים. צורת כתיבה זו נקראת כתיב סופי (postfix notation), וביטויים המופיעים בייצוג זה נקראים ביטויים סופיים (postfix expressions). לדוגמא: הביטוי התוכי ייכתב בכתיב סופי בתור הביטוי התוכי A + (B * C) ייכתב בכתיב סופי בתור A B C * + . הביטוי התוכי (A + B) * C ייכתב בכתיב סופי בתור A B + C *. גם בצורת כתיבה זו אין צורך להשתמש בסוגריים כלל. יש המכנים צורת זו בשם כתיב פולני הפוך (Reversed Polish notation). בשנות ה-70' וה-80' היו מחשבוני כיס מסוימים שעבדו בשיטה זו (כלומר, קודם היה צריך להקליד את האופרנדים, ורק אח"כ את האופרטור).

45 כתיב תחילי, תוכי, סופי המירו את הביטויים הבאים מייצוג תוכי לייצוג סופי ולייצוג תחילי: ייצוג תוכי ייצוג סופי ייצוג תחילי 7 – 2 A + B – C (A + B) * (C – D) (A – B) * C + D ניתן לכתוב אלגוריתמים המקבלים ביטוי באחד משולשת הייצוגים, וממירים אותו לשני הייצוגים האחרים. – 7 2 – + A B C * + A B – C D + * – A B C D 7 2 – A B + C – A B + C D – * A B – C * D +

46 חישוב ביטוי בייצוג סופי
כעת נכיר אלגוריתם המקבלים ביטוי בייצוג סופי, המכיל אופרטורים ומספרים, ומחשב את ערכו, תוך שימוש במחסנית: בכל פעם שאנו קוראים אופרנד, נדחוף אותו למחסנית. לכן כאשר נגיע לאופרטור, נדע כי האופרנדים שלו (שעליהם הוא פועל) הינם שני האיברים העליונים במחסנית. אנו יכולים לשלוף את שני האיברים האלה ולבצע עליהם את הפעולה המצוינת על-ידי האופרטור. את התוצאה נדחוף למחסנית, כדי שתהיה שם כאופרנד בשביל האופרטור הבא.

47 חישוב ביטוי בייצוג סופי
נכתוב את האלגוריתם: אתחל-מחסנית  S כל עוד יש תווים בקלט, בצע: קרא את תו הקלט הבא והשם אותו ב-symbol אם symbol הוא אופרנד, אזי: דחוף-למחסנית (S,symbol) אחרת: שלוף-ממחסנית (S)  second_operand שלוף-ממחסנית (S)  first_operand הפעל את האופרטור symbol על first_operand ו- second_operand, והשם את התוצאה ב-value דחוף-למחסנית (S,value) החזר את שלוף-ממחסנית (S) אם נסמן ב-n את אורך מחרוזת הקלט, מהו סדר הגודל (חסם אסימפטוטי) של סיבוכיות זמן הריצה, כפונקציה של n? עקבו אחר ריצת האלגוריתם עבור הביטוי הבא, הנתון בייצוג סופי: – / + * 2 * 3 +

48 נוסחאות נסיגה ראינו שאפשר לתאר את זמן הריצה של אלגוריתמים רקורסיביים באמצעות נוסחת נסיגה, אשר מביעה את זמן הריצה של הבעיה בעבור קלט בגודל n. נוכחנו בכך כשפיתחנו את באלגוריתם הרקורסיבי לפתרון בעיית מגדלי האנוי, וכן כשפיתחנו את האלגוריתם למיון-מיזוג. בשני המקרים ראינו כיצד לבנות נוסחה רקורסיבית המתארת את מספר הצעדים המתבצע, ועברנו מכלל הנסיגה אל ביטוי סגור, בדרכים שונות: שיטת ההצבה: ניחוש הפתרון והוכחת נכונותו על-ידי אינדוקציה מתמטית. שיטת האיטרציה: פיתוח חוזר-ונשנה של הנוסחה הרקורסיבית, עד שמגיעים לתנאי העצירה, ואז פישוט הביטוי המתקבל. נכיר כעת מספר משפטים אשר יסייעו לנו לפתור משפחות של נוסחאות נסיגה.

49 נוסחאות נסיגה משפט 1: נתונים a,b,c קבועים לא-שליליים, המקיימים a>=1 ו-b>1. הפתרון לנוסחת הנסיגה: T(n) = a · T(n/b) + cn הוא: T(n) = Θ(n) a < b אם T(n) = Θ(nlogn) a = b אם T(n) = Θ(nlogba) a > b אם

50 נוסחאות נסיגה למשל, נוסחת הנסיגה של מיון-מיזוג היא:
T(n) = 2T(n/2) + Θ(n) נוסחה זו מתאימה למשפט 1, עבור הקבועים a = 2, b = 2 ו-c כלשהו. הפתרון, מכיוון ש-a = b הוא Θ(nlogn), וזו אכן סיבוכיות זמן הריצה של אלגוריתם למיון-מיזוג.

51 תרגיל מהו סדר הגודל של נוסחת הנסיגה: T(n) = 3T(n/2) + n
נוסחה זו מתאימה למשפט 1, עבור הקבועים a = 3, b = 2 , c = 1. הפתרון, מכיוון ש-a > b, הוא Θ(nlog23), כלומר, בערך Θ(n1.58).

52 תרגיל חשבו את סדר הגודל של נוסחת הנסיגה הבאה: T(n) = 5T(n/5) + 15n
נוסחה זו מתאימה למשפט 1, עבור הקבועים a = 5, b = 5 ו-c=15. הפתרון, מכיוון ש-a = b, הוא T(n)= Θ(nlogn).

53 תרגיל חשבו את סדר הגודל של נוסחת הנסיגה הבאה: T(n) = T(n/2) + n
נוסחה זו מתאימה למשפט 1, עבור הקבועים a = 1, b = ו-c = 1. הפתרון, מכיוון ש-a < b, הוא T(n) = Θ(n). נסו להגיע לפתרון זה על-ידי שימוש בשיטת האיטרציה.


Κατέβασμα ppt "מבני נתונים ויעילות אלגוריתמים"

Παρόμοιες παρουσιάσεις


Διαφημίσεις Google