רקורסיה היא פונקציה שפותרת בעיה בעזרת גרסה קטנה יותר של אותה בעיה. כדי לחשב 4 עצרת, מספיק לדעת 3 עצרת. כדי לדעת 3 עצרת, מספיק לדעת 2 עצרת. וכך הלאה, עד מקרה שקל לפתור לבד. כאן עוקבים אחרי מחסנית הקריאות ורואים מה קורה כששוכחים את מקרה הבסיס. כדאי להכיר קודם פונקציות בפייתון. אחר כך ממשיכים לסיבוכיות.
def fact(n):
if n == 0: # base case
return 1
return n * fact(n - 1) # recursive step
print(fact(4)) # 24
המקרה הקטן ביותר, שהתשובה שלו ידועה בלי עוד קריאה. אצל עצרת: 0! = 1. בלי מקרה בסיס, הפונקציה קוראת לעצמה בלי סוף.
הפונקציה קוראת לעצמה עם בעיה קטנה יותר, ומשתמשת בתשובה: 4! = 4 · 3!. כל קריאה חייבת להתקרב למקרה הבסיס.
כל קריאה מחכה לקריאה שמתחתיה. המחשב שומר אותן אחת על השנייה, כמו ערימת צלחות. כשמגיעים לבסיס, התשובות חוזרות מלמעלה למטה, וכל קריאה יורדת מהערימה.
RecursionError.בחרו פונקציה ומספר. לחצו "שורה הבאה" ועקבו אחרי המחסנית: בכל קריאה נוספת שורה למעלה, וכשקריאה מחזירה תשובה, היא יורדת. נסו גם את הפונקציה בלי מקרה בסיס.
נסו את אלה:
אפשר לשנות את הקוד ולהריץ פייתון אמיתי. בפעם הראשונה הטעינה לוקחת כמה שניות.
הפונקציה קוראת לעצמה שוב ושוב. אחרי בערך 1000 קריאות פייתון עוצר אותה עם RecursionError.
def fact(n):
return n * fact(n - 1)
✓ נכוןdef fact(n):
if n == 0:
return 1
return n * fact(n - 1)
יש מקרה בסיס, אבל לא מגיעים אליו. עם n + 1 המספר רק גדל, ו-fact(-1) מדלג מעל 0.
return n * fact(n + 1)
print(fact(-1))
✓ מתקרבים ל-0 return n * fact(n - 1)
print(fact(4))
בלי return, הפונקציה מחשבת ולא מחזירה כלום, כלומר None. הקריאה שמעליה מנסה להכפיל ב-None ונופלת.
if n == 0:
return 1
n * fact(n - 1)
✓ נכון if n == 0:
return 1
return n * fact(n - 1)
אם מקרה הבסיס מחזיר 0, כל המכפלה מתאפסת. בכפל הבסיס הוא 1, ובחיבור הוא 0.
✗ fact(4) = 0 if n == 0:
return 0
✓ fact(4) = 24 if n == 0:
return 1
הפונקציה מדפיסה מספר, ואז קוראת לעצמה עם מספר קטן ב-1. כשמגיעים ל-0 היא מדפיסה Go! ועוצרת. עקבו אחרי המחסנית: היא גדלה עד הבסיס, ואז מתרוקנת.
פונקציה שקוראת לעצמה עם בעיה קטנה יותר, עד שמגיעים למקרה בסיס שהתשובה שלו ידועה. אז התשובות חוזרות אחורה, וכל קריאה משלימה את החישוב שלה. נסו במעבדה.
פייתון מרשה בערך 1000 קריאות שמחכות זו לזו. אם הרקורסיה לא מגיעה למקרה הבסיס, המחסנית מתמלאת והתוכנית עוצרת עם השגיאה הזו. בודקים שיש מקרה בסיס ושכל קריאה מתקרבת אליו.
כל רקורסיה אפשר לכתוב גם בלולאה. רקורסיה קצרה וברורה כשהבעיה בנויה מבעיות קטנות מאותו סוג, כמו עץ תיקיות או חיפוש בינארי. לולאה חוסכת זיכרון, כי אין מחסנית של קריאות. תרגלו למטה.