Introduction to Programming in C

Slides:



Advertisements
Similar presentations
Introduction to C Systems Programming Concepts. Introduction to C A simple C Program A simple C Program –Variable Declarations –printf ( ) Compiling and.
Advertisements

What is a pointer? First of all, it is a variable, just like other variables you studied So it has type, storage etc. Difference: it can only store the.
1 CS 201 Pointers (2) Debzani Deb. 2 Overview Pointers Functions: pass by reference Quiz 2 : Review Q & A.
Kernighan/Ritchie: Kelley/Pohl:
1 Lecture 7  Fundamental data types in C  Data type conversion:  Automatic  Casting  Character processing  getchar()  putchar()  Macros on ctype.h.
Introduction to C Programming CE Lecture 18 Dynamic Memory Allocation and Ragged Arrays.
Introduction to C Systems Programming. Systems Programming: Introduction to C 2 Systems Programming: 2 Introduction to C  A ‘C’ Program –Variable Declarations.
1 Chapter 8 Functions, Pointers, and Storage Classes  Pointer  Pointers to void  Call-by-Reference  Basic Scope Rules  Storage Classes  Default Initialization.
1 Review of Chapter 6: The Fundamental Data Types.
Computer Science 210 Computer Organization Introduction to C.
CP104 Introduction to Programming Modular Programming Lecture 16__ 1 Modular Programming II Functions with single output Functions with multiple outputs.
1 Milena Mihail Georgia Tech. with Stephen Young, Giorgos Amanatidis, Bradley Green Flexible Models for Complex Networks.
Lexical Analysis CSE 340 – Principles of Programming Languages Fall 2015 Adam Doupé Arizona State University
1 CS 552/652 Speech Recognition with Hidden Markov Models Winter 2011 Oregon Health & Science University Center for Spoken Language Understanding John-Paul.
Elementary Combinatorics Combinatorics  Deals with enumeration (counting) techniques and related algebra.  Basis of counting XSet |X|No. of elements.
תרגול 13 חזרה 1. Exam example 8 public class Stam { private char x; public Stam() { this.x = '*'; } public Stam (char c) { this.x = c; } public Stam getStam()
Constants Numeric Constants Integer Constants Floating Point Constants Character Constants Expressions Arithmetic Operators Assignment Operators Relational.
Dennis Ritchie 1972 AT & T Bell laboratories (American Telephone and Telegraph) USA 1www.gowreeswar.com.
Counting CSC-2259 Discrete Structures Konstantin Busch - LSU1.
Pointers: Basics. 2 What is a pointer? First of all, it is a variable, just like other variables you studied  So it has type, storage etc. Difference:
Introduction to Computer Organization & Systems Topics: C arrays C pointers COMP Spring 2014 C Part IV.
C programming---Pointers The first step: visualizing what pointers represent at the machine level. In most modern computers, main memory is divided into.
2/23/2016Course material created by D. Woit 1 CPS 393 Introduction to Unix and C START OF WEEK 9 (C-3)
POINTERS IN C Pointer Basics, Pointer Arithmetic, Pointer to arrays and Pointer in functions.
CMPSC 16 Problem Solving with Computers I Spring 2014 Instructor: Lucas Bang Lecture 11: Pointers.
1 Strings and Languages Lecture 2-3 Ref. Handout p12-17.
CHAPTER TWO LANGUAGES By Dr Zalmiyah Zakaria.
Pointers: Basics. 2 Address vs. Value Each memory cell has an address associated with it
1 Memory, Arrays & Pointers. Memory 2 int main() { char c; int i,j; double x; cijx.
Arrays and Pointers (part 1) CSE 2031 Fall July 2016.
CSC 215 Pointers and Arrays. Pointers C provides two unary operators, & and *, for manipulating data using pointers The operator &, when applied to a.
Ex-1 #include <stdio.h> struct sample { int a=0; char b='A';
Computer Science 210 Computer Organization
Computer Science 210 Computer Organization
CSE 220 – C Programming Pointers.
CSC-2259 Discrete Structures
Text and Other Types, Variables
C/C++: type sizes in memory pointers
Inside of SQL Server Indexes
Pointers Department of Computer Science-BGU יום שלישי 31 יולי 2018.
LANGUAGES Prepared by: Paridah Samsuri Dept. of Software Engineering
Data Types, Type Conversions, Switch-Case, Methods
Hassan Khosravi / Geoffrey Tien
Arrays in C.
POINTERS.
Programmazione I a.a. 2017/2018.
Programming Languages and Paradigms
Lecture 6 C++ Programming
CSI-121 Structured Programming Language Lecture 16 Pointers
מצביעים והקצאה דינאמית
Computer Science 210 Computer Organization
Computer Science 210 Computer Organization
Introduction to Programming in C
Instructor: Ioannis A. Vetsikas
הרצאה 6: כתובות ומצביעים הקצאה דינמית מחרוזות
Lecture 8.
Pointers Department of Computer Science-BGU יום רביעי 21 נובמבר 2018.
Computer Programming תירגול 13 חזרה למבחן.
Computer Programming Fall 2012 תרגול 6 פונקציות
Introduction to Programming in C
Pointers.
CSE 100 Data Types Declarations Displays.
Outline Defining and using Pointers Operations on pointers
Recap Week 2 and 3.
Pointers Pointers point to memory locations
Pointers Chapter 11 Copyright © 2008 W. W. Norton & Company.
Pointers Chapter 11 Copyright © 2008 W. W. Norton & Company.
Lecture 8.
FUNCTION ||.
Enumerating all Permutations
Presentation transcript:

Introduction to Programming in C תרגול 8 1 1 Introduction to C - Fall 2010 - Amir Menczel

נושאים מצביעים רקורסיה רקע אופרטורים על מצביעים מצביעים כפרמטרים לפונקציה רקורסיה

מצביעים תאור הזיכרון של המחשב: ניתן לחשוב על זיכרון המחשב כעל רצף של תאים, כל אחד בגודל בית (byte) כאשר כל בית בגודל של 8 סיביות (bits) כאשר כל סיבית יכולה לקבל או ערך 1 או ערך 0. סה"כ כל בית (byte) יכול לכן לקבל 256 ערכים שונים המייצגים את מנעד המספרים הבינאריים באורך 8. כתובת הזיכרון- כל תא בזיכרון מזוהה ע"י ערך מספרי המתאר את מיקומו המדוייק. ערך זה הינו הכתובת של התא בזיכרון (memory address). למשל, אם נגדיר את המשתנים הבאים: char var1; // storage for type char is 1 byte on any platform. double var2; // storage for type double is mostly 8 bytes but can varies - it’s platform dependent int var3; // storage for type int is mostly 4 bytes but can varies -it’s platform dependent sizeof המחזירה את גודל הטיפוס בבתים.sizeofעל מנת לדעת גודל טיפוס ניתן להשתמש בפונקציה sizeof (name_of_character)בצורה: sizeof (char) אילו הינו כותבים לדוגמה: היינו מקבלים 1. תוכן הזיכרון- בכל כתובת בזיכרון מופיע ערך מספרי כלשהו המייצג את הערך הספציפי של הטיפוס הנמצא בכתובת זו. טווח ערכים זה נע בין 0 ל- -1 2^(8*sizeof(type)) . משמעות הערך נגזרת מגודל מספר זה ומסוג הטיפוס עצמו השמור בכתובת זו.

אופרטורים מצביעים מצביעים (pointers): מצביע הינו משתנה שהערך שלו הוא כתובת של משתנה כלשהו. במילים אחרות, מצביע הינו משתנה שמצביע למשתנה אחר. אם נרצה להגדיר את p כמצביע למשתנה כלשהו מטיפוסint , שורת ההצהרה תיראה כך: int *p; באופן כללי, תבנית הצהרה על מצביעים הינה: <variable-type> *<variable name>; האופרטור & אם x הוא משתנה אזי &x היא כתובת הזיכרון של x, כלומר האופרטור & מציין "כתובתו של…". האופרטור  האופרטור  הינו אופרטור שפועל על מצביעים. למשל עבור הדוגמה הקודמת אם היינו כותבים בהמשך p=&x(כאשר x הוא משתנה מסוג int ) *p היה שקול לx- כלומר *p הוא התוכן של תא הזיכרון ש-p מצביע עליו. אופרטורי הקידום: +,- פעולות אריתמטיות של קידום ב-k מקדמת את הפוינטר ב k*sizeof(type) בתים.

תיאור הזיכרון במחשב var1 var2 var3 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 כתובת של משתנה הינה הכתובת של הבית הראשון ברצף הבתים שמשתנה זה תופס בזיכרון.  ברגע שנצהיר על משתנים כמצביעים, נוכל להציב לתוכם כתובות של משתנים. לדוגמא:   int x = 200 ; int p; /*Declaration of a pointer “p” that is of type “int *”.*/ p=&x; /*Assign the address of “x” to be the value of “p”.*/ בשלב זה המשתנה p מכיל את הכתובת של המשתנה x.

תאור סכמתי של הזיכרון 200 466675 x p 466680 466679 466678 466677 466676 466675 int x = 200 ; int p; /*Declaration of a pointer “p” that is of type “int *”.*/ p=&x; /*Assign the address of “x” to be the value of “p”.*/ בשלב זה המשתנה p מכיל את הכתובת של המשתנה x. הערה: לשם פשטות מתואר המצב בציור בו לטיפוס המצביע ולמשתנה int מוקצה לכאורה בית אחד בלבד אולם במציאות דרושים מספר בתים לייצג שני טיפוסים אלה.

דוגמאות int x = 200 ; int p; /*Declaration of a pointer “p” that is of type “int *”.*/ p=&x; /*Assign the address of “x” to be the value of “p”.*/ printf(“%p”, p) ;//print the address of p printf(“%d”, *p) ;//print 200 p=500; // < שקול= => x=500;. זהירות בעת שימוש במצביעים: כשמצהירים על מצביע p אין הוא בהכרח מצביע על ערך חוקי! int *pi; *pi = 100; /*wrong!!!! to correct : do before this operation : ” pi = <concrete address>; ” */ טעות זו ידועה בשם segmentation fault ,זוהי אינה שגיאת קומפילציה, אלא, שגיאה בזמן ריצה. מצביעים למצביעים - כאמור מצביע הוא משתנה המכיל כתובת של משתנה. אותו משתנה מוצבע יכול עקרונית גם הוא להיות מצביע. לדוגמה נוכל להוסיף לקוד: int **p2=&p printf(“%d”,**p2);//print 200

העברת מצביעים כפרמטרים לפונקציה מכיוון שמצביעים הם משתנים לכל דבר, ניתן להעביר את ערכיהם בתור פרמטרים לפונקציות - מנגנון זה מאפשר לפונקציה נקראת לשנות את ערכיהם של משתנים בסביבה הקוראת (מאותה סיבה מצביעים יכולים להיות גם ערך החזרה של פונקציה). לדוגמא ,נכתוב פונקציה אשר מחליפה בין ערכיהם של זוג משתנים מטיפוס int: void swap(int a, int b){ int tmp = a; a = *b; *b = tmp; } כתובת של מערך שמו של מערך דומה למצביע המייצג את הכתובת של האיבר הראשון בתוך המערך. על אף הדמיון הרב, קיים גם שוני בכך שמערך מייצג כתובת קבועה, שאינה ניתנת לשינוי במהלך התוכנית, בניגוד למצביע.

דוגמא נוספת #include <stdio.h> void main(){ int x , *px; /*Define a variable of type int named "x" and a variable of type pointer to int named "px".*/ int y, *py ; px=&x; /*Assign the address of "x" to be the value of "px".*/ py=&y; scanf("%d%d",px,py); /*Read two integers values from the input and assign them to "x" and "y". Make sure that you understand why!!! */ printf("x=%d , y=%d\n",*px,*py); /*Print the values of the x and y which are the variables pointed to by px and py. */

תרגיל 1 שאלה 1: נתונה השורה הבאה של תכנית בשפת C: int a, *b, c[4]; סמנו את כל ההוראות שאינן יכולות להופיע באופן חוקי בהמשך התכנית. הוראה חוקית היא הוראה נכונה מבחינה תחבירית שעוברת קומפילציה.   א) *(c+3) = 8; //same as c[3]=8 ב) a = *(c + *b); ג) *(c++) = 12; ד) c = b; ה) b = c; ו) a = (*c)++; ז) *(b+1) = (*c)++; ח) a = *b - *c; ט) *c = *(b++); י) *(b++)= *(&a); יא) *b==2=a; יב) c[3] = *b == 2;

תרגיל 1 נתונה השורה הבאה של תכנית בשפת C: int a, *b, c[4]; סמנו את כל ההוראות שאינן יכולות להופיע באופן חוקי בהמשך התכנית. הוראה חוקית היא הוראה נכונה מבחינה תחבירית שעוברת קומפילציה.   א) *(c+3) = 8; //same as c[3]=8 ב) a = *(c + *b); ג) *(c++) = 12; /*Compilation Error: error C2105: '++' needs l-value*/ ד) c = b; /*Compilation Error: error C2106: '=' : left operand must be l-value*/ ה) b = c; ו) a = (*c)++; ז) *(b+1) = (*c)++; ח) a = *b - *c; ט) *c = *(b++); י) *(b++)= *(&a); יא) *b==2=a; /*Compilation Error: error C2106: '=' : left operand must be l-value*/ יב) c[3] = *b == 2;

