Professional Documents
Culture Documents
פרק 4
ֶרקורסיה
כאשר אנו ניגשי לפתור בעיה ,עומדי לרשותנו כמה כלי בסיסיי המקלי עלינו להגיע
לפתרו .אחד הכלי לפתרו בעיה הוא שיטת " ַה ְפ ֵרד ! ְמ"#ל" ,כלומר חלוקת הבעיה לתתבעיות.
כ $פתרו כל אחת מהבעיות בנפרד מביא אותנו בסופו של דבר לפתרו הבעיה המקורית ,על ידי
הרכבת כל הפתרונות של התתבעיות .גישת "הפרד ומשול" משמשת אותנו ג בחיי היומיו.
לדוגמה ,כאשר אנו מתכנני לצאת לטיול ,אנו מפרקי את המשימה הגדולה "יציאה לטיול",
לתתמשימות :קביעת התארי $והיעד ,סוג הבגדי שיש לקחת ,כמויות האוכל והשתייה הנחוצי
וכדומה .ע פתרו כל המשימות הללו יש לקוות שנמצא את עצמנו בפתח הדלת ,תרמיל על הגב
ומקל ביד...
לפעמי אנו נתקלי בבעיות שנית לפרק לתתבעיות שה למעשה זהות לבעיה המקורית ,אלא
שה "קטנות יותר" .פתרו תתבעיות אלה יאפשר לנו לפתור את הבעיה המקורית .תתבעיה נית
לפתור באופ ישיר ,א היא מספיק פשוטה .א לא ,נית לפתור אותה באותה שיטה ,של פירוק
לבעיות פשוטות יותר מאותו סוג .להל דוגמה מחיי היומיו :כשעלינו לאסו( את ארבעת האחי
הקטני מהג ומבית הספר בצהריי ,נית לחשוב על הפתרו של 'איסו( האח הקט' מהג
ו'איסו( שלושת האחרי' מבית הספר היסודי .איסו( האח הקט הוא פעולה אחת .איסו(
השלישייה מתפרט לכמה פעולות' :איסו( האח הקט בשלישייה'' ,איסו( שני האחרי הגדולי'
וכ $הלאה .פתרו הבעיה באמצעות פירוקה לכמה בעיות זהות קטנות יותר נקרא פתרו
רקורסיבי .בפרק זה נלמד כיצד לפתור בעיות בשיטה רקורסיבית.
לפני שנתעמק ברקורסיות מעול התכנות ,נבח בעיה נוספת )מתוקה( מהחיי...
חפיסת השוקולד
מחנכת הכיתה החליטה להפתיע את 32תלמידיה ולכבד בטבלת שוקולד שבה 32קוביות .מכיוו
שיצא ל $ש של תלמיד אחראי ביותר ,היא מורה ל $לקחת את הטבלה ,ולחלקה כ $שכל אחד
מהתלמידי ייהנה מקוביית שוקולד אחת .זהו יו קי -ח ויש סיבה לדאגה :א תחלק את
הקוביות אחת אחת ,תגלה עד מהרה שהחפיסה נמסה ורק בני מזל מעטי ייהנו מהשוקולד ,שכ
רובו יישאר על אצבעותי .$כיצד תפתור את הבעיה?
לאחר כמה שניות של מחשבה מאומצת ,אתה מציע את הרעיו הבא:
אחלק את טבלת השוקולד לשני חלקי שווי ,ואת כל חלק לתלמיד אחר .כל תלמיד שבידו
מחצית הטבלה ,ימשי $באותה השיטה ,כלומר יחלק את החפיסה שקיבל לשתיי ,ויית כל
מחצית לתלמיד אחר .כ $יימש $התהלי $עד שבידי כל תלמיד תהיה קובייה אחת בלבד .במקרה
זה – יאכל התלמיד את הקובייה הבודדה שבידו.
קל לראות כי בדר $זו כל תלמיד מקבל קובייה אחת ,ומה שיותר חשוב ,החבילה מתחלקת
במהירות .כל ילד בכיתה מקבל קוביית שוקולד מוצקה בתו 5 $שלבי בלבד ,במקו שוקולד נמס
בתו 31 $שלבי.
עיצוב תוכנה מבוסס עצמי – ג'אווה 84
בהמש $נראה שלמרבית הפתרונות הרקורסיביי שניתקל בה יש מאפייני דומי :נית
להגדיר באמצעות מקרה בסיסי ומקרה מורכב .המקרה הבסיסי "קל" להבנה ,והוא נית
לפתרו ישיר .את המקרה המורכב נית להגדיר במונחי פשוטי יותר ,בעלי אפיו זהה לאפיו
של הבעיה המקורית .כלומר ,הבעיה המקורית ניתנת לחלוקה למקרי פשוטי יותר ממנה ,שה
עדיי אינ בסיסיי אלא מורכבי .את המקרי המורכבי האלה אפשר לחלק למקרי פשוטי
עוד יותר עד שמגיעי להגדרה של המקרה הבסיסי שפתרונו פשוט וידוע .חשיבות הפירוק
למקרי פשוטי יותר של הבעיה ברורה :דבר זה מבטיח ששרשרת ההפעלות הרקורסיביות
תסתיי במקרי בסיסיי הנפתרי ישירות ,ולכ האלגורית הרקורסיבי יוצר תהלי $רקורסיבי
סופי .שרשרת ההפעלות נפסקת כאשר הרקורסיה מגיעה למקרה הבסיסי ,ולכ המקרה הבסיסי
נקרא ג תנאי עצירה.
א המספר גדול מ ,9אזי הוא מיוצג על ידי שתי ספרות לפחות ,ולפנינו מקרה מורכב .תהלי$
הפתרו ייראה כ :$תחילה נדפיס את ספרת האחדות שלו )זו הספרה הימנית ביותר בייצוגו(,
אחריה נדפיס בסדר הפו $את המספר המיוצג על ידי הספרות של המספר הנתו בהשמטת ספרה
זו .למשל ,להדפסת הספרות של 1849בסדר הפו ,$נדפיס את 9ואז נדפיס את הספרות של 184
בסדר הפו .$דבר זה נעשה כמוב על ידי הפעלה רקורסיבית של אותו תהלי .$שימו לב שהפרמטר
הנשלח לפעולה בהפעלה הרקורסיבית קט מהפרמטר המקורי ,כיו שהוא התקבל ממנו על ידי
השמטת ספרת האחדות.
כיצד נבצע את הפירוק שתיארנו? נניח שהמספר הנתו הוא ,nוהוא גדול מ .9את ספרת האחדות
שלו נית לקבל על ידי ביצוע הפעולה ,n%10המחזירה את השארית של חלוקת nב .10שארית זו
היא כמוב מספר ב ספרה אחת בלבד .אל המספר המתקבל מ nלאחר שהושמטה ספרת האחדות
נוכל להגיע על ידי ביצוע הפעולה n/10והכנסת התוצאה למשתנה מטיפוס של.
נסכ את מאפייני הרקורסיה בבעיה הנתונה )אנו מסמני את המספר הטבעי הנתו ב:(n>0 ,n
מקרה מורכב :א ,n>9הייצוג של nהוא ב יותר מספרה אחת .הפתרו יבודד את ספרת האחדות
וידפיס אותה ישירות ,ואחר כ $יזמ את הפעולה באופ רקורסיבי על המספר הנותר המיוצג על
ידי יתר הספרות ,לאחר שקופדה ספרת האחדות שלו.
ננסח את האלגורית:
הדפס במהופ )(n
א ,n<10הדפס את n
אחרת :הדפס את ) n%10השארית של חלוקת nב(10
זמ את הדפס במהופ )(n/10
ֵ
נעקוב אחרי ביצוע האלגורית בעזרת האיור והטבלה .הפרמטר הוא המספר :357
)35 (357/10 הדפס )7 (357%10 )! (n < 10 n = 357 הפעלה 1
)3 (35/10 הדפס )5 (35%10 )! (n < 10 n = 35 הפעלה 2
סו( הרקורסיה 3 הדפס n < 10 n=3 הפעלה 3
שימו לב כי כאשר זימנו את הפעולה הרקורסיבית בפע הראשונה ,הפרמטר לא התאי למקרה
הבסיסי )תנאי העצירה( ,אול ,בסופו של דבר תזומ הפעולה הרקורסיבית עבור הפרמטר
שמתאי למקרה זה והתהלי $ייעצר .א לא נגדיר מקרה בסיסי ,או א ההתכנסות לקראת תנאי
העצירה לא תיכתב כראוי ,ייווצרו זימוני חדשי בזה אחר זה .בגלל ריבוי הקריאות בריצת
הפעולה ,תזהה מערכת ג'אווה את הבעיה אחרי פרק זמ מסוי ,תשלח הודעת שגיאה ותפסיק את
ביצוע הפעולה.
המשימה של הדפסת המספר במהופ $הסתיימה כאשר זימנו את הפעולה הרקורסיבית ע מספר
חדספרתי )המקרה הבסיסי(.
87 פרק – 4רקורסיה
סביר לממש את הפעולה reverseכפעולה סטטית הפועלת על מספר של .נית לכתוב את הפעולה
הרקורסיבית הסטטית במחלקת שירות או בקוב -שבו מופיעה הפעולה הראשית ולזמ אותה
מתו $הפעולה הראשית )או מכל פעולה אחרת( ,כמו בדוגמה:
אול ,באופ כללי ,פעולה רקורסיבית אינה חייבת להיות סטטית .סיווג פעולה כרקורסיבית
מציי את האופ שבו אנו משתמשי בפעולה .בג'אווה אי הבדל בי פעולה רקורסיבית לפעולה
לא רקורסיבית .לכל הפעולות ,רקורסיביות או לא ,אות צורות זימו .כל מה שכבר למדנו ביחידה
על פעולות ,או שנלמד בהמש ,$תק( בפרט ג לפעולות רקורסיביות.
ישנ פתרונות רקורסיביי מורכבי יותר ,ובה ג לאחר זימו הפעולה הרקורסיבית האחרונה
יש המש $של תהלי $הפתרו .בדוגמה הבאה נראה בעיה שכזו.
1! = 1
2! = 1 * 2 = 2
3! = 1 * 2 * 3 = 6
4! = 1 * 2 * 3 * 4 = 24
5! = 1 * 2 * 3 * 4 * 5 = 120
...
n! = 1 * 2 * 3 *…* (n-1) * n )עבור nכלשהו(
נית לחשב את העצרת בחישוב איטרטיבי ,א $בפרק זה אנו מעונייני להציג חישוב רקורסיבי.
כדי לכתוב אלגורית רקורסיבי לפתרו בעיית חישוב העצרת של מספר נתו ,נציג הגדרה נוספת
של המושג עצרת ,והפע הגדרה רקורסיבית .מהגדרה זו נקבל בקלות חישוב רקורסיבי.
עיצוב תוכנה מבוסס עצמי – ג'אווה 88
א נוסי( להבנה זו את העובדה ש) 0! = 1 :המקרה הבסיסי( ,נקבל הגדרה רקורסיבית של
העצרת של מספר טבעי:
שימו לב שהקריאה הרקורסיבית נעשית על ידי זימו עצמי של הפעולה ,כלומר הפעולה מזמנת את
עצמה ,א $ע פרמטר שונה .על מנת להבי כיצד פועלת הרקורסיה ,נעקוב אחרי מהל $ביצוע
החצי באיור מסמני את שני שלבי הביצוע
הפעולה בעזרת האיור הבא ,עבור ער $הפרמטר ִ .4
של אלגורית זה .הראשו הוא שלב "הלו ,"$שבו מתבצעי זימוני רקורסיביי בזה אחר זה,
.השלב השני הוא שלב "חזור" ,שבו מוחזרי ערכי לחישובי שממתיני והוא מסומ בח-
. והוא מסומ בח -הפו$
89 פרק – 4רקורסיה
בשורה הראשונה ,הער $של nהוא .4כיוו שער $זה אינו ,0יש לבצע כפל של nב ).factorial (n-1
כדי לקבל את הער $של ) factorial (n-1הפעולה מחשבת את ער ,n-1 $מקבלת ,3ומבצעת זימו
רקורסיבי של הפעולה factorialע ער $הפרמטר .3החישוב מופסק והפעולה ממתינה לתוצאות
זימו זה.
בשורה השנייה מתוארת ההפעלה עבור ער $הפרמטר ,3בביצוע דומה :כדי לחשב את factorial
) ,(3יש לזמ באופ רקורסיבי את ) ,factorial (2וכ $הלאה עד לשורה החמישית ,שבה ער$
הפרמטר בזימו המתואר בשורה הוא .0
החישוב של ) factorial (0הוא חישוב של המקרה הבסיסי )שכ הפרמטר הוא ,(0ועל כ התוצאה
שלו היא .1תוצאה זו מוחזרת לזימו הקוד ,של ) ,factorial (1שהיה בהמתנה .משלב זה זימו
זה ממשי ,$מכפיל 1ב ,1ומחזיר 1לזימו ) .factorial (2זה מחדש את פעולתו ,מכפיל 1ב,2
ומחזיר .2כ $מוחזרי ערכי לזימוני הממתיני ,בזה אחרי זה .כל זימו מכפיל את ער$
הפרמטר שלו בתוצאה שהוחזרה אליו ,ומחזיר את התוצאה לזימו שהפעיל אותו .כאשר הזימו
הראשו מחזיר את התוצאה שלו ,שהיא ,24החישוב מסתיי.
שימו לב להבדל בי התהליכי הרקורסיביי בשתי הבעיות שהצגנו :הפיכת ספרות של מספר
ובעיית העצרת .בהפיכת ספרות של מספר ביצענו רק את תהלי $ה"הלו – "$הדפסנו את ספרת
האחדות ואז זימנו את הפעולה הרקורסיבית .כאשר הסתיימה הקריאה האחרונה ,סיימנו את
התהלי $הרקורסיבי .לעומת זאת ,בחישוב העצרת היינו צריכי להחזיר את הער $של זימו
הפעולה ולבצע הכפלה נוספת ב .nלכ ,כאשר פעולה מזמנת את עצמה ע ער $פרמטר קט יותר,
היא נשארת בהמתנה לער $שיוחזר מהזימו הרקורסיבי .כאשר מסתיי הזימו ,הוא מחזיר ער,$
והפעולה הממתינה "מתעוררת לחיי" וממשיכה בחישוב ,עד שהיא מסיימת ומחזירה ער.$
בדוגמה שראינו כל הזימוני התבצעו בזה אחר זה ,ואחריה התבצעו החזרות הערכי .בתהלי$
רקורסיבי זה החלוקה לשני שלבי ברורה" ,הלו "$ואחריו "חזור".
עיצוב תוכנה מבוסס עצמי – ג'אווה 90
א נסמ את הער $של המספר במקו ה kבסדרת פיבונצ'י כ :מספר פיבונצ'י ) ,(kנקבל הגדרה
רקורסיבית של ער $כל מספר בסדרה:
זוהי הגדרה רקורסיבית כיוו שהיא מגדירה כל איבר ,פרט לשני הראשוני ,באמצעות שני
האיברי הקודמי .מהגדרה זו קל לקבל אלגורית רקורסיבי לחישוב מספרי פיבונצ'י .כדי
לעשות זאת ,המושג 'הער $הוא' יתורג באלגורית להנחיה 'החזר'.
כפי שנראה מהאיור ,חלק מהחישובי הנעשי ה מיותרי .שוב ושוב נדרשת הרקורסיה לתת
מענה על ערכ של ) fibonacci (1ושל ) ,fibonacci (2א( על פי שאלו חושבו כבר .ככל שהמספר
הראשו שישלח לפעולה יהיה גדול יותר ,כ $יגדל מספר של החישובי המיותרי החוזרי על
עצמ .נראה שפתרו רקורסיבי לסדרת פיבונצ'י אינו פתרו אידיאלי ,ואינו חוס $במשאבי
השוני.
? כאשר מזמני את fibonacciעבור הער ,5 $כמה פעמי מחושב ) fibonacci (1וכמה פעמי
מחושב )?fibonacci (2
ג חישוב העצרת מתבצע על ידי אלגורית שבו קריאה רקורסיבית יחידה ,אול התהלי$
הרקורסיבי הזה הוא תהלי" $הלו חזור" .אחרי שמתבצעת הקריאה הרקורסיבית האחרונה,
עיצוב תוכנה מבוסס עצמי – ג'אווה 92
הער $שח!שב מוחזר לשלב הקוד של הרקורסיה ,והוא בתורו מוחזר להמש $החישוב ,וכ$
הלאה.
נית לזהות רקורסיית "הלו$חזור" א נענה בחיוב על השאלה" :הא אחרי הקריאה
הרקורסיבית יש לבצע משהו נוס(?" .בדוגמה שראינו ,בחישוב העצרת ,יש להכפיל את התוצאה
ב ,nזוהי רקורסיית "הלו$חזור" ,לעומת זאת בהיפו $הספרות של המספר אי צור $לבצע משהו
נוס( ולכ זו אינה רקורסיית "הלו$חזור".
כמוב שאלה אינ כל הסוגי האפשריי של רקורסיה ,א $ה מדגימי שני קריטריוני
חשובי בניתוח אלגורית רקורסיבי :א( מספר הקריאות הרקורסיביות בגו( האלגורית; ב(
הא אחרי קריאה רקורסיבית צרי $לבצע עוד חישוב בפעולה המזמנת .חשוב להדגיש שלא בכל
מקרה שקיימת רק קריאה רקורסיבית אחת באלגורית ,מדובר בהכרח ברקורסיית זנב או
רקורסיית הלו$חזור .א הקריאה היחידה מופיעה בתו $לולאה ,ייתכ שתתבצע פעמי רבות.
להל דוגמה:
לדוגמה ,נראה את בעיית העצרת ,אלא שהפע היא נכתבת כרקורסיה הדדית של שתי הפעולות:
)…( factorialו)…( :g
)public static int factorial(int n
{
)if (n == 0
;return 1
else
;)return g(n
}
בעיה :כתבו פעולה המקבלת מחרוזת ומחזירה 'אמת' א המחרוזת הנתונה היא פלינדרו,
ו'שקר' אחרת.
פתרו :נית לפתור בעיה זו ג על ידי פעולה שאינה רקורסיבית ,למשל על ידי לולאה שתשווה כל
זוג תווי הנמצאי באותו מרחק מתחילת המילה ומסופה ,א $לצור $התרגול של פרק זה נעצב
פתרו רקורסיבי.
המקרה הבסיסי :א המחרוזת מכילה תו אחד או אינה מכילה תווי כלל ,אזי היא פלינדרו.
המקרה המורכב :א המחרוזת מכילה יותר מתו אחד ,אזי :א התו האחרו זהה לתו הראשו
וא המחרוזת ללא תווי אלה )הראשו והאחרו( ,מהווה פלינדרו ,אזי המחרוזת כולה היא
פלינדרו.
קל לראות כי בקריאה הרקורסיבית הפרמטר קט יותר מזה שבקריאה המקורית ,שכ שני תווי
הורדו משני צדי המחרוזת.
עיצוב תוכנה מבוסס עצמי – ג'אווה 94
תזכורת :הפעולות הבאות לעבודה ע מחרוזות לקוחות מתו $ממשק המחלקה :String
שימו לב ,תנאי העצירה בדוגמה זו כולל ג את המקרה של מחרוזת ריקה ) 0תווי( וג את
המקרה של מחרוזת בת תו אחד.
לגבי המקרה המורכב ,א במחרוזת מספר איזוגי של תווי ,ואנו מקטיני את הבעיה על ידי
הורדת התו האחרו והתו הראשו ,תישאר בסו( התהלי $מחרוזת בת תו אחד .א במחרוזת
מספר זוגי של תווי ,אזי בסו( התהלי $תישאר מחרוזת ריקה שבה 0תווי .בשני המקרי ,א
הגענו לשלב הזה ,סימ שהמחרוזת היא פלינדרו.
נית למצוא את המקסימו בעזרת לולאה ,א $כדי להציג פתרו רקורסיבי לבעיה נזמ באופ
רקורסיבי את הפעולה כ $שתמצא את המקסימו במער ,$החל מהמקו השני בו ,וכ $הלאה.
מהו תנאי העצירה? כאשר קטע המער $שבידינו מכיל ער $אחד בלבד ,ברור שער $זה הוא
המקסימו בקטע .החזרת ער $זה תסיי את תהלי $הזימו הרקורסיבי.
לפניכ קוד הפתרו .יש לזמ את הפעולה לראשונה ע הער ,begin = 0 $כדי להתחיל את חיפוש
המקסימו במער $החל מהאיבר הראשו בו:
זהו אלגורית ע זימו רקורסיבי יחיד .שימו לב כי אחרי החזרה מהזימו הרקורסיבי ,עלינו
לבצע פעולה נוספת :השוואת הער $המוחזר מהקריאה והשמור ב ,tempלער .arr[begin] $כיוו
שכ ,$זוהי רקורסיית "הלו$חזור".
עיצוב תוכנה מבוסס עצמי – ג'אווה 96
נחזור ונדגיש :בכל זימו רקורסיבי נשמרי גבולות המער $המקורי .מיקוד ההסתכלות על
הערכי השמורי במער $בכל שלב משתנה ,ובאיור הוא מודגש בקו תחתי .רעיו ההמתנה של
התהליכי עד לשוב של הערכי המחושבי )הצור $בהמש $החישובי( ,מודגש באיור על ידי
כתיבת … לאחר הזימו עצמו:
פתרו :2נחלק את המער $לשני חלקי שווי )א מספר האיברי זוגי( ,או כמעט שווי )א
מספר האיברי אינו זוגי( .נחפש באופ רקורסיבי את המקסימו בכל אחד מהחלקי .אחרי
שנמצא ,נשווה בי שני הערכי המקסימליי ונחזיר את המספר הגדול מביניה.
כפי שאמרנו קוד ,חלוקת המער $אינה נעשית על ידי יצירת מערכי קטני יותר ,אלא בעזרת
מיקוד ההסתכלות על תתמערכי מתו $המער $המקורי ,בעזרת האינדקסי .לכ בנוס( למער,$
הפעולה תקבל שני פרמטרי :אינדקס נקודת ההתחלה של תתהמער $הנוכחי ,ואינדקס נקודת
הסו( שלו.
מהו תנאי העצירה? כאשר הגענו לתתמער $בגודל ) 1אינדקס ההתחלה שווה לאינדקס הסו((,
נחזיר את הער $השמור במער $זה ,כמקסימו.
97 פרק – 4רקורסיה
להל קוד הפתרו .המשתמש צרי $לזמ את הפעולה ע ערכי :end = arr.length-1 ,begin = 0
כיוו שהמשתנה middleהוא מטיפוס ,intהחלוקה ב 2תמיד תית לנו מספר של .עבור כל קטע
המכיל לפחות שני איברי ,תהלי $החלוקה של הקטע ייצור שני קטעי קצרי ממנו ,לכ אנו
בטוחי שבסופו של דבר תמיד נגיע לתנאי העצירה ).(begin == end
שימו לב שבאלגורית יש שני זימוני רקורסיביי .התהלי $יהיה דומה לזה שראינו בחישוב
מספרי פיבונצ'י )תהלי" $הלו$חזור" לא רצי(( ,ואנו נפרוס אותו במבנה של ע -מעקב.
}.arr = {4 ,6 ,9 ,7 ,1 ,5 ,8 דוגמה :נעקוב חלקית אחרי הפעולה )…( findMaxלגבי אותו מער:$
כיוו שאור $המער $הוא ,7והאינדקס של האיבר האחרו הוא ,6יהיה הזימו הראשו ע
הערכי :begin = 0, end = 6
עיצוב תוכנה מבוסס עצמי – ג'אווה 98
? השלימו את הציור ובדקו שאכ הער $המוחזר מהרקורסיה הוא הער.9 $
לידס
ו .דוגמה :האלגורית של א( ְק ֵ
אחד האלגוריתמי המפורסמי במתמטיקה הומצא על ידי אוקלידס ,מתמטיקאי יווני מפורס
שחי במאה השלישית לפני הספירה .האלגורית מוצא את המחלק המשות $הגדול ביותר של שני
מספרי שלמי חיוביי )שימו לב שתמיד יש כזה ,ג א הוא רק המספר .(1בתיאור להל אנו
מניחי שהמספרי שבה אנו עוסקי ה שלמי וחיוביי .המחלק המשות( המקסימלי של
שני מספרי ,הוא המספר הגדול ביותר המחלק את שניה .לדוגמה :המחלק המשות(
המקסימלי עבור שני המספרי 42ו ,28הוא ,14כיוו שזהו המספר הגדול ביותר המחלק את שני
המספרי.
האלגורית מתבסס על העובדה ,כי א מספר מחלק שני מספרי ,אזי הוא מחלק ג את
השארית של חלוקת הגדול שביניה במשנהו .למשל 3 ,מחלק את 24וג את ,18וכ את 6שהוא
השארית של חלוקת 24ב .18עובדה זו נכונה בפרט למחלק המשות( המקסימלי .מכא שכדי
למצוא מחלק משות( מקסימלי של 24ו ,18די למצוא את המחלק המשות( המקסימלי של 18
ושל .6כאשר נחלק את 18ב ,6נקבל שארית ,0כלומר 6מחלק את .18מכא ש 6הוא המחלק
המשות( המקסימלי של 18ו ,6ולכ ג של 24ו .18באופ כללי נאמר שכדי למצוא מחלק
משות( מקסימלי של שני מספרי ,די למצוא את המחלק המשות( המקסימלי של הקט מה,
ושל שארית חלוקת הגדול בקט ,א שארית זו אינה .0א היא ,0אזי הקט שבה הוא המחלק
99 פרק – 4רקורסיה
המשות( המקסימלי .החלפת זוג המספרי הנתו בזוג חדש ,שהוא "פשוט יותר" )כיוו שהמספר
הגדול הוחל( בשארית החלוקה שלו ,שהוא מספר קט יותר( ,מובילה לאלגורית רקורסיבי.
האלגורית הרקורסיבי מקבל שני מספרי חיוביי ,k ,jומחזיר את המחלק המשות(
המקסימלי שלה .האלגורית הרקורסיבי מגיע לתוצאת החישוב במהירות מפתיעה.
להל פירוט שלבי הרקורסיה עבור קלט של שני מספרי גדולי למדי ):(j=1071, k=1029
ער שארית
החזרה מחליפי החלוקה חלוקה j k
j 1029
42 1071/1029 1071 1029
k 42
j 42
21 1029/42 1029 42
k 21
j 21
21 0 42/21 42 21
k 0
האלגורית של אוקלידס פשוט ואלגנטי ,ומפתיע במהירות שבה הוא מגיע לתוצאה ,ג עבור
מספרי גדולי יחסית .נסו לדמיי כמה מסוב $היה חישוב "רגיל" של המכנה המשות( הגדול
ביותר ,שהרי חישוב כזה מבצע לולאה בי המספרי 1עד ) kהמספר הקט מבי השניי( ובודק
הא jו kמתחלקי בכל אחד מהמספרי האלה .במקרה שלנו הלולאה הייתה מתבצעת יותר
מ 1000פעמי!
זוהי דוגמה מובהקת לכוחה הרב של הרקורסיה ,המאפשרת לעתי למצוא פתרונות אלגנטיי,
פשוטי ויעילי לבעיות הנחשבות "קשות" .לעתי קרובות ,אחרי שנמצא פתרו כזה ,אפשר
למצוא ג פתרו איטרטיבי המבוסס על אותו רעיו .לפעמי ,ג הפתרו האיטרטיבי יהיה
אלגנטי ופשוט ,א $בלא מעט מקרי הוא יהיה מסוב $יותר מהפתרו הרקורסיבי )ג א
יעילות זהה(.
עיצוב תוכנה מבוסס עצמי – ג'אווה 100
א $ישנה אי נוחות מסוימת מבחינת המשתמש בקריאה מסוג זה .המשתמש רוצה לדעת מהו
המקסימו במער $מסוי .ברור לו כי הוא צרי $להעביר את המער $כפרמטר .א $הדרישה
להעביר ה את תחילת המער $וה את סופו אינה מובנת )יתרה מזאת ,הרי בקריאה שהמשתמש
מבצע ,begin=0ו ,end=arr.length-1לכ ההכנסה שלה כפרמטרי נראית מיותרת לחלוטי(.
לעתי קרובות נרצה לעטו( את הפעולה הרקורסיבית בפעולה נוספת אשר מסתירה פרטי
טכניי מסוג זה.
מוב שג א משתמשי בפתרו הרקורסיבי הראשו לבעיית החיפוש במער $נוצרת אותה
הבעיה ,משו שאנו נדרשי להעביר ג ער $של אינדקס אחד .ג כא הפתרו המוצע הוא
להשתמש בכותרת של פעולת חיפוש שאינה מציינת את האינדקס במער $שבו החיפוש מתחיל
אלא מעבירה את המער $עצמו בלבד .בגו( הפעולה יהיה זימו לפעולת עזר פרטית ובי
הפרמטרי שלה יהיה כלול ג האינדקס הנדרש.
101 פרק – 4רקורסיה
ח .סיכו
אלגורית רקורסיבי הוא אלגורית שמשמש לפתרו בעיות שאינ פשוטות דיי לפתרו ישיר.
האלגורית הרקורסיבי מפעיל את עצמו פע אחת או יותר ,על מופעי אחרי של אותה בעיה.
כדי שהגדרה רקורסיבית של אלגורית )פתרו( תהיה יעילה ,חשוב שמופעי הבעיה הנפתרי
בהפעלות הרקורסיביות יהיו "קטני יותר" או "פשוטי יותר" מהמופע המקורי .תנאי זה מבטיח
שתהליכי הפירוק מגיעי בסופו של דבר לבעיות פשוטות שאות נית לפתור ישירות.
רקורסיה היא הגדרה של מושג או אלגורית )כלומר של פתרו לבעיה( ,המשתמשת במושג •
או באלגורית המוגדר .אלגורית רקורסיבי יזמ את עצמו שוב ושוב על ערכי פרמטרי
הולכי וקטני ,עד שיגיע למקרה הבסיסי שהפתרו שלו מתקבל באופ ישיר.
כשכותבי פתרו רקורסיבי לבעיה יש להגדיר שני מצבי :מקרה מורכב שאותו נפרק •
למקרי פשוטי יותר ,ומקרה בסיסי ,כלומר תנאי עצירה ,המגדיר את הפתרו למקרה
הפשוט בלי זימו נוס( של אלגורית הפתרו.
קיימי סוגי רקורסיה רבי .בפרק זה התייחסנו במפורט לארבעה" :רקורסיית זנב" •
המסתיימת ע פתרו המקרה הבסיסי; "רקורסיית הלו$חזור" שבה לאחר פתרו המקרה
הבסיסי יש לחזור ע הער $המחושב ולבצע חישובי קודמי שממתיני לסיומ;
"רקורסיה כפולה" המזמנת את ההלי $הרקורסיבי פעמיי בכל שלב; "רקורסיה הדדית",
המתבצעת באמצעות שתי פעולות שונות )לפחות(.
באלגוריתמי על מערכי ,הדר $הטיפוסית לקבלת פתרו רקורסיבי היא על ידי הגדרת •
הבעיה מחדש ,כ $שתתייחס לקטעי של מערכי .כ $מתקבלי מקרי פשוטי יותר על ידי
צמצו קטעי המער $רק לחלקי שעליה האלגורית פועל.
מושגי
מקרה מורכב
recursion רקורסיה
רקורסיה הדדית
רקורסיית הלו$חזור
תרגילי
א .מעקב אחר רקורסיות
לפניכ כמה פעולות רקורסיביות .עבור כל אחת מה רשמו את טענת היציאה.
ומספר נוס( של, של מספרי שלמי שאינו ריק$ הפעולה מקבלת מער: טענת כניסה.5
וחיובי
.$הקט או שווה לגודל המער
if (k == 1)
return (a[0]);
x = mystery(a, k-1) * (k-1);
return ((a[k-1] + x) / k);
}
. המספר השני הוא ג אישלילי, הפעולה מקבלת שני מספרי שלמי: טענת כניסה.7
לפניכ כמה פעולות רקורסיביות ובצמוד לכל אחת טענת הכניסה שלה וטענת היציאה שלה.
בכל פעולה חסרי ביטויי אחדי שעליכ להשלי כדי שהפעולה תבצע את הנדרש.
.8
טענת כניסה :הפעולה מקבלת מספר של וחיובי.
טענת יציאה :הפעולה מחזירה את סכו הספרות של המספר המתקבל.
)public static int sumDigits(int num
{
)___________( if
;)return (num
;))___________(return (num % 10 + sumDigits
}
.9
טענת כניסה :הפעולה מקבלת מספר של וחיובי.
טענת יציאה :הפעולה מחזירה את מספר הספרות של המספר המתקבל.
)public static int numDigits(int num
{
)if (num < 10
;)___________( return
;))___________(return (___________ + numDigits
}
.10
טענת כניסה :הפעולה מקבלת מחרוזת.
טענת יציאה :הפעולה מדפיסה את המחרוזת המתקבלת בסדר הפו.$
)public static void reverseString(String str
{
)if (str.length() > 0
{
;)___________(char ch = str.charAt
;)___________(reverseString
;)System.out.print(ch
}
}
105 פרק – 4רקורסיה
.11
טענת כניסה :הפעולה מקבלת מספר של חיובי וספרה.
טענת יציאה :הפעולה מחזירה trueא הספרה נמצאת במספר ,אחרת מחזירה . false
)public static boolean isDigitExist(int num, int digit
{
;boolean ans = num%10 == digit
)___________( if
;)return (ans
;)___________( return
}
.12
טענת כניסה :הפעולה מקבלת מספר של חיובי.
טענת יציאה :הפעולה מחזירה trueא כל הספרות במספר זוגיות ,אחרת מחזירה . false
)public static boolean isAllDigitsEven(int num
{
;___________ = boolean ans
)if (num < 10
;)___________( return
;))___________(return (ans && isAllDigitsEven
}
בכל אחת מהשאלות הבאות עליכ לבחור פעולה רקורסיבית אחת מהשלוש המוצעות ,כ
שתתאי לטענת הכניסה והיציאה המוצגות.
.13
טענת כניסה :הפעולה מקבלת שני מספרי שלמי.n >= 0 ,m > 0 :
טענת יציאה :הפעולה מחזירה .n % m
א.
)public static int mod(int n, int m
{
)if (n > m
;return m
else
;)return mod(m-n, m
}
עיצוב תוכנה מבוסס עצמי – ג'אווה 106
.ב
public static int mod(int n, int m)
{
if (m < n)
return 1;
else
return mod(n-m, m);
}
.ג
public static int mod(int n, int m)
{
if (n < m)
return n;
else
return mod(n-m, m);
}
.14
. הפעולה מקבלת מחרוזת:טענת כניסה
. הפעולה מחזירה מחרוזת הפוכה לזו שהתקבלה:טענת יציאה
.א
public static String rev(String str)
{
if (str.length() == 0)
return str;
else
return rev(str.substring(1)) + str.charAt(0);
}
.ב
public static String rev(String str)
{
if (str.length() == 0)
return str;
else
return rev(str.substring(0)) + str.charAt(0);
}
.ג
public static String rev(String str)
{
if (str.length() == 0)
return str;
else
return rev(str.substring(0)) + str.charAt(1);
}
107 – רקורסיה4 פרק
.ב הרקורסיבי – הריצו כל אחת מהתוכניות הבאות ובדקו מה היא מבצעת2ָ ַה
: תוכנית ראשונה.15
public class RecursiveTurtleDemo1
{
public static void main(String[] args)
{
Turtle t = new Turtle();
t.setDelay(50);
t.turnRight(90);
t.moveBackward(200);
t.tailDown();
t.setTailColor(Color.BLUE);
draw(t, 15, 4);
t.tailUp();
t.moveForward(40);
}
public static void draw(Turtle t, int width, int step)
{
if (step <= 1)
t.moveForward(width);
else
{
draw(t, width, step-1);
t.turnLeft(60);
draw(t, width, step-1);
t.turnRight(120);
draw(t, width, step-1);
t.turnLeft(60);
draw(t, width, step-1);
}
}
}
: תוכנית שנייה.16
public class RecursiveTurtleDemo2
{
public static void main(String[] args)
{
Turtle t = new Turtle();
t.setDelay(50);
t.turnRight(90);
t.moveBackward(200);
t.tailDown();
t.setTailColor(Color.RED);
hilbert0(t, 4, 10);
t.tailUp();
t.moveForward(40);
}
עיצוב תוכנה מבוסס עצמי – ג'אווה 108
.22כתבו אלגורית חשב חזקה ) ,(x, yהמחשב את – xyתוצאת העלאת xבחזקת ,yכאשר שני
המספרי שלמי y ,לא שלילי x ,חיובי ממש .ממשו את הפעולה.
תרגילי על מער:
.23כתבו פעולה רקורסיבית המקבלת מער $של מספרי ומחזירה את סכו איבריו.
.24כתבו פעולה רקורסיבית הבודקת הא מער $נתו ממוי בסדר עולה.
.25כתבו פעולה רקורסיבית הבודקת הא ערכי המער $מהווי סדרה חשבונית )כלומר קיי
הפרש קבוע בי איברי המער.($
עיצוב תוכנה מבוסס עצמי – ג'אווה 110
תרגילי הדפסה:
.26כתבו פעולה רקורסיבית המקבלת כפרמטר מספר טבעי nומדפיסה משולש של כוכביות ,ובו n
שורות .בשורה העליונה של המשולש יהיו nכוכביות צמודות ,בשורה שמתחת יהיו n-1
כוכביות צמודות ,וכ $הלאה .בשורה התחתונה תודפס כוכבית אחת.
לדוגמה ,עבור n = 4יודפס:
****
***
**
*
הגדירו מהו סוג הרקורסיה שאת מבצעי בפתרו שכתבת? הסבירו.
.27כתבו פעולה רקורסיבית המקבלת כפרמטר מספר טבעי nומדפיסה משולש של כוכביות ,ובו
nשורות .בשורה העליונה של המשולש תהיה כוכבית אחת ,בשורה השנייה שתי כוכביות
צמודות וכ $הלאה .בשורה האחרונה יודפסו nכוכביות צמודות.
לדוגמה ,עבור n = 4יודפס:
*
**
***
****
.28כתבו פעולה רקורסיבית המקבלת כפרמטר מספר טבעי ,nומדפיסה צורה של שני משולשי
המחוברי בקודקוד .בשורה הראשונה שלו nכוכביות ובשורה האחרונה שלו nכוכביות:
לדוגמה ,עבור :n = 3
***
**
*
**
***
111 פרק – 4רקורסיה
2 ,4 ,8
2 ,8 ,4
4 ,2 ,8
4 ,8 ,2
8 ,2 ,4
8 ,4 ,2
ב .בא( שלב אסור שדיסקית גדולה תהיה מונחת על דיסקית קטנה ממנה.
למשל עבור הזימו ) ,solveHanoi (2כלומר משחק ב 2דיסקיות ,יתקבל פלט זה:
הזז מ 1ל2
הזז מ 1ל3
הזז מ 2ל3
פירושה של הפקודה "הזז מ aל "bהיא הזזת הדיסקית העליונה ממוט מספר aלמוט מספר .b
רמז :היעזרו בפעולת עזר רקורסיבית ,אשר תקבל כפרמטר את מספרי המוטות ואת מספר
הדיסקיות הכולל:
כתבו פעולה רקורסיבית שתציב שמונה מלכות על לוח שחמט ריק )מער $דוממדי בגודל ,(8x8כ$
שא( אחת לא תאיי על האחרות.
8 1
7 2
row פרש
6 3
5 4
col
מעבר הפרש למשבצת אפשרית נקרא מסע פרש ) (knight moveהמורכב ממעבר על שתי
משבצות רצופות בכיוו מאוז ואחת בכיוו מאונ ,$או שתי משבצות רצופות בכיוו מאונ$
ואחת בכיוו מאוז .שימו לב ,כי א הפרש נמצא על אחת המשבצות שבמסגרת הלוח או
קרוב לה ,הוא אינו יכול לבצע את כל 8המסעות!
113 פרק – 4רקורסיה
כתבו פעולה רקורסיבית שתאפשר ביקור של הפרש בכל משבצות לוח השחמט .הביקור
יתחיל במשבצת ) .(0,0בסו( התוכנית בכל משבצת בלוח יופיע מספר )מ 1ועד (64המתאר את
מספר הביקור של הפרש במשבצת .לדוגמה התבוננו בלוח מסעי הפרש שהתקבל:
.33בעיית המבו
נתו מער $דוממדי המדמה מבו $שלו נקודת כניסה אחת ונקודת יציאה אחת .יש לכתוב פעולה
רקורסיבית המדפיסה את המסלול ליציאה מהמבו.$