מכללת אורט כפר-סבא מבני נתונים ויעילות אלגוריתמים מחסנית ()Stack כתיב תוכי ( ,)infixתחילי ( ,)prefixסופי ()postfix משפט לפתרון נוסחאות נסיגה 05.11.14 אורי וולטמן [email protected] חידה לחימום נתון לוח שחמט ועליו צריח ( ,)rookהמונח על משבצת כלשהי. כזכור ,צריח יכול לנוע מספר משבצות כרצונו לאורך עמודה או לרוחב שורה. שני שחקנים משחקים משחק של הזזת צריח .הם מזיזים ,כל אחד בתורו ,את הצריח כמה משבצות שהם רוצים ,בכיוונים 'למטה' ו'-שמאלה' בלבד .כלומר, לא ניתן להזיז את הצריח ימינה או למעלה. כל שחקן חייב להזיז את הצריח בתורו. השחקן המנצח הוא זה שמביא את הצריח למשבצת השמאלית-התחתונה של הלוח. פתחו אלגוריתם המקבל כקלט שני מספרים טבעיים המציינים את מיקומו ההתחלתי של הצריח .האלגוריתם יבחר האם להיות השחקן הפותח או השני ,והוא ישחק כך שהוא ינצח תמיד. מחסנית מחסנית ( )Stackהיא סוג של טיפוס נתונים ( )data typeהתומך בפעולות הכנסה והוצאה ,כך שמתקיימת התכונה הבאה: הוצאת ערך ממחסנית אפשרית רק כאשר היא אינה ריקה ,והיא מחזירה תמיד את הערך שהוכנס אחרון ,מבין הערכים הקיימים במחסנית. לדוגמא: הכנס 9 הכנס 5 הכנס 3 הוצא הוצא 3 ► ראש המחסנית 5 ► ראש המחסנית 9 ► ראש המחסנית מחסנית מחסנית הינה טיפוס נתונים המאפשר הוצאת והכנסת נתונים אך ורק מצידה האחד .לפתח זה נקרא 'ראש המחסנית'. ניתן לגשת רק לאיבר הנמצא בראש המחסנית ,והוא מסתיר את כל האיברים שמתחתיו. אם נדחוף למחסנית איבר ומיד לאחר מכן נבצע שליפה ,אז יתקיים ש: האיבר שנשלוף יהיה אותו איבר שזה עתה דחפנו. מצב המחסנית לאחר השליפה יהיה זהה למצבה לפני הדחיפה. המבנה המיוחד של המחסנית גורם לכך שבמהלך הכנסה והוצאה של איברים למחסנית מתקיימת בה התכונה הנקראת ,Last In First Out (LIFO) :כלומר – האיבר שנכנס אחרון הוא היוצא ראשון. מחסנית אילו פעולות נגדיר על מחסנית? כדי שאפשר יהיה להוסיף איברים למחסנית ולהוציא אותם ממנה ,נגדיר את הפעולות דחוף-למחסנית ו-שלוף-ממחסנית. המחסנית עשויה להיות ריקה ,אך אז אסור לבצע עליה פעולת שליפה .לכן ,נגדיר פעולה בשם מחסנית-ריקה? ,המחזירה 'אמת' אם המחסנית ריקה ,ו'-שקר' אם יש במחסנית איברים. לעיתים נרצה להציץ לראש המחסנית ,כדי לדעת את ערכו של האיבר שנמצא שם ,אך מבלי לשלוף אותו .איך אפשר לעשות זאת? בפעולה זו נשתמש לפני שנשלוף מהמחסנית ,כדי למנוע מצב שבו אנחנו מנסים לשלוף איבר ממחסנית שהיא ריקה. דרך אחת היא לבצע פעולת שליפה ,ואז לדחוף בחזרה למחסנית את האיבר ששלפנו. אפשרות אחרת ,היא להגדיר את הפעולה הצץ-למחסנית שמחזירה לנו את האיבר שבראש המחסנית ,אך מבלי לשלוף אותו (מבלי להוציא אותו החוצה). לבסוף ,נגדיר פעולה בשם אתחל-מחסנית ,שיוצרת מחסנית ריקה. מחסנית הממשק של טיפוס הנתונים מחסנית: מחסנית תארו את תכולת המחסניות S1ו S2-במהלך סדרת הפעולות הבאה: אתחל-מחסנית S1 דחוף-למחסנית ()S1,7 דחוף-למחסנית ( )S1,9 אתחל-מחסנית S2 שלוף-ממחסנית (i )S1 דחוף-למחסנית ()S2,i דחוף-למחסנית ()S1,6 שלוף-ממחסנית (i )S2 שלוף-ממחסנית (i )S1 דחוף-למחסנית ()S1,8 מחסנית תארו את תכולת המחסניות S1ו S2-במהלך סדרת הפעולות הבאה: אתחל-מחסנית S2 אתחל-מחסנית S1 דחוף-למחסנית (’)S1,’a דחוף-למחסנית (’)S1,’b דחוף-למחסנית (’)S2,’c שלוף-ממחסנית (ch )S1 דחוף-למחסנית ()S2,ch שלוף-ממחסנית (ch )S1 אם לא מחסנית-ריקה? ( )S1אזי הצג כפלט הצץ-למחסנית ()S1 אם לא מחסנית-ריקה? ( )S2אזי הצג כפלט הצץ-למחסנית ()S2 Undo דוגמא לשימוש במחסנית ,ניתן למצוא בתוכנות עריכה גראפיות ,שמאפשרות למעצב לבטל את הפעולה האחרונה (.)Undo פעולת Undoמבטלת את הפעולה האחרונה שבוצעה (פרט לפעולת Undo עצמה) .המשתמש יכול לחזור ולהפעיל פעולה זו ללא הגבלה עד לביטול הפעולה הראשונה שעשה. איך נממש פעולה זו? בכל פעם שהמשתמש יבצע איזושהי פעולת עיצוב (למשל, ציור קו או נקודה) ,נשמור את פרטי הפעולה (למשל :מהו צבע הנקודה ,מאיפה לאיפה מותחים את הקו ,וכו'). את התיאורים הללו יש לשמור כך שניתן יהיה לגשת אליהם בסדר הפוך לסדר בו הפעולות התבצעו. נשתמש במחסנית :בכל פעם שמתבצעת פעולה (פרט לפעולת Undoעצמה), נדחוף למחסנית את תיאורה ואת הפרמטרים שלה .כאשר המשתמש ירצה לבטל פעולה ,נשלוף את הפעולה שבראש המחסנית ,ונבצע פעולה המבטלת אותה. Undo כיצד תיראה המחסנית במהלך ביצוע סדרת הפעולות הבאה: צייר-קו ()... צייר-נקודה ()... צייר-קו ()... Undo צייר-מצולע ()... Undo Undo 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.1.1הצג כפלט" :אין פעולות בזיכרון" ( )2.3.2אחרת: ( )2.3.2.1שלוף-ממחסנית (y )S ( )2.3.2.2בצע את הפעולה המבטלת את y מחסנית זמן ריצה דוגמא נוספת לשימוש במחסנית ,זהו רכיב הקיים במערכת ההפעלה ,הנקרא מחסנית זמן הריצה ( ,)run time stackאו מחסנית הקריאות (.)call stack בכל פעם שמזמנים פונקציה ,במהלך ריצת התוכנית ,מערכת ההפעלה דוחפת לתוך מחסנית זמן הריצה נתונים שישמשו אותה כדי לדעת לאיזו נקודה בתכנית עליה לחזור ,לאחר שהפונקציה תסיים את ריצתה .נתונים אלו כוללים את הפרמטרים שאיתם זימנו את הפונקציה ,וכן את הכתובת אליה יש לחזור בסופה. לאחר סיום ביצוע הפונקציה ,שולפים מהמחסנית את הרשומה שבראש המחסנית ,וביצוע התכנית ממשיך לפי הנתונים המופיעים שם. אם מחסנית הקריאות מתמלאת ,בעקבות קינון עמוק מדי של פונקציה-בתוך-פונקציה, מתרחשת שגיאה הנקראת 'גלישת מחסנית' ( ,)Stack overflowהמביאה בדרך כלל לשגיאת זמן ריצה ,ולסיום התכנית. שגיאה כזו מתרחשת ,למשל ,אם כותבים פונקציה רקורסיבית ,המזמנת את עצמה שוב ושוב ,ללא תנאי עצירה. בדיקת תקינות סוגריים נגדיר את המושג 'ביטוי חשבוני תקין מבחינת סוגריים': זהו ביטוי שיכול להכיל סוגריים במספר לא מוגבל ,ובלבד שיהיו מאוזנים. איזון הסוגריים מחייב שמספר הפותחים והסוגרים יהיה שווה בדיוק. לדוגמא ,הביטויים האלה תקינים: ))((a )( b + a – 2 * 7 ( + 32 * ( 37 * ) / ( 5 + 1 ) ) – 4 נשים לב כי הביטוי האחרון תקין מבחינת הסוגריים ,על אף שכביטוי חשבוני הוא אינו תקין. ואילו הביטויים האלה אינם תקינים: a+((c )((x+y z+)t פתחו אלגוריתם המקבל כקלט ביטוי חשבוני ,ומחזיר 'אמת' אם הוא תקין מבחינת סוגריים ,ו'-שקר' אם לא .נסו לפתור את הבעיה ללא שימוש במחסנית... בדיקת תקינות סוגריים נכתוב את האלגוריתם הבא ,הפותר את הבעיה ,תוך שימוש במונה: counter 0 כל עוד יש עדיין תווים בקלט ,בצע: קרא תו chמהקלט אם chשווה לתו ’(‘ ,אזי: הגדל ב 1-את counter אם chשווה לתו ’)‘ ,אזי: הקטן ב 1-את counter אם ,counter < 0אזי: החזר 'שקר' אם ,counter > 0אזי: החזר 'שקר' אחרת: החזר 'אמת' בדיקת תקינות סוגריים כעת ,נגדיר מחדש את המושג 'ביטוי חשבוני תקין מבחינת סוגריים' ,כך שיכלול גם ביטויים המכילים סוגריים מסוגים שונים: כעת הביטוי יכול להכיל ,בנוסף לסוגריים רגילים ,גם סוגריים מסולסלים ,מרובעים, וכו' ,במספר לא מוגבל ,ובלבד שיהיו מאוזנים. איזון הסוגריים מחייב שמספר הפותחים והסוגרים יהיה שווה בדיוק ,וכן שכנגד כל פותח יימצא הסוגר המתאים מאותו סוג במקום המתאים. לדוגמא ,הביטויים האלה תקינים: ))((a ][b+a–2*7 { + 32 * ( 37 * ) / [ 5 + 1 ] } – 4 נשים לב כי הביטוי האחרון תקין מבחינת הסוגריים ,על אף שכביטוי חשבוני הוא אינו תקין. ואילו הביטויים האלה אינם תקינים: a+((c ]([3+a)+4 ][ ) ( 5 – 3 ] * [ 2 – 3 • האם לדעתכם ניתן יהיה להשתמש באותו הרעיון האלגוריתמי כמקודם? • ואם נגדיר כמה מונים? בדיקת תקינות סוגריים כדי לפתור את הבעיה נשתמש במחסנית ,ונסרוק את הביטוי משמאל לימין. כיוון שאנו בודקים את תקינות הביטוי רק מבחינת הסוגריים ,נתעלם מכל התווים שאינם סוגריים. בזמן סריקת הביטוי ,נדחוף למחסנית כל פותח שניתקל בו. כשניתקל בסוגר ,אז נשלוף מהמחסנית את הפותח הנמצא בראשה ,ונשווה ביניהם .אם הם לא מאותו סוג – אז הביטוי אינו תקין. ניסיון לשלוף פותח ממחסנית ריקה מציין גם הוא שהביטוי שאינו תקין ,שכן אז קיימים יותר סוגרים מפותחים. אם הגענו לסוף הקלט והמחסנית טרם התרוקנה ,סימן שהיו פותחים שאין להם סוגרים ,ואז הביטוי אינו תקין .אם לעומת זאת המחסנית ריקה בסוף הקלט ,אז הביטוי תקין. בדיקת תקינות סוגריים נכתוב את האלגוריתם: אתחל-מחסנית S כל עוד יש עדיין תווים בקלט ,בצע: קרא תו chמהקלט אם chהוא פותח ,אזי: דחוף-למחסנית ()S,ch אם chהוא סוגר ,בצע: אם מחסנית-ריקה? ( , )Sאזי: החזר 'שקר' אחרת: שלוף-ממחסנית (old_ch )S אם chו old_ch-אינם מתאימים ,אזי: החזר 'שקר' אם לא מחסנית-ריקה? ( , )Sאזי: החזר 'שקר' אחרת: החזר 'אמת' בדיקת תקינות סוגריים )}5+([3+2]/{4-1 בדיקת תקינות סוגריים )}5+([3+2]/{4-1 ( בדיקת תקינות סוגריים )}5+([3+2]/{4-1 [ ( בדיקת תקינות סוגריים )}5+([3+2]/{4-1 [ ( בדיקת תקינות סוגריים )}5+([3+2]/{4-1 { ( בדיקת תקינות סוגריים )}5+([3+2]/{4-1 { ( בדיקת תקינות סוגריים )}5+([3+2]/{4-1 ( בדיקת תקינות סוגריים )}5+([3+2]/{4-1 הביטוי תקין בדיקת תקינות סוגריים )(2+{5*2))+(3/5 בדיקת תקינות סוגריים )(2+{5*2))+(3/5 ( בדיקת תקינות סוגריים )(2+{5*2))+(3/5 { ( בדיקת תקינות סוגריים )(2+{5*2))+(3/5 { ( אין התאמה בין הסוגר הנוכחי לבין הפותח שבראש המחסנית הביטוי אינו תקין! בדיקת תקינות סוגריים ( ( 2 + 5) + 3 בדיקת תקינות סוגריים ( ( 2 + 5) + 3 ( בדיקת תקינות סוגריים ( ( 2 + 5) + 3 ( ( בדיקת תקינות סוגריים ( ( 2 + 5) + 3 ( ( בדיקת תקינות סוגריים ( ( 2 + 5) + 3 ( המחסנית אינה ריקה בסיום המעבר על הביטוי הביטוי אינו תקין! מחסנית במהלך לימודינו הכרנו כל מיני טיפוסי נתונים ( :)data typesמספר שלם (,)int מספר ממשי ( ,)floatתו ( ,)charוכו'. גם מחסנית ( ,)stackכפי שהגדרנו אותה ,היא סוג של טיפוס נתונים ,אם כי להבדיל מטיפוסי נתונים מוחשיים ,הקיימים בשפת Cואנו יודעים כיצד הם ממומשים בזיכרון המחשב ,כגון ,int, double, longוכו' ,הרי שהמחסנית היא טיפוס נתונים מופשט (טנ"מ) ,או באנגלית .Abstract Data Type (ADT) - מה הכוונה? מחסנית היא טיפוס נתונים לכל דבר :אפשר להגדיר משתנה מטיפוס מחסנית ,לכתוב פונקציה שמקבלת מחסנית כפרמטר ,להגדיר מערך של מחסניות ,לכתוב פונקציה שמחזירה מחסנית ,להגדיר משתנה שהוא מצביע למחסנית ,וכו'. מצד שני ,מחסנית היא טיפוס נתונים מופשט ,במובן זה שאנחנו יכולים לעשות את כל הדברים המפורטים מעלה ,מבלי לדעת כיצד היא ממומשת בזיכרון המחשב. כל העבודה שלנו עם משתנה מטיפוס מחסנית נעשתה דרך פעולות ממשק ( ,)interfaceמבלי שהתעניינו כלל בשאלה כיצד נעשה בפועל המימוש ( .)implementationהפרדה זו בין ממשק למימוש ,וההסתרה של פרטי המימוש מהמתכנת ,היא מהמאפיינים של טנ"מ (.)ADT מחסנית איך תיעשה ההפרדה בין ממשק למימוש בסביבת העבודה של ?C נבנה בעצמנו יחידת ספרייה ,stack.hשתכלול את הכותרות של כל הפונקציות הפועלות על מחסנית (דחיפה ,שליפה ,בדיקה האם ריק ,איתחול ,הצצה). נכתוב בקובץ stack.cמימוש לכל הפעולות השונות על מחסנית. כשמתכנת אחר ירצה להשתמש במחסנית ,הוא יצרף את יחידת הספרייה שלנו באמצעות ההוראה ” ,#include “stack.hויזמן את הפונקציות הכלולות בה ,מבלי לדעת כיצד הן מומשו. במהלך הקורס ,נממש את טיפוס הנתונים המופשט 'מחסנית' בצורות שונות: באמצעות מערך סטטי באמצעות מערך דינאמי באמצעות רשימה מקושרת וכו' האם המתכנת שישתמש ביחידת הספרייה stack.hיהיה מודע למבנה הנתונים בו אנחנו משתמשים מאחורי הקלעים? לא ,ובכך תישמר ההפרדה בין ממשק למימוש. מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית Sלא ריקה ,ומחזיר את הערך הגדול ביותר הנמצא בה .הניחו ש S-מועבר כפרמטר לפי ערך. מצא-מקסימום ()S אתחל-מחסנית S1 שלוף-ממחסנית (max )S דחוף-למחסנית ()S1,max כל עוד לא מחסנית-ריקה? ( ,)Sבצע: שלוף-ממחסנית (x )S דחוף-למחסנית ()S1,x אם ,x > maxאזי: max x החזר max האם הכרחי היה להשתמש במחסנית עזר? מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית Sלא ריקה, ומחזיר מחסנית המכילה את אותם האיברים ,אך בסדר הפוך. הפוך-מחסנית ()S אתחל-מחסנית S1 כל עוד לא מחסנית-ריקה? ( ,)Sבצע: שלוף-ממחסנית (x )S דחוף-למחסנית ()S1,x החזר S1 במקום שתי הוראות אלו ,אפשר היה לרשום: דחוף-למחסנית (שלוף-ממחסנית ()S1,)S מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית Sלא ריקה ,אשר אין בה איבר המופיע יותר מפעם אחת .האלגוריתם יחזיר מחסנית חדשה ,שזהה למחסנית ,Sפרט לכך שהערך הגדול ביותר מבין איברי המחסנית S נמצא בראש המחסנית החדשה. הגדול-בראש ()S אתחל-מחסנית S1 מצא-מקסימום (max )S כל עוד לא מחסנית-ריקה? ( ,)Sבצע: שלוף-ממחסנית (x )S אם ,x < maxאזי: דחוף-למחסנית ()S1,x הפוך-מחסנית (S1 )S1 דחוף-למחסנית ()S1,max החזר S1 מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית Sלא ריקה .האלגוריתם יחזיר מחסנית חדשה ,שזהה למחסנית ,Sפרט לכך שהערך הגדול ביותר מבין איברי המחסנית Sנמצא בראש המחסנית החדשה .במידה והוא מופיע יותר מפעם אחת ,יש להעביר לראש המחסנית את כל הערכים האלה. הגדולים-בראש ()S אתחל-מחסנית S1 אתחל-מחסנית S2 מצא-מקסימום (max )S כל עוד לא מחסנית-ריקה? ( ,)Sבצע: שלוף-ממחסנית (x )S אם ,x < maxאזי: דחוף-למחסנית ()S1,x אחרת: דחוף-למחסנית ()S2,x הפוך-מחסנית (S1 )S1 כל עוד לא מחסנית-ריקה? ( ,)S2בצע: דחוף-למחסנית (שלוף-ממחסנית ()S1,)S2 החזר S1 האם היה הכרחי להשתמש בשתי מחסניות לצורך פתרון הבעיה? מחסנית פתחו אלגוריתם אשר מקבל כקלט מחסנית Sלא ריקה .האלגוריתם יחזיר מחסנית חדשה ,שזהה למחסנית ,Sפרט לכך שהערך הגדול ביותר מבין איברי המחסנית Sנמצא בראש המחסנית החדשה .במידה והוא מופיע יותר מפעם אחת ,יש להעביר לראש המחסנית את כל הערכים האלה. הגדולים-בראש ()S אתחל-מחסנית S1 count_max 0 מצא-מקסימום (max )S כל עוד לא מחסנית-ריקה? ( ,)Sבצע: שלוף-ממחסנית (x )S אם ,x < maxאזי: דחוף-למחסנית ()S1,x אחרת: הגדל ב 1-את count_max הפוך-מחסנית (S1 )S1 בצע count_maxפעמים: דחוף-למחסנית ()S1,max החזר S1 כתיב תוכי ()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שאינן מחייבות שימוש בסוגריים. כתיב תחילי ()prefix ב 1920-המציא המתמטיקאי הפולני Jan Łukasiewiczצורת כתיב של ביטויים אלגבריים ולוגיים ,שבה האופרטור מופיע לפני האופרנדים ,במקום בין שני האופרנדים כמקובל. צורת כתיבה זו נקראת כתיב תחילי ( ,)prefix notationוביטויים המופיעים בייצוג זה נקראים ביטויים תחיליים ( .)prefix expressionsלדוגמא: נשים לב שבדוגמא השלישית ,בכתיב תוכי היה צורך להשתמש בסוגריים על מנת לדעת מהי הפעולה המתבצעת קודם (לא ניתן היה להשמיט את הסוגריים ,שכן לכפל קדימות יותר גבוהה מאשר לחיבור) ,ואילו בכתיב התחילי לא היה צורך בסוגריים כלל .זהו אחד המאפיינים של צורת ייצוג זו. בשפת התכנות אסמבלי ,הביטויים כתובים בכתיב תחילי: הביטוי התוכי 2 + 3ייכתב בכתיב תחילי בתור . + 2 3 הביטוי התוכי ) A + (B * Cייכתב בכתיב תחילי בתור . + A * B C הביטוי התוכי (A + B) * Cייכתב בכתיב תחילי בתור .* + A B C ADD AX,BX SUB AX, BX וכו' יש המכנים צורת כתיבה זו בשם כתיב פולני ( ,)Polish notationלזכר Łukasiewiczהפולני ,שהכניס לראשונה את הייצוג הזה לשימוש. כתיב סופי ()postfix צורת כתיבה אחרת היא זו שבה האופרטור מופיע אחרי שני האופרנדים .צורת כתיבה זו נקראת כתיב סופי ( ,)postfix notationוביטויים המופיעים בייצוג זה נקראים ביטויים סופיים ( .)postfix expressionsלדוגמא: הביטוי התוכי 2 + 3ייכתב בכתיב סופי בתור . 2 3 + הביטוי התוכי ) A + (B * Cייכתב בכתיב סופי בתור . A B C * + הביטוי התוכי (A + B) * Cייכתב בכתיב סופי בתור * . A B + C גם בצורת כתיבה זו אין צורך להשתמש בסוגריים כלל. יש המכנים צורת זו בשם כתיב פולני הפוך (.)Reversed Polish notation בשנות ה '70-וה '80-היו מחשבוני כיס מסוימים שעבדו בשיטה זו (כלומר ,קודם היה צריך להקליד את האופרנדים ,ורק אח"כ את האופרטור). כתיב תחילי ,תוכי ,סופי המירו את הביטויים הבאים מייצוג תוכי לייצוג סופי ולייצוג תחילי: ייצוג תוכי 7–2 A+B–C )(A + B) * (C – D (A – B) * C + D ייצוג סופי –72 –AB+C *–AB+CD AB–C*D+ ניתן לכתוב אלגוריתמים המקבלים ביטוי באחד משולשת הייצוגים ,וממירים אותו לשני הייצוגים האחרים. ייצוג תחילי –72 – +ABC *+AB–CD +*–ABCD חישוב ביטוי בייצוג סופי כעת נכיר אלגוריתם המקבלים ביטוי בייצוג סופי ,המכיל אופרטורים ומספרים, ומחשב את ערכו ,תוך שימוש במחסנית: בכל פעם שאנו קוראים אופרנד ,נדחוף אותו למחסנית. לכן כאשר נגיע לאופרטור ,נדע כי האופרנדים שלו (שעליהם הוא פועל) הינם שני האיברים העליונים במחסנית. אנו יכולים לשלוף את שני האיברים האלה ולבצע עליהם את הפעולה המצוינת על-ידי האופרטור. את התוצאה נדחוף למחסנית ,כדי שתהיה שם כאופרנד בשביל האופרטור הבא. חישוב ביטוי בייצוג סופי נכתוב את האלגוריתם: אתחל-מחסנית S כל עוד יש תווים בקלט ,בצע: קרא את תו הקלט הבא והשם אותו בsymbol- אם symbolהוא אופרנד ,אזי: דחוף-למחסנית )(S,symbol אחרת: שלוף-ממחסנית )second_operand (S שלוף-ממחסנית )first_operand (S הפעל את האופרטור symbolעל first_operandו- ,second_operandוהשם את התוצאה בvalue- דחוף-למחסנית )(S,value החזר את שלוף-ממחסנית )(S אם נסמן ב n-את אורך מחרוזת הקלט ,מהו סדר הגודל (חסם אסימפטוטי) של סיבוכיות זמן הריצה ,כפונקציה של ?n עקבו אחר ריצת האלגוריתם עבור הביטוי הבא ,הנתון בייצוג סופי: 623+–382/+*2*3+ נוסחאות נסיגה ראינו שאפשר לתאר את זמן הריצה של אלגוריתמים רקורסיביים באמצעות נוסחת נסיגה ,אשר מביעה את זמן הריצה של הבעיה בעבור קלט בגודל .n נוכחנו בכך כשפיתחנו את באלגוריתם הרקורסיבי לפתרון בעיית מגדלי האנוי ,וכן כשפיתחנו את האלגוריתם למיון-מיזוג. בשני המקרים ראינו כיצד לבנות נוסחה רקורסיבית המתארת את מספר הצעדים המתבצע ,ועברנו מכלל הנסיגה אל ביטוי סגור, בדרכים שונות: שיטת ההצבה :ניחוש הפתרון והוכחת נכונותו על-ידי אינדוקציה מתמטית. שיטת האיטרציה :פיתוח חוזר-ונשנה של הנוסחה הרקורסיבית ,עד שמגיעים לתנאי העצירה ,ואז פישוט הביטוי המתקבל. נכיר כעת מספר משפטים אשר יסייעו לנו לפתור משפחות של נוסחאות נסיגה. נוסחאות נסיגה משפט :1נתונים a,b,cקבועים לא-שליליים ,המקיימים a>=1ו .b>1-הפתרון לנוסחת הנסיגה: T(n) = a · T(n/b) + cn הוא: אם a < b אם a = b אם a > b )T(n) = Θ(n )T(n) = Θ(nlogn )T(n) = Θ(nlogba נוסחאות נסיגה למשל ,נוסחת הנסיגה של מיון-מיזוג היא: )T(n) = 2T(n/2) + Θ(n נוסחה זו מתאימה למשפט ,1עבור הקבועים b = 2 ,a = 2 ו c-כלשהו. הפתרון ,מכיוון ש a = b-הוא ) ,Θ(nlognוזו אכן סיבוכיות זמן הריצה של אלגוריתם למיון-מיזוג. תרגיל מהו סדר הגודל של נוסחת הנסיגה: T(n) = 3T(n/2) + n נוסחה זו מתאימה למשפט ,1עבור הקבועים , b = 2 ,a = 3 .c = 1הפתרון ,מכיוון ש ,a > b-הוא ) ,Θ(nlog23כלומר, בערך ).Θ(n1.58 תרגיל חשבו את סדר הגודל של נוסחת הנסיגה הבאה: T(n) = 5T(n/5) + 15n נוסחה זו מתאימה למשפט ,1עבור הקבועים b = 5 ,a = 5 ו .c=15-הפתרון ,מכיוון ש ,a = b-הוא ).T(n)= Θ(nlogn תרגיל חשבו את סדר הגודל של נוסחת הנסיגה הבאה: T(n) = T(n/2) + n נוסחה זו מתאימה למשפט ,1 עבור הקבועים b = 2 ,a = 1 ו.c = 1- הפתרון ,מכיוון ש,a < b- הוא ).T(n) = Θ(n נסו להגיע לפתרון זה על-ידי שימוש בשיטת האיטרציה.
© Copyright 2025