עדכון מספר ערכים ראינו כבר כי ע"י שימוש בפונקציה ניתן לקבל ערך החזרה יחיד בלבד. לדוגמה: רוצים לחשב שני שורשים של משוואה ריבועית. דרך ראשונה לחשב זאת ע"י קריאות מתאימות לפונקציה: #include<stdio.h> #include<Math.h> double roots(double a,double b,double ,int discriminant_sign){ double discriminant = sqrt(b*b-4*a*c); discriminant *=discriminant_sign; return (b+discriminant)/(2*a); } void main(){ double root1 = roots(2,-6,4, 1); double root2 = roots(2,-6,4, -1); כאשר 1 מסמל חישוב שורש עם דיסקרימיננטה חיובית ו-(-1) עם דיסקרימיננטה שלילית.

תרגיל 2 כתוב אותה פונקציה כך שהשורשים נשלחים כארגומנטים ומתעדכנים בפונקציה עצמה

פתרון ע"י שימוש במצביעים void roots(double a,double b,double c,double *pz1,double *pz2){ double discriminant = sqrt(b*b-4*a*c); *pz1=(-b+discriminant)/(2*a); *pz2=(-b-discriminant)/(2*a); } void main(){ double z1,z2; roots(2,-6,4,&z1,&z2); printf(“roots: %f %f\n”,z1,z2); דרך זו עדיפה מכיוון שמבנה הפונקציה פשוט וטבעי יותר וגם הקריאה לפונקציה מתבצעת פעם אחת בלבד. במקרה זה אמנם עידכנו סה"כ 2 משתנים אבל יתכנו מקרים בהם נרצה לעדכן מספר רב יותר של משתנים ואז הדבר יהיה משמעותי יותר

