1 / 26

Introduction to Programming in C

Introduction to Programming in C. תרגול 8. 1. 1. רקורסיה תזכורת. הגדרה : המונח רקורסיה (recursion) מתאר מצב שבו פונקציה קוראת לעצמה באופן ישיר או באופן עקיף.

baxter
Download Presentation

Introduction to Programming in C

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Introduction to Programming in C תרגול 8 1 1

  2. רקורסיהתזכורת הגדרה: המונח רקורסיה (recursion) מתאר מצב שבו פונקציה קוראת לעצמה באופן ישיר או באופן עקיף. שימוש: נוח להשתמש בפונקציות רקורסיביות על מנת לפתור בעיות בעלות אופי רקורסיבי. באופן כללי, השיטה תהיה להקטין את מימד הבעיה, לפתור את הבעיה על המימד היותר קטן ולהשתמש בפיתרון שמתקבל על מנת לפתור את הבעיה במימד אחד יותר גבוהה.

  3. תרגיל מס' 1 – abc כתוב פונקציה רקורסיבית אשר חתימתה היא: void abc(char arr[],int lastPlace, int curPlace) המקבלת מערך של char-ים, את אינדקס סוף המערך ומספר שלם שהוא המקום במערך ממנו אנו מעונינים להתחיל במילוי המערך בתווים (בקריאה ראשונה יהיה מס' זה שווה ל- 0). הפונקציה מדפיסה את כל האפשרויות למלא את המערך מהמקום שקיבלה, curPlace, עד המקום lastPlace באותיות a,b,c במילים אחרות, מטרת הפונקציה היא להדפיס את כל האפשרויות ליצור מילה באורך מסוים בעזרת האותיות a,b,c .

  4. תרגיל מס' 1 – abc למשל: עבור התוכנית הבאה: void abcInvokingFun(char word[],int lengthOfWord){ abc(word, lengthOfWord,0); } void main(){ char word[5]; abcInvokingFun(word,3); } נקבל את הפלט: aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cab cac cba cbb cbc cca ccb ccc

  5. תרגיל מס' 1 – abc כתובתו של תחילת המערך המקום הנוכחי שאנחנו משנים אינדקס סוף המערך void abc(char arr[],int lastPlace, int curPlace) { תנאי העצירה: אם הגענו ל- lastPlace אז שים במקום הנוכחי יש '0\' (הגענו לסוף), הדפס את המחרוזת (מהתחלתה) וחזור. if (curPlace == lastPlace) { arr[curPlace]='\0'; printf("%s\t",arr); • כעת אנחנו עובדים על המקום curPlace במערך: • הצב בו a, וקרא לפונק' abc (עם כתובת המערך, ואינדקס המקום הבא) אשר תדאג למילוי כל האפשרויות הקימות בשאר המערך. • לאחר שחזרת, הצב את b במקום a, ושוב קרא לפונק' עם המקום הבא במערך כפרמטר. • כנ"ל לגבי c. return; } arr[curPlace] = 'a'; abc (arr,lastPlace,curPlace+1); arr[curPlace] = 'b'; abc (arr,lastPlace,curPlace+1); arr[curPlace] = 'c'; abc (arr,lastPlace,curPlace+1); }

  6. תרגיל מס' 1 – abc ??? a?? b?? c?? aa? ab? ac? ba? bb? bc? ca? cb? cc? aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cab cac cba cbb cbc cca ccb ccc הדפסה: aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cab cac cba cbb cbc cca ccb ccc aaa נראה את תרשים הקריאות המתקבל מהרצת הדוגמא שלמעלה (בדומה לתרשים הקריאות שראינו בכתה): כל ריבוע מייצג קריאה לפונקציה, כאשר בתוך כל ריבוע מופיע תוכן המערך שהפונקציה מקבלת. ערך הפרמטרcurPlace הוא משותף לכל רמה בתרשים ומצויין בצד ימין. curPlace=0 curPlace=1 curPlace=2 curPlace=3

  7. תרגיל מס' 2 • כתבו תוכנית הקולטת עד 20 ערכים שלמים (1- מציין סוף קלט) למערך ומוצאת את הערך המקסימאלי. • לשם כך, ניתן לפרק את הבעיה לשתי בעיות קטנות יותר • כתבו פונקציה רקורסיבית שקולטת את המערך • כתבו פונקציה רקורסיבית שמחזירה את הערך המקסימאלי

  8. תרגיל מס' 2 int Max(int a[], int i,int length){ int max; if (i == length -1) { return a[i]; } max = Max(a, i + 1); return (a[i] > max ? a[i] : max); } int main(){ int max,a[MAX_LEN],length; length = GetArray(a, 0); max=Max(a,0, length ); printf(“max=%d\n”max); return 0; } int GetArray(int arr[], int i){ int num; if (i > MAX_LEN – 1){ return 0; } scanf("%d", & num); if (num!= -1) { arr[i] = num; return GetArray(arr, i + 1) +1; } else { return 0; } }

  9. תרגיל מס' 3 • כתוב פונקציה רקורסיבית אשר מקבלת מחרוזת s, תו ch, ומספר שלם n. הפונקציה תחפש את המופע ה- n-י של ch במחרוזת s ותחזיר את האינדקס שלו. דוגמא: בהינתן המחרוזת “abbc”, התו b, והשלם 2 (עבור n) הפונקציה תחזיר 2. התו ‘b’ מופיע בפעם השנייה במחרוזת s באינדקס 2 במחרוזת.

  10. תרגיל מס' 3 int FindNOccurence(char s[], char ch, int n, int index){ if (n == 0) return index -1; if (s[index] == '\0') return -1; if (s[index] == ch) returnFindNOccurence (s, ch, n – 1, index + 1); return FindNOccurence(s, ch, n, index + 1); {

  11. תרגיל מס' 4 נתונה הפונקציה הרקורסיבית הבאה: #include <stdio.h> int secret( int n){ If( n<0 ) return 1 + secret ( -1 * n); if ( n<10 ) return 1; return 1 + secret( n/10 ); } מה הערך של secret(-4321) ו- secret(12345) ? מה מבצעת הפונקציה secret עבור פרמטר חיובי ועבור פרמטר שלילי? כתוב פונקציה זו מחדש בצורה לא רקורסיבית.

  12. תרגיל מס' 4 פתרון: סעיף א': 5 בשני המקרים. סעיף ב': הפונקציה מחזירה את מס' התווים שמייצגים את הארגומנט. כך למשל הפלט של התוכנית הבאה: void main() { char s[80]; printf("%d\n",secret(-43221)); printf("%d\n",secret(12345)); יהיה: 6 5 }

  13. תרגיל מס' 4 כתוב אותה פונקציה בצורה לא רקורסיבית. פתרון: int iterSecret( int n){ int len=1; if( n<0 ) { n=(-1) * n; len++; } while( n>=10 ) { n/=10; len++; } return len; }

  14. תרגיל מס' 5 • עיין בפונקציה הבאה: • int what(int a, int b){ • if(!a && !b) • return 1; • if(a > b) • return a * what(a-1, b); • return b * what(a, b-1); • } • בהנחה שהפונקציה הנ"ל מקבלת שני ערכים אי-שליליים (חיוביים או אפס), סמן את כל התשובות הנכונות (בדף התשובות): • הפונקציה נכנסת לרקורסיה אינסופית. • הערך המוחזר של הפונקציה תמיד 0. • הערך המוחזר של הפונקציה יכול להיות 0. • הערך המוחזר של הפונקציה תמיד 1. • הערך המוחזר של הפונקציה יכול להיות 1. • בקבלת שני ערכים חיובייםa ו- b, אם הערכים לא גדולים מידי,הפונקציה מחזירה את הערך של a! x b!. • אף לא אחת מהתשובות לעיל.

  15. פתרון:5 (למשל כאשר a=0 וגם b=0) + 6 (יש להדגים למשל עבור a=2 וגם b=1 )

  16. תרגיל מס' 6 Subset Sum problem נתונה סדרת ערכים שלמים (כמערך) arr ומספר שלם S. צ"ל: האם קיימת תת-סדרה במערך כך שסכומה S. למשל עבור: arr ו-S=14 התשובה היא כן כי קיימת תת-סדרה במערך שהיא: 7,6,1 וסכומה 14.

  17. תרגיל מס' 6 • האלגוריתם: • עבור כל איבר i במערך, יש 2 אפשרויות: • אפשרות א': כן לוקחים את האיבר ה-i, ומנסים למצוא SubsetSum בגודל S-arr[i] במערך קטן יותר ב-1. • אפשרות ב': לא לוקחים את האיבר ה-i, ומנסים למצוא SubsetSum בגודל S במערך קטן יותר ב-1. • תנאי העצירה שלנו יהיו: • אם קיבלנו באיזשהו שלב S==0 אז נמצא SubsetSum במערך ונחזיר 1. • אחרת, אם קיבלנו S<0 או 0==n, אז הבחירות שלקחנו עד עכשיו אינן מובילות לפתרון (אם S<0 אזי עברנו את הסכום המבוקש ואם 0==n אזי עדיין לא הגענו לסכום ואין לרשותנו אברים מתוכם נוכל לבחור) ונחזיר 0.

  18. תרגיל מס' 6 int SubsetSum(int arr[], int idx, int n, int S) { if (S==0) return 1; //This is stopping condition #1. if (S<0 || n==0) return 0; //This is stopping condition #2. return SubsetSum(arr, idx+1, n-1, S-arr[idx]) || SubsetSum(arr, idx+1, n-1, S); } טיפול באפשרות א' טיפול באפשרות ב'

  19. תרגיל מס' 7 שנו את הפונקציה שכתבתם בשאלה הקודמת כך שהיא תחזיר את כמות תתי-הקבוצות של איברי המערך המקיימות שסכומן שווה בדיוק ל-s.

  20. תרגיל מס' 7 int SubsetSum(int arr[], int idx, int n, int S) { if (S==0) return 1; //This is stopping condition #1. if (S<0 || n==0) return 0; //This is stopping condition #2. return SubsetSum(arr, idx+1, n-1, S-arr[idx]) + SubsetSum(arr, idx+1, n-1, S); }

  21. תרגיל מס' 8 • נתון מערך דו-מימדי בגודל m*n המכיל מספרים טבעיים קטנים מ-100. מסלול חוקי במערך מתחיל בתא (0,0) ומסתיים בתא (m-1,n-1), כאשר ההתקדמות תלויה בספרת האחדות והעשרות של המס' שבתא הנוכחי. אם בתא (2,3) רשום המס' 13, אז ישנם 2 דרכים להתקדם מתא זה: 1) 1+ בשורות ו 3+ בעמודות. כלומר לתא (3,6). 2) 3+ בשורות ו 1+ בעמודות. כלומר לתא (5,4). אם בתא רשום מס' חד ספרתי, למשל 3, נתייחס אליו כאל 03.

  22. תרגיל מס' 8 (המשך) • לדוגמה: מסלול אפשרי הוא המסלול הצבוע בירוק. • כתבו פונקציה רקורסיבית המחזירה את מס' המסלולים החוקיים במערך (כמובן שאסור להשתמש בלולאות).

  23. תרגיל מס' 8 (פתרון)

  24. תרגיל מס' 9 כתבו פונקציה רקורסיביתvoid X(int lines) שמדפיסה את האות X באמצעות כוכביות ב-lines שורות (נניח ש-lines תמיד אי-זוגי). ניתן להשתמש בלולאות. לדוגמא עבור X(9) יודפס:  * * * * * * * * * * * * * * * * * הכוכבית הראשונה חייבת להופיע בתחילת השורה הראשונה. אין להגדיר פונקציה נוספת.

  25. פתרן תרגיל מס' 9 פתרון: הרעיון הוא להדפיס את השורה הראשונה של X, אחר כך לפתור את תת-הבעיה עבור X ללא השורה הראשונה והאחרונה ואז להדפיס את השורה האחרונה (שהיא זהה לראשונה). ניתן להדגים את סדר הפעולות הכרונולוגי בכל קריאה רקורסיבית באמצעות התרשים הבא:

  26. פתרון תרגיל מס' 9 void X(int lines){ int static blank; int I; for (i=0; i<blank; i++) putchar(' '); putchar('*');   if(lines==1){ puts(""); return; }   for (i=0; i<lines-2; i++) putchar(' '); puts("*");   blank++; X(lines-2); blank--; for (i=0; i<blank; i++) putchar(' '); putchar('*'); for (i=0; i<lines-2; i++) putchar(' '); puts("*"); }

More Related