Transcript Document
קורס תכנות
שיעור תשיעי :רקורסיה
מודל הזיכרון של התכנית
• הקוד
• פקודות התכנית
קוד
קריאה
בלבד
• Data
• מחרוזות שהוגדרו ע"י המתכנת
Data
• מחסנית ()Stack
• משמש לאיחסון משתנים לוקאליים
• )Last In First Out (LIFO
• בשליטת התכנית
מחסנית
קריאה
וכתיבה
• ערימה ()Heap
• בשליטת המתכנת (בהמשך הקורס)
ערימה
הרצת התכנית -שלבים
.1
.2
.3
.4
טעינת התכנית מהדיסק לזיכרון
• הקוד לחלק הקוד ,המחרוזות הקבועות לחלק הקבוע
הקצאת Stack Frameעבור הפונקציה main
• הקצאת מקום במחסנית עבור המשתנים המקומיים של main
קריאה לפונקציה main
ביצוע סדרתי של פקודות התכנית
• קריאה לפונקציה – הקצאה של Stack Frameעבור הפונקציה
וביצוע הקוד שלה
• חזרה מקריאה – ביטול ה Stack Frameוחזקה להמשך ביצוע
הפקודות ממקום הקריאה
strlen קריאה ל
004147CC
‘H’
0
count
‘u’
‘m’
NULL
ptr
‘p’
‘t’
004147CC
humpty
004147D8
rhyme
main
‘y’
‘\0’
‘ ’
004147D8
‘H’
???
needlelen
004147D8
haystack
004147CC
needle
‘u’
’m’
.
.
if (*needle == '\0')
return (char *) haystack;
needlelen = strlen(needle);
for ( ; (haystack = strchr(haystack, *needle)) != NULL;
haystack++)
if (strncmp(haystack, needle, needlelen) == 0)
return (char *) haystack;
return NULL;
strstr
004147CC
strlen
רקע :הגדרת נוסחאות
• דרך מקובלת להגדיר נוסחה או פעולה מתמטית היא
לרשום את שלבי החישוב שלה:
n! = 1*2*3*….*n
an = a*a*…..*a
nפעמים
• מקובל גם לחשב את ערך הנוסחה שלב-אחר-שלב למשל:
4! = 1*2*3*4 = 2*3*4 = 6*4 = 24
דוגמא -עצרת
הגדרה איטרטיבית
n! = n * (n-1) * (n-2) * ...* 3 * 2 * 1
הגדרה רקורסיבית
!2! = 2 * 1
!3! = 3 * 2
!4! = 4 * 3
!)n! = n * (n-1
1! = 1
!5! = 5 * 4
דוגמא -חזקה
הגדרה איטרטיבית
an = a * a * a * ...* a * a * a
הגדרה רקורסיבית
22 = 2 * 21
23 = 2 * 22
24 = 2 * 23
an = a * an-1
a0 = 1
25 = 2 * 24
הגדרה רקורסיבית
.1מקרה קצה פשוט (תנאי עצירה)
.2כלל כיצד לצמצם את כל המקרים האחרים ב"כיוון" מקרה
הקצה
יתרונות
• קצר
• בהרבה מקרים ,ההגדרה הרקורסיבית קצרה בהרבה
מהאיטרטיבית
• נוח
• במקרים מסוימים ,ההגדרה הרקורסיבית היא ההגדרה הטבעית
והנוחה ביותר של מה שרוצים לחשב
דוגמא -סדרת פיבונאצ'י
• איברי הסדרה
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ...
• שני האיברים הראשונים הם 1ו.1 -
• שאר האיברים מוגדרים כסכום שני האיברים שלפניהם
Fib(1) = 1
Fib(2) = 1
)Fin(n) = Fin(n-1) + Fib(n-2
אז...
• זה נורא מעניין ,אבל איך זה קשור אלינו?
• פונקציות ב Cקוראות לפונקציות אחרות בפרט לעצמן!
• עצרת
int factorial(int n) /* n >= 0 */
{
;)return (n == 0) ? 1 : n * factorial(n - 1
}
• פיבונאצ'י
int fibonaci(int n) /* n > 0 */
{
? )return (n == 1 || n == 2
1 :
;)fibonacci(n-1) + fibonacci(n-2
}
איטרטיבי- סדרת פיבונאצ'י
int fib(int n)
{
int i, next, fib1 = 1, fib2 = 1;
if (n == 1 ||
return 1;
n == 2)
for (i = 3; i <= n; i++)
{
next = fib1 + fib2;
fib1 = fib2;
fib2 = next;
}
return fib2;
}
דוגמת הרצה
int main()
{
printf("%d\n", factorial(3));
return 0;
{
main
3
3*2
factorial
2
2*1
factorial
1
1*1
factorial
0
1
factorial
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
main
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
fac
main
n
3
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
fac
n
2
fac
n
3
main
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
fac
n
1
fac
n
2
fac
n
3
main
בסיס הרקורסיה
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
1
facc
n
0
fac
n
1
fac
n
2
fac
n
3
main
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
1
fac
n
1
fac
n
2
fac
n
3
main
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
2
fac
n
2
fac
n
3
main
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
6
fac
main
n
3
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
main
int factorial(int n) /* n >= 0 */
{
return (n == 0) ?
1 :
n * factorial(n - 1);
}
int main()
{
printf("%d\n", factorial(3));
return 0;
{
printf
main
6
נקודות לתשומת-לב :משתנים
• כשקוראים לפונקציה מתוך עצמה ,משתנים שהוגדרו בפונקציה
הקוראת נשארים בזיכרון.
• כי המשתנים נשארים בזיכרון עד שהפונקציה מסתיימת.
• הרבה מאוד קריאות רקורסיביות עלולות למלא את הזיכרון של
המחשב
שימוש ברקורסיה
• מצד אחד:
• לפעמים לשימוש ברקורסיה יש יתרון -קל יותר לכתוב
באמצעותו את החישוב
• מצד שני:
• לא תמיד קל למצוא הגדרה רקורסיבית לפונקציה
• לא תמיד קל להבין תוכניות שנכתבו באופן רקורסיבי
• לכן נבחר להשתמש ברקורסיה במקרים שבאמת נוחים לכך.
רקורסיה – שימושים נוספים
לא רק לחישוב נוסחאות
אפשר להשתמש בה עבור כל בעיה שניתן לפתור על-ידי
פתרון של מקרה יותר קטן/פשוט שלה
סכום מערך (האיבר הראשון וסכום שאר המערך)
( strchr התו הנוכחי או חיפוש בשאר המחרוזת)
...
סכום מערך
int sum_array(int array[], int size)
{
if (size == 0)
return 0;
return array[0] + sum_array(array + 1, size - 1);
}
עוד דוגמאות
strchr •
char* strchr(const char *str, char c)
{
if (*str == '\0')
return NULL;
if (*str == c)
return str;
return strchr(str + 1, c);
}
strlen •
int strlen(const char *str)
{
if (*str == '\0')
return 0;
return 1 + strlen(str + 1);
{
חיפוש בינארי במערך ממויין
int binarySearch(int *arr, int size, int num)
{
int mid = size/2;
if ( size == 0 )
return 0;
if ( size == 1 )
return (arr[0] == num);
.המערך
if ( arr[ mid ] == num )
return 1;
:תנאי עצירה
.0 • מערך בגודל
.1 • מערך בגודל
• המספר נמצא באמצע
נחפש בחצי השמאלי
if ( arr[ mid ] > num )
return binarySearch( arr, mid, num );
נחפש בחצי הימני
return binarySearch( arr+mid+1, size-mid-1, num );
}
Towers of Hanoi
• משימה:
• העבירו את כל הדיסקיות ממגדל Aלמגדל C
C
B
A
Towers of Hanoi
• חוקים:
• מותר להזיז רק את הדיסקית העליונה
• אסור להניח דיסקית גדולה על דיסקית קטנה
C
B
A
Towers of Hanoi
•
הזזת מגדל בין nדיסקיות שקולה ל-
.1הזזת מגדל בין n-1דיסקיות
C
B
A
Towers of Hanoi
•
הזזת מגדל בין nדיסקיות שקולה ל-
.2הזזת דיסקית אחת
C
B
A
Towers of Hanoi
•
הזזת מגדל בין nדיסקיות שקולה ל-
.3הזזת מגדל בין n-1דיסקיות
C
B
A
Towers of Hanoi - C
void move_disk(char from, char to)
{
printf("move disk from %c to %c\n", from, to);
}
void move_tower(int height, char from, char to, char temp)
{
if (height == 1) {
move_disk(from, to);
} else {
move_tower(height - 1, from, temp, to);
move_disk(from, to);
move_tower(height - 1, temp, to, from);
}
}
רקורסיה -סיכום
פונקציה רקורסיבית היא פונקציה שקוראת לעצמה
מוגדרת בעזרת הפעלתה עבור פרמטרים יותר קטנים/פשוטים,
נדרש תנאי התחלה עבור פרמטר כלשהו ,שמובטח שנגיע אליו
במהלך החישוב.
קל לתרגם נוסחה רקורסיבית לפונקציה רקורסיבית
לא תמיד קל למצוא ניסוח רקורסיבי אם הוא לא נתון