תרגיל 3 כתוב פונקציה char * strstr(char *st1, char *st2) שמקבלת כארגומנט שתי מחרוזות. במידה שהמחרוזת st2 מוכללת במחרוזתst1 , הפונקציה מחזירה מצביע לתו של st1 שממנו מתחילה המחרוזת הזאת, אחרת הפוקציה מחזירה NULL.

תרגיל 3 #include <stdio.h> #include <stdlib.h> char * iterStrstr(char *st1, char *st2){ char *p1,*p2; int len2 = strlen(st2); int len1 = strlen(st1); while (len1>=len2){ for(p1=st1,p2=st2;*p2 && *p1==*p2;p1++,p2++); if(!*p2) return st1; st1++; len1--; } return NULL;

המשך תרגיל 3 void main(){ char st1[]="bnbnbacdhghg",st2[]="acd"; char *c; if(c= strstr(st1,st2)) printf ("The first character in both st2 and st1 is %c\n ",*c); else printf ("Sorry, st2 isn't contained in st1!!!\n"); {

המשך רקורסיה

דוגמה מס' 1 הגדרה: פלינדרום הוא רצף תווים שניתן לקרוא משני הכיוונים ללא שינוי בתוצאה. דוגמאות: "ABBA", "ABA", "לבלבל". כתוב פונקציה רקורסיבית המקבלת מחרוזת ואת אורכה, ומחזירה TRUE אם המחרוזת היא פלינדרום, FALSE אם אינה. (נניח כי FALSE מוגדר כ-0 ו- TRUE מוגדר כ-1)

דוגמה מס' 1 int is_pal(char s[], int n,int index) { if (n<2) return TRUE; return (s[index] == s[index + n-1]) && is_pal(s, n-2,index+1); }

דוגמה מס' 1- מחסנית ריצה: void main() { is_pal("ABBA",4); T T T הערה: הערך האמיתי של s הוא למעשה "BBA", אך מאחר שאורך המחרוזת מוכרז כ-2, ניתן להתייחס אל s כ- “BB” . is_pal("ABBA",4); s=“ABBA” , n=4 if (n<2) return TRUE; return (s[0] == s[n-1]) && is_pal(s+1,n-2); s=“BB” , n=2 if (n<2) return TRUE; return (s[0] == s[n-1]) && is_pal(s+1,n-2); s=“” , n=0 if (n<2) return TRUE; return (s[0] == s[n-1]) && is_pal(s+1,n-2); T T T

דוגמה מס' 2 – harmonic number ברצוננו לכתוב פונקציה רקורסיבית rec_harmonic_num(n) אשר תדפיס למסך את הפיתוח של h(n). למשל, עבור הקריאה rec_harmonic_num(5), נקבל על המסך: 1/5+1/4+1/3+1/2+1=2.28

תזכורת – משתנים סטאטיים מגדירים באופן הבא: static int var; ניתן לאתחל בשורת ההגדרה בלבד כאשר חייבים לאתחל בביטויי קבוע!!! אם אנחנו לא מאתחלים אותם, המערכת תאתחל אותם אוטומטית לאפס. הscope- של משתנים סטאטיים הוא רק הפונקציה שבה הם הוגדרו אך הם ממשיכים להתקיים גם אחרי שביצוע הפונקציה נגמר (למעשה עד סוף ריצת התוכנית). לפיכך הערך של משתנה סטאטי בפונקציה כלשהי נשמר בין קריאות עוקבות לפונקציה זו.

דוגמה מס' 2– harmonic number void rec_harmonic_sum(int n) { static double sum=1; if(n==1)/*Stop condition*/ printf("1=%.2f\n",sum); sum=1; /*Must initialize the static variable "sum" so that we can call this function repeatedly in the same program run. Make sure you understand why!!! */ return; } printf("1/%d+",n); sum+=1.0/n; /*The "1." syntax is used in order to prevent automatic casting to integer type (so that the expression "1/n" will be evaluated as type double). */ rec_harmonic_sum(n-1); /*The recursive call. */

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

תרגיל מס' 1 – abc void abcInvokingFun(char word[],int lengthOfWord){ למשל: עבור התוכנית הבאה: 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

תרגיל מס' 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'; }

תרגיל מס' 1 – abc נראה את תרשים הקריאות המתקבל מהרצת הדוגמא שלמעלה (בדומה לתרשים הקריאות שראינו בכתה): כל ריבוע מייצג קריאה לפונקציה, כאשר בתוך כל ריבוע מופיע תוכן המערך שהפונקציה מקבלת. ערך הפרמטרcurPlace הוא משותף לכל רמה בתרשים ומצויין בצד ימין. 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 הדפסה: aa? ab? ac? ba? bb? bc? ca? cb? cc? a?? b?? c?? ??? curPlace=0 curPlace=1 curPlace=2 curPlace=3

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

תרגיל מס' 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 {

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

תרגיל מס' 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) return FindNOccurence (s, ch, n – 1, index + 1); return FindNOccurence(s, ch, n, index + 1); {

תרגיל מס' 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 עבור פרמטר חיובי ועבור פרמטר שלילי? כתוב פונקציה זו מחדש בצורה לא רקורסיבית.

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

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

תרגיל מס' 5 int what(int a, int b){ if(!a && !b) return 1; עיין בפונקציה הבאה: 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!. אף לא אחת מהתשובות לעיל.

פתרון: 5 (למשל כאשר a=0 וגם b=0)

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

תרגיל מס' 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.

תרגיל מס' 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); } טיפול באפשרות א' טיפול באפשרות ב'

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

תרגיל מס' 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); }

תרגיל מס' 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.

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

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

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

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

פתרון תרגיל מס' 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++) puts("*"); blank++; X(lines-2); blank--; for (i=0; i<blank; i++) putchar(' '); }