Presentation is loading. Please wait.

Presentation is loading. Please wait.

מבוא לשפת C תרגול 12: עוד רקורסיה

Similar presentations


Presentation on theme: "מבוא לשפת C תרגול 12: עוד רקורסיה"— Presentation transcript:

1 מבוא לשפת C תרגול 12: עוד רקורסיה
מבוסס על השקפים שחוברו ע"י שי ארצי, גיתית רוקנשטיין, איתן אביאור וסאהר אסמיר עבור הקורס "מבוא למדעי המחשב" נכתב ע"י טל כהן, עודכן ע"י יורי פקלני. © כל הזכויות שמורות לטכניון – מכון טכנולוגי לישראל

2 מבוא למדעי המחשב. כל הזכויות שמורות ©
תוכנייה חזרה קצרה על התרגול הקודם עוד רקורסיה פתרון של שאלות ממבחנים 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

3 מבוא למדעי המחשב. כל הזכויות שמורות ©
תזכורת בתכנות, פונקציה רקורסיבית היא פונקציה המשתמשת בעצמה לצורך חישוב התוצאה שלה. ניסוח אחר: פונקציה רקורסיבית היא פונקציה הקוראת לעצמה. לכל פונקציה רקורסיבית חייב להיות "תנאי עצירה", מצב בו הפונקציה מופעלת והיא מחזירה ערך מבלי לקרוא לעצמה שוב. אחרת, אם בכל קריאה לפונקציה היא תקרא לעצמה, הפונקציה לא תסתיים לעולם. כלומר נקבל "רקורסיה אינסופית". 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

4 תזכורת: מחסנית הקריאות של התוכנית
בכל פעם שהתוכנית קוראת לפונקציה, נוספת "מסגרת" למחסנית. המסגרת מכילה את הפרמטרים והמשתנים המקומיים של הפונקציה הנקראת. factorial(1) n = 1, prev = … int factorial(int n) { int prev; if (n == 1) return 1; prev = factorial(n – 1); return n * prev; } factorial(2) n = 2, prev = … 1 factorial(3) n = 3, prev = … 2 factorial(4) n = 4, prev = … 6 factorial(5) n = 5, prev = … 24 main 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

5 מבוא למדעי המחשב. כל הזכויות שמורות ©
רקורסיה: המוטיבציה כל רקורסיה שראינו עד עכשיו ניתן היה לכתוב ע"י לולאה. אז למה בכלל צריכים רקורסיה? התשובה היא שיש משימות שהרבה יותר קל לממש בעזרת רקורסיה. למשל, בואו ננסה לכתוב אלגוריתם שבהינתן ספריה על דיסק קשיח מדפיס את כל שמות הקבצים שנמצאים בתוך הספריה ובתוך כל תתי הספריות שלה. נניח שיש לנו פונקציות עזר הבאות: get_files(directory) – פונקציה המוצאת את כל הקבצים בספריה print_file_names(files) – פונקציה המדפיסה שמות של קבצים get_sub_directories(directory) – פונקציה המוצאת את כל תתי הספריות של הספריה הנתונה 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

6 הדפסת שמות של קבצים ע"י לולאה
ננסה לכתוב את התוכנית ע"י לולאה: print_files(directory): 1. files = get_files(directory) 2. print_file_names(files) 3. sub_directories = get_sub_directories(directory) 4. for each sub_directory in sub_directories 4.1 files = get_files(sub_directory) 4.2 print_file_names(files) 4.3 sub_sub_directories = get_sub_directories(sub_directory) 4.4 for each sub_sub_directory in sub_sub_directories 4.4.1 files = get_files(sub_sub_directory) ... וזה לא נגמר! 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

7 הדפסת שמות של קבצים ע"י רקורסיה
ובעזרת רקורסיה הכל הרבה יותר פשוט: print_files(directory): 1. files = get_files(directory) 2. print_file_names(files) 3. sub_directories = get_sub_directories(directory) 4. for each sub_directory in sub_directories 4.1 print_files(sub_directory) 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

8 גישה כללית לפתרון רקורסיבי
בהינתן בעיה שרוצים לפתור: מצא דרך להקטין את הבעיה. תניח שהפונקציה יודעת לפתור את הבעיה המוקטנת. ומצא דרך לפתור את הבעיה הגדולה ע"י שימוש בפתרון של הבעיה המוקטנת – זה יהיה הצעד של הרקורסיה. מצא מהי הבעיה המוקטנת שאנחנו יודעים את הפתרון שלה – זה יהיה תנאי העצירה. מיד נראה איך משתמשים בגישה הזאת באופן מעשי... 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

9 שאלות ממבחנים

10 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 1 הגדרה: סדרה מצטברת (Accumulative) היא סדרה בת לפחות שלושה איברים, בה כל איבר הוא סכום שני האיברים שלפניו. סדרת פיבונאצ'י היא מקרה פרטי של סדרה מצטברת, בה שני האיברים הראשונים הם 0 ו-1. כתוב פונקציה רקורסיבית: int is_accumulative(long a[ ], int size); המקבלת מערך של מספרים שלמים ואת גודלו, ומחזירה 1 אם המספרים במערך מהווים סדרה מצטברת, ו-0 אחרת. למשל עבור המערך: הפונקציה תחזיר 0, מאחר והמספר 40 אינו הסכום של 16 ו-25. 2 7 9 16 25 40 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

11 שאלה 1 – האלגוריתם הרקורסיבי
הבעיה: יש לבדוק האם מערך בגודל size הינו סדרה מצטברת. נקטין את הבעיה ע"י הקטנת גודל המערך. נניח שאנחנו יודעים לבדוק האם מערך בגודל size-1 הינו סדרה מצטברת. אם a[0]+a[1] != a[2] זאת לא סדרה מצטברת – נחזיר "0". אחרת, יש לבדוק את כל השלשות במערך בלי האיבר הראשון, ואת זה הנחנו שאנחנו יודעים לעשות. תנאי עצירה: מערך בגודל קטן מ-3 אינו סדרה מצטברת לפי הגדרה. עבור מערך בגודל 3 יש לבדוק האם מתקיים a[0]+a[1] == a[2]. 1+1 != 1 לכן זאת לא סדרה מצטברת 1 2 3 1 2 3 5 1+1 == 2 לכן יש לבדוק את 1 2 3 5 לא סדרה מצטברת לפי הגדרה 2+3 == 5 לכן זאת סדרה מצטברת 7 6 2 3 5 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

12 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 1 - פתרון int is_accumulative(long a[], int size) { /* small series are not accumulative */ if (size < 3) { return 0; } /* stop condition */ if (size == 3) { return (a[0]+a[1]==a[2]) ? 1 : 0 ; /* recursive check */ if ( (a[0]+a[1]==a[2]) && is_accumulative(a+1,size-1) ) { return 1; 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

13 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 2 ממשו פונקציה רקורסיבית void SeparateLetters(char *A, char *B, char *C) המקבלת מחרוזת A ושני מערכים ריקים B ו C. הפונקציה מעתיקה את האותיות הגדולות מ-A ל-B ואת האותיות הקטנות מ-A ל-C. בסוף הריצה B ו C צריכות להיות מחרוזות כלומר להסתיים ב-'0\' יש להניח שגודל המערכים B ו C הוא לפחות כמו הגודל של A. לדוגמא עבור: נקבל: A K i 1 @ B j 7 Y 8 \0 C 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

14 שאלה 2 – האלגוריתם הרקורסיבי
הבעיה: יש להעביר אותיות ממחרוזת A ל-B ו-C. הדרך להקטין פרמטר-מחרוזת היא להוריד את התו הראשון. נניח שאנחנו יודעים להעביר אותיות כמבוקש עבור מחרוזת בלי התו הראשון. אם תו הראשון הוא אות גדולה – נעביר אותו ל-B. אם תו הראשון הוא אות קטנה – נעביר אותו ל-C. אחרת נתעלם מהתו הראשון. אחרי שטיפלנו בתו הראשון, נשאר לטפל במחרוזת בלי התו הראשון, והנחנו שאת זה הקריאה הרקורסיבית יודעת לעשות. תנאי עצירה: כאשר מחרוזת A מסתיימת (כלומר מגעים ל-‘\0’) סיימנו להעביר את כל התווים. נשאר רק לשים ‘\0’ בסוף של B ושל C. 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

15 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 2 - פתרון void SeparateLetters(char *A, char *B, char *C) { if (*A == '\0') { *B = '\0'; *C = '\0'; return; } if (*A >= 'a' && *A <= 'z') { /* copy small letter to B */ *B = *A; SeparateLetters(A+1, B+1, C); if (*A >= 'A' && *A <= 'Z') { /* copy big letter to C */ *C = *A; SeparateLetters(A+1, B, C+1); SeparateLetters(A+1, B, C); /* skip non letters */ 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

16 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 3 עליכם לכתוב פונקציה רקורסיבית void PrintReverseWordsOrder(char s[ ], int i); שמקבלת מחרוזת הכוללת מספר מילים מופרדות על ידי תו רווח ומשתנה עזר כקלט . ותדפיס את המילים בסדר ההפוך. כלומר המילה שהופיעה במשפט המקורי במקום האחרון תודפס במקום הראשון והמילה שהופיעה במקום הראשון תודפס במקום האחרון וסדר התווים בתוך המילה לא ישתנה. אסור להשתמש בלולאות. ניתן להניח שבין כל שתי מילים יש רווח אחד בלבד ואין רווחים בהתחלה וסוף המשפט. מותר לשנות את המחרוזת. ערך משתנה העזר בקריאה הראשונה הינו תמיד 0. 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

17 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 3 (המשך) לדוגמה, עבור התכנית הבאה : int main(void){ char str[ ] = “The book is on the table”; PrintReverseWordsOrder(str, 0); return 0; } הפונקציה תדפיס : table the on is book The רמזים: החליפו את כל הרווחים של המחרוזת בתו '\0' במהלך הרקורסיה. ניתן להדפיס מחרוזת ע"י שימוש ב- %s בתוך printf. 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

18 שאלה 3 – האלגוריתם הרקורסיבי
באמצעות פרמטר i נסתכל כל פעם על תת מחרוזת str+i אחרת (מתחילים מ i=0) אם עכשיו מסתכלים על תת המחרוזת str+i: נניח שבמחרוזת str+i+1 כבר החלפנו את כל הרווחים ב‘\0’ והדפסנו את כל המילים בסדר הפוך נותר רק לבדוק את התו הראשון בתת המחרוזת str+i (str[i]) אם זה רווח – נחליף אותו ב-‘\0’ אם לא – נבדוק האם זה התחלה של מילה, כלומר האם i==0 או האם התו הקודם (s[i-1]) הוא רווח. אם זה התחלה של מילה נדפיס אותה נדפיס רווח כדי להפרידה מהמילה שתודפס אחריה אם i==0 כלומר זו המילה הראשונה במחרוזת המקורית ולכן האחרונה בהדפסה לא צריך להדפיס רווח אחריה תנאי עצירה: אם s[i] == ‘\0’ – המשפט הסתיים, חוזרים מהרקורסיה 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

19 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 3 - פתרון void PrintReverseWordsOrder(char s[], int i) { /* “i” is the current position inside “s” */ if (s[i] == '\0') { /* end of the sentence */ return; } PrintReverseWordsOrder(s, i+1); if (s[i] == ' ') { /* change all spaces to ‘\0’ */ s[i] = '\0'; /* check if this is beginning of a word */ if (i==0 || s[i-1] == ' ') { printf("%s", s+i); /* if it’s not the first word – print a space */ if (i != 0) { printf(" "); שמו לב: גם ההחלפה של הרווחים וגם ההדפסה מתבצעים אחרי הקריאה הרקורסיבית, כלומר בדרך חזרה מעומק הרקורסיה. 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

20 שאלה 4 a הוא מערך שלמים באורך n. ידוע ש- n אי-זוגי.
כתבו פונקציה רקורסיבית void extreme_to_middle(int a[ ], int n) שתדפיס את אברי a בסדר הבא משמאל לימין: a[0] a[n-1] a[1] a[n-2] … a[(n-1)/2] 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

21 מבוא למדעי המחשב. כל הזכויות שמורות ©
שאלה 4 - פתרון void extreme_to_middle(int a[], int n) { if (n==0) { return; } if (n==1) { printf("%d ", a[0]); printf("%d", a[n-1]); extreme_to_middle(a+1, n-2); 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

22 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים... בכל סעיף נתון קטע קוד שבו מופיעה שגיאה אחת או יותר. סמנו את השורה בה נפלה השגיאה, קבעו אם שגיאה זו תתגלה בשלב הקומפילציה או בשלב אחר, והסבירו במשפט אחד מהי השגיאה. 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

23 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים – 1 מצא 2 שגיאות: #include <stdio.h> int main() { int x = 100, i; double D[x]; for(i=0; i < 100; i++) { scanf("%lf", D++); } return 0; 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

24 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים – 2 מצא שגיאה אחת: #include <stdio.h> int f(int n) { if(n == 0) { return 1; } return f(n-1) + 5*f(n-2); int main() int n; scanf("%d", &n); if(n >= 0) { printf("%d\n", f(n)); return 0; 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

25 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים – 3 מצא 3 שגיאות: #include <stdio.h> int main() { int A[40]; for(i=1; i <= 40; i++) { scanf("%d", &(A+i)); } printf("%d", A[i]); return 0; 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

26 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים – 4 מצא 2 שגיאות: void swap(double *a, double *b) { int tmp; tmp = *a; *a = *b; *b = tmp; } int main() int *a, *b; *a=4; *b=5; swap(a,b); return 0; 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

27 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים – 5 #include <stdio.h> void IsEven(int x) { return (x+1)%2; } int main() int x, res=3; int a[40]; scanf("%d",x); if (x<0 || x>40) return 1; while (res=IsEven(x)) { x++; *a=x; a++; return 0; מצא 3 שגיאות: 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

28 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים – 6 מצא 2 שגיאות: void copy(int *source, int n, int *target) { while(n--) target[n] = source[n]; return; } int main() int a[] = {5, 6, 8, 9, 10, 12}; int *t; copy(a, 6, &t); return 0; 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©

29 מבוא למדעי המחשב. כל הזכויות שמורות ©
עוד קצת באגים – 7 מצא שגיאה אחת: int main() { int n, i; scanf("%d", &n); for(i = 0; i < 100; i++) { int sum = 0; sum += (i+1)*n; } return sum; 14 תרגול מבוא למדעי המחשב. כל הזכויות שמורות ©


Download ppt "מבוא לשפת C תרגול 12: עוד רקורסיה"

Similar presentations


Ads by Google