מבוא למדעי המחשב שיעור 5: רקורסיה מעט מהשיעור מתבסס על פרקים 4.2 ו- 4.3 בספר: J.V. Guttag, Introduction to Computation and Programming Using Python, MIT Press, 2013. לקריאה נוספת: Leonardo Pisano (Fibonacci), Il Liber Abbaci,1202. Parmanand Singh, the so-called Fibonacci numbers in ancient and medieval India, Historia Mathematica, 12:229-244, 1985. Sarina Singh et al., India travel guide 15th ed., Lonely Planet, 2013.
דוגמה תזכורת: אלגוריתם אוקלידס למציאת מחלק משותף גדול ביותר מחליף את החישוב של GCD(m,n) בחישוב של.GCD(n,m%n) נתרגם זאת מילולית לקוד: זה יעבוד?
קוד מתוקן אם נמחק את ה-# משורת ההדפסה, מה תדפיס 45 27 הקריאה?gcd(45,27) 27 18 18 9 9 0 זו ההדפסה של ערך הביטוי gcd(45,27) 9
עוד דוגמה print(x, y) >>> fast_power(2,10) 2 10 4 5 16 2 256 1 65536 0 1024.0
רקורסיה הגדרה מילולית (מוויקיפדיה): תופעה שכל מופע שלה מכיל מופע נוסף שלה, כך שהיא מתרחשת ומשתקפת בתוך עצמה שוב ושוב. דוגמה: רקורסיה וויזואלית כמו זו שנקראת אפקט דרוסטה. ברקורסיה סופית (או עוצרת), מעבר לרמה מסויימת אין רקורסיה. ברקורסיה אינסופית, כל רמה מכילה רמות משנה מאותו סוג.
תכנות רקורסיבי פונקציה שהמימוש שלה מכיל קריאה לעצמה נקראת פונקציה רקורסיבית. קריאה לפונקציה מתוך עצמה נקראת קריאה רקורסיבית. על מנת שהביצוע לא ימשיך לעד, חשוב להקפיד על: הגדרה ומימוש של תנאי עצירה; כאשר התנאי מתקיים, אין קריאה רקורסיבית. בכל קריאה רקורסיבית מתקדמים לעבר קיום תנאי העצירה..1.2
רקורסיה ואינדוקציה יש הרבה סדרות מתמטיות מעניינות שמוגדרות באינדוקציה. כלומר, ערך האבר ה- n -י מוגדר על סמך ערכים קודמים בסדרה. דוגמה: הסדרה 3!, 2!, 1!, מוגדרת כך: 1! = 1 n! = n (n-1)! בסדרה כזו קל לכתוב קוד שמחשב את האבר ה- n -י ברקורסיה.
חישוב!n שימוש לדוגמה: f = factorial(5) print(f) מה יודפס? 120
ניהול הזיכרון בכל קריאה לפונקציה נוצר עותק חדש שלה. בעותק: הקצאה חדשה של כל המשתנים הפנימיים, כולל הפרמטרים שמופיעים בחתימה שלה. השמה התחלתית של ערכים לפרמטרים לפי מה שהועבר בקריאה אליה. איתחול של הפקודה הבאה לבצע בקוד - בעותק החדש זו הפקודה הראשונה בפונקציה.
איך זה נראה? n = 3 n = 2 n = 1 factorial(3) return n*factorial(n-1) return n*factorial(n-1) return 1 6 2 1
להשוואה הקוד הרקורסיבי תואם להגדרה באינדוקציה ויפה יותר אבל דורש יותר זיכרון במהלך הביצוע - בתחתית הרקורסיה מוחזקים בו-זמנית n עותקים של.factorial
סדרת פיבונצ י F1 = F2 = 1 הסדרה מוגדרת באינדוקציה: Fn = Fn-2 + Fn-1 האברים הראשונים בסדרה: 21, 1, 1, 2, 3, 5, 8, 13, הערה: הסדרה היתה ידועה למתמטיקאים הודים לפני פיבונצ י: पङ गल - Pingala, c.200 B.C. वरह ङ क - Virhanka, c.700 ग प ल - Gopala, before 1135 ह मचन द र स र - Acharya Hemachandra, c.1150 זוג ארנבים מזדווגים מגיל חודש. ההריון נמשך חודש, בסופו נולד זוג נוסף. מתחילים מזוג אחד בגיל 0. ארנבים לא מתים אף פעם. כמה זוגות יהיו אחרי n חודשים?
inspirationgreen.com : מתוך
מימוש ברקורסיה
הערכת ביצועים נספור כמה קריאות מתבצעות ל- fibonacci. נסמן ב-( T(n את מספר הקריאות שנדרשות כדי לחשב את.fibonacci(n) T(1) = T(2) = 1 T(n) = 1 + T(n-2) + T(n-1) הפיתרון מקיים - 1 n T(n) = 2F (הוכחה באינדוקציה). ידוע כי F n הוא השלם הקרוב ביותר ל- 5 / n (Φ 1.618 ).Φ זה מספר שגדל בקצב מאוד מהיר! למשל: F. 100 3.54X10 20 צריכת הזיכרון: בשלב מסויים בריצה פתוחים בבת אחת n עותקים של הפונקציה.
מה הבעייה? אנחנו מחשבים מחדש אותם ערכים פעם אחר פעם. למשל, עבור 10==n, הקריאה fibonacci(n-2) מחשבת את F8 וגם הקריאה fibonacci(n-1) מחשבת את F8 באחת הקריאות הרקורסיביות שם. הערכים F1 ו- F2 מוחזרים סך הכל Fn פעמים. אם נזכור את שני הערכים האחרונים, לא נצטרך לחשב מחדש ברקורסיה את כל הערכים הקודמים.
F5 F3 F4 F1 F2 F2 F3 F1 F2
מימוש בלולאה הלולאה מתבצעת 2-n פעמים. נדרש מספר קבוע של משתנים סקלרים.
מגדלי האנוי האגדה מספרת: במקדש קאשי וישוואנאת (בעיר ואראנסי בהודו) יש חדר גדול ובו שלושה מוטות עליהם מושחלות 64 דיסקיות זהב עגולות שהקוטר שלהן שונה זו מזו. כוהנים ברהמינים, בהשראת נבואה עתיקה, עסוקים בהעברת הדיסקיות מהמוט הראשון לשני, בעזרת השלישי. בתחילה, הדיסקיות היו מושחלות על המוט הראשון בהתאם לקוטרן - הגדולה ביותר למטה והקטנה ביותר למעלה. הדיסקיות מועברות על פי הכללים המחייבים של ברהמה: יש להעביר את הדיסקיות אחת-אחת. מותר להעביר רק דיסקית מראש ערמה אחת לראש ערמה אחרת. אסור להניח דיסקית גדולה יותר על גבי דיסקית קטנה יותר. לכשתסתיים העברת הדיסקיות, יבוא העולם לקיצו. בגרסה אחרת של האגדה, אלו נזירים בודהיסטים שנמצאים בעיר האנוי Nội) (Hà בווייטנאם. משחק הדיסקיות וכנראה גם האגדה הומצאו על ידי המתמטיקאי הצרפתי אדוארד לוקאס ב- 1883. הערה: קאשי וישוואנאת הוא אחד מ- 12 מקדשי הג יוטירלינגה ङ ग) (Jyotirlinga - ज य त लर של שיווה. למה במקדש של שיווה शव) (Shiva - פועלים לפי הכללים של ברהמה ब रह म )?(Brahmā -
המחשה עם 9 דיסקיות
פיתרון ברקורסיה, n דיסקיות אם == 0 n, אין צורך לעשות דבר. אחרת (אם > 0,(n נעביר 1-n דיסקיות ממוט 1 למוט 3, בעזרת מוט 2. נעביר את הדיסקית האחרונה ממוט 1 למוט 2. נעביר 1-n דיסקיות ממוט 3 למוט 2, בעזרת מוט 1. http://towersofhanoi.info/animate.aspx
מימוש
ניתוח כמה פעמים קוראים לפונקציה?hanoi - נסמן ב-( T(n את מספר הקריאות שמתבצעות כאשר מפעילים את.hanoi(n, ) T(0) = 1 T(n) = 1 + 2T(n-1) נקבל n+1-1 T(n) = 2 (הוכחה באינדוקציה)
(המשך) כמה צעדים יש בפיתרון? - נסמן ב-( S(n את מספר הצעדים שמדפיסה הקריאה ל-.hanoi(n, ) S(0) = 0 S(n) = 1 + 2S(n-1) נקבל - 1 n S(n) = 2 (הוכחה באינדוקציה)
קץ העולם האם צריך לחשוש שהאגדה נכונה? - אפשר להראות שחייבים לבצע לפחות - 1 n 2 העברות של דיסקית כדי לפתור את הבעייה. מספר ההעברות שהברהמינים צריכים לבצע הוא:.2 64-1 = 18,446,744,073,709,551,615 אם כל העברה לוקחת שנייה, זה ייקח כ- 585 מיליארד שנים. זה יותר מפי 42 מגיל היקום ויותר מפי 127 מגיל כדור הארץ.
מבוא למדעי המחשב שיעור 5 א: תיעוד פונקציות
תכנות מודולרי עיקרון המודולריות (modularity) - תוכנית מורכבת מחלקים ליחידות נבדלות שניתן להחליף בלי לשנות יחידות אחרות. פונקציות הן כלי למימוש העיקרון הזה. כל פונקציה צריכה להיות ממוקדת, קצרה ופשוטה. לכל פונקציה צריך להיות ממשק מוגדר וברור. די בידיעת הממשק כדי להשתמש בפונקציה. השפעה בין פונקציות מתרחשת רק דרך הממשק שלהן.
הממשק של פונקציה תיאור הפעולה שהפונקציה מבצעת. המשמעות של הפרמטרים והתנאים שהפונקציה מצפה מהם לקיים בעת הפעלתה (טיפוס, תחום ערכים). המשמעות של הערך שמחזירה הפונקציה והתנאים שהערך הזה מקיים. אם הפונקציה משנה פרמטרים,mutable יש לתעד גם את המשמעות והתנאים שערכי הפלט הללו מקיימים. הודעות שגיאה ופסיקות שהפונקציה עלולה להעלות, אם יש כאלה. תופעות לוואי, אם ישנן (למשל: שינוי משתנים גלובליים).
עוד על הממשק מקובל לתעד את הממשק ב- docstring, כלומר מחרוזת התחומה בשלושה זוגות גרשיים בתחילתה ובסופה. מחרוזת כזו יכולה להשתרע על פני מספר שורות. שמים את התיעוד מייד אחרי חתימת הפונקציה. הפעלת help(func) מדפיסה את ה- docstring שמופיע בתוך הפונקציה.func לחילופין אפשר לבצע את הפקודה.print(func. doc ) יש להתייחס לתיעוד כאל חוזה שמפרט מה הפונקציה מבטיחה לבצע ומה התנאים שהיא דורשת לשם כך. אם הקלט אינו תקין, הפונקציה לא מבטיחה דבר, אלא אם כן צויין במפורש שהפונקציה מעלה הודעות שגיאה על בעיות מסויימות בקלט.