Presentation is loading. Please wait.

Presentation is loading. Please wait.

Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Recursion.

Similar presentations


Presentation on theme: "Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Recursion."— Presentation transcript:

1 Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Recursion

2 2 Overview  What is recursion?  Examples of Recursive Function  Wrapper Function  Program Call Stack in Recursion  Inline recursive function?  Common Pitfalls in Recursion  When function declaration is necessary?

3 Department of Computer Science and Engineering, HKUST 3 Recursion What is recursion?

4 4  Recall that to write a function, we can call other functions to help us.  Recursion is: Call the function itself to help itself.  For example (computation of factorial - n!): factorial(n) = 1 * 2 * 3 * … * n If we know the value of factorial(n-1), then we can simply multiply it with n to produce factorial(n). However, we don’t know factorial(n-1) in hand.  We may call the function itself to compute factorial(n-1).

5 5 What is recursion? int factorial( int n ){ if ( n == 0 ) // base case return 1; else return n * factorial(n-1); // recursive case }

6 6 What is recursion? (Dry run the example)  Compute factorial(4): factorial(4) : return 4 * factorial(3) factorial(3) : return 3 * factorial(2) factorial(2) : return 2 * factorial(1) factorial(1) : return 1 * factorial(0) Here, factorial(0) will return 1, so factorial(1) : return 1 * 1// ie. return 1 factorial(2) : return 2 * 1// ie. return 2 factorial(3) : return 3 * 2// ie. return 6 factorial(4) : return 4 * 6// ie. return 24

7 7 What is recursion?  A recursion consists of at least two parts: Base case:  The problem is simple enough, we can solve it without other help.  Here, factorial(0) = 1, simple enough. Recursive case:  We don’t know how to solve the problem, say factorial(2).  So call the function itself with a smaller input and then combine that result to form the solution of the larger input.

8 Department of Computer Science and Engineering, HKUST 8 Recursion Examples of Recursive Function

9 9 Example 1  Compute the x y (y is a non-negative integer): // pre-condition: y >= 0 double exp(double x, int y){ if(y==0) return 1; return x * exp(x, y-1); }

10 10 Example 1 (Dry run)  Compute exp(3.2,3) exp(3.2, 3) returns 3.2 * exp(3.2, 2) exp(3.2, 2) returns 3.2 * exp(3.2, 1) exp(3.2, 1) returns 3.2 * exp(3.2, 0) exp(3.2, 0) returns 1 exp(3.2, 1) returns 3.2 * 1 // returns 3.2 exp(3.2, 2) returns 3.2 * 3.2// returns 10.24 exp(3.2, 3) returns 3.2 * 10.24// returns 32.768

11 11 Example 2  Write a recursive function that counts the number of zero digits in a non-negative integer.  Example: zeros(10200) returns 3. int zeros(int n){ if(n==0) return 1; if(n < 10) return 0; if(n%10 == 0) return 1 + zeros(n/10); else return zeros(n/10); }

12 12 Example 2 (Dry run)  Compute zeros(230400) Since 230400%10 == 0, zeros(230400) = 1 + zeros(23040) Since 23040%10 == 0, zeros(23040) = 1 + zeros(2304) Since 2304%10 != 0, zeros(2304) = zeros(230) Since 230%10 == 0, zeros(230) = 1 + zeros(23) Since 23%10 != 0, zeros(23) = zeros(2) Since 2 < 10 (and 2 != 0), zeros(2) = 0  Therefore, zeros(230400) = 3

13 13 Example 2 (Cont’)  We can have multiple base cases and multiple recursive cases. Here, we have 2 base cases and 2 recursive cases.

14 14 Example 3  Computation of Fibonacci number: int fibonacci( int n ){ if ( n <= 1 ) return 1; return fibonacci(n-1) + fibonacci(n-2); }  Note that a recursive function may call itself more than once.

15 15 Example 3 (Dry run)  Compute fibonacci(4): fibonacci(4) returns fibonacci(3) + fibonacci(2) fibonacci(3) returns fibonacci(2) + fibonacci(1) fibonacci(2) returns fibonacci(1) + fibonacci(0) fibonacci(1) and fibonacci(0) returns 1 fibonacci(2) returns 1+1// returns 2 fibonacci(3) returns 2 + 1// returns 3 fibonacci(4) returns 3 + 2// returns 5

16 16 Example 4  Linear Search: Objective: search a number (target) from an array. Idea:  Compare target with every number in the array.  Use left and right to indicate the range of array needs to be considered. int linearSearch( int array[], int left, int right, int target){ if ( left == right ) return NOT_FOUND; if ( array[left] == target ) return left; return linearSearch( array, left+1, right, target); } Call: linearSearch( array, 0, n, target); // array from 0 to (n-1)

17 17 Example 4 (Dry run 1)  Consider array = {2, 5, 6, 8, 9}, target = 8:  Initial Call = linearSearch( array, 0, 5, 8 ) Returns linearSearch( array, 1, 5, 8 ) Returns linearSearch( array, 2, 5, 8 ) Returns linearSearch( array, 3, 5, 8 ) linearSearch( array, 3, 5, 8 ) returns 3  Therefore, linearSearch( array, 0, 5, 8 ) returns 3 (the index of the location which contains target).

18 18 Example 4 (Dry run 2)  Consider array = {2, 5, 9, 8}, target = 11:  Initial Call = linearSearch( array, 0, 4, 11 ) Returns linearSearch( array, 1, 4, 11 ) Returns linearSearch( array, 2, 4, 11 ) Returns linearSearch( array, 3, 4, 11 ) Returns linearSearch( array, 4, 4, 11 ), which returns NOT_FOUND  Therefore, linearSearch( array, 0, 4, 11 ) returns NOT_FOUND.

19 19 Example 4 (Cont’)  Recall that the input in the recursive calls must be smaller. Here the size of the effective considering range of the array is one less than the original input.

20 20 Example 5  Binary Search Objective: search a number (target) from a ascending sorted array. Idea:  Compare target with the middle element of the array.  If array[middle] is the same as target, you find it.  If array[middle] is larger than your target, then the second half of the array must not contain your target (since it is sorted).  discard it from further consideration.  Similar for another symmetric case.

21 21 Example 5 (Cont’) int bSearch( int array[ ], int left, int right, int target ){ if ( left > right ) return NOT_FOUND;// base case #1 int mid = (left + right) / 2;// integer division if ( target == array[mid] ) return mid; // base case #2 else if ( target < array[mid] ) return bSearch( array, left, mid-1, target ); // recursive #1 else return bSearch( array, mid+1, right, target ); // recursive #2 } Call: bSearch( array, 0, n-1, target ) // array from 0 to (n-1)

22 22 Example 5 (Dry run 1)  Consider array = {2, 5, 6, 8, 9}, target = 8:  Initial Call = bSearch( array, 0, 4, 8 ) mid = 2, array[2] = 6 < 8 = target So, returns bSearch( array, 3, 4, 8 ) mid = 3, array[3] = 8 = target So, bSearch( array, 3, 4, 8 ) returns 3  Therefore, bSearch( array, 0, 4, 8 ) returns 3.

23 23 Example 5 (Dry run 2)  Consider array = {2, 5, 6, 8, 9}, target = 7:  Initial Call = bSearch( array, 0, 4, 7 ) mid = 2, array[2] = 6 < 7 = target So, returns bSearch( array, 3, 4, 7 ) mid = 3, array[3] = 8 > 7 = target So, returns bSearch( array, 3, 3, 7 ) mid = 3, array[3] = 8 > 7 = target So, returns bSearch( array, 3, 2, 7 ), which returns NOT_FOUND  Therefore, bSearch( array, 0, 4, 7 ) returns NOT_FOUND.

24 Department of Computer Science and Engineering, HKUST 24 Recursion Wrapper Function

25 25 Wrapper Function  Sometimes, it is not easy to use the recursive function. Eg. bSearch( array, 0, n-1, target ) The user of this function must provides an “0” in the second argument.  Sometimes, we would like to provide a wrapper function to call this function for us. int bSearch( int array[ ], int size, int target ) { // do nothing, but only call recursive function // with appropriate arguments return bSearch( array, 0, size-1, target ); }  Sometimes, we even name the internal function (the recursive one) with another name, so that the user will not mistakenly call this function. For example, rename it as bSearch_internal, _bSearch, or something else. The user will only use bSearch. bSearch will call bSearch_internal for the user.

26 Department of Computer Science and Engineering, HKUST 26 Recursion Program Call Stack in Recursion

27 27 Program Call Stack  Recall that calling functions will put activation records on the program call stack.  Recursive function will call itself. Several activation records are correspond to the same recursive function. But they are corresponds to different instances of function invocations (or function calls).

28 28 Program Call Stack (Cont’)  Consider the following recursive function: double exp(double x, int y){ double result = 0;// line A if(y==0) result = 1; else result = x * exp(x, y-1);// line B return result;// line C }  Now, trace the invocation of exp(3.2, 2).

29 29 Program Call Stack (Cont’) main exp x = 3.2 y = 2 result = 0 (Line A) main exp x = 3.2 y = 2 result = 0 exp x = 3.2 y = 1 result = 0 (Line A) (Line B) main exp x = 3.2 y = 2 result = 0 exp x = 3.2 y = 1 result = 0 exp x = 3.2 y = 0 result = 0 (Line A) (Line B)

30 30 Program Call Stack (Cont’) main exp x = 3.2 y = 2 result = 0 exp x = 3.2 y = 1 result = 0 exp x = 3.2 y = 0 result = 1 (Line B) (Line C) main exp x = 3.2 y = 2 result = 10.24 (Line C) main exp x = 3.2 y = 2 result = 0 exp x = 3.2 y = 1 result = 3.2 (Line B) (Line C)

31 31 Program Call Stack in recursion  Some students misunderstood the concept of recursion because: They think that different instances of function cannot be co-exist, The value of the local variables are shared among all invocation of the functions.  Instead, these are not correct since Different invocations of the same function corresponds to different activation records in the program call stack. Local variables are stored in activation record, and hence are not shared among different invocation of the same function.

32 Department of Computer Science and Engineering, HKUST 32 Recursion Inline recursive function?

33 33 Inline Function  Recall that inlining a function can avoid the overhead of calling function.  However, based on the property of recursive function, C++ will not inline a recursive function. If it tries to inline a recursive function, it will copy the whole function to replace the recursive function, and then copy again and again … Infinite loop…   Therefore, even if you try to put the keyword inline in front of the function, it won’t inline recursive function.  inline keyword is only a hint (not a must) to the C++ compiler.

34 Department of Computer Science and Engineering, HKUST 34 Recursion Common Pitfalls in Recursion

35 35 Forget to write the base case int factorial( int n ){ //if ( n == 0 )// base case //return 1; // else return n * factorial(n-1);// recursive case }  This will loop infinitely: For example, factorial(0) will call factorial(-1), factorial(-10000) will call factorial(-10001), loops forever.

36 36 Inputs of recursive call does not change int factorial( int n ){ if ( n == 0 )// base case return 1; else return n * factorial(n);// recursive case }  This will also loop infinitely: Then, factorial(100) will call factorial(100), which will call factorial(100), … Some tricky questions in COMP251 says that this is not necessary to result in infinite loop.

37 37 Stack Overflow  Note that the program call stack is a limited resource. FYI: usually, we allocate 1MB or 2MB to stack, even on an 2GB RAM machine.  Recursive function will put many records on the stack.  easily to make it overflow.

38 38 Stack Overflow  Solutions: Minimize the use of local variables in recursive functions:  This can reduce the size of each activation record.  Note that local variables include function parameters (many students forget this). Don’t write recursive functions:  Will be covered in a minute. Implement a stack yourself:  Allocate a stack on the heap (2GB RAM machine will have ~2GB heap).  Push/pop the activation record (or simply the variable you needed to store) on/from the stack.

39 39 Stack Overflow (Solution 2) – Don’t write recursive functions  Many recursive function can be written in an iterative form. int factorial( int n ){ if ( n == 0 ) return 1; return n * factorial(n-1); } int factorial( int n ){ int result = 1; for (int i=2; i<=n; i++) result *= i; return result; }

40 40 Stack Overflow (Solution 2) – Don’t write recursive functions double exp(double x, int y){ if(y==0) return 1; return x * exp(x, y-1); } double exp(double x, int y){ double result = 1; for (int i=0; i<y; i++) result *= x; return result; }

41 41 Stack Overflow (Solution 2) – Don’t write recursive functions int zeros(int n){ if(n==0) return 1; if(n < 10) return 0; if(n%10 == 0) return 1 + zeros(n/10); else return zeros(n/10); } int zeros(int n){ int numZero = 0; do { if ( n%10 == 0 ) numZero += 1; n /= 10; } while ( n >= 10 ); return numZero; }

42 42 Stack Overflow (Solution 2) – Don’t write recursive functions  Convince yourself the following conversion is correct. int fibonacci( int n ){ if ( n <= 1 ) return 1; return fibonacci(n-1) + fibonacci(n-2); } int fibonacci( int n ){ int val[2]; val[0] = val[1] = 1; for (int i=2; i<=n; i++) val[i%2] = val[0] + val[1]; return val[n % 2]; }

43 43 Stack Overflow (Solution 2) – Don’t write recursive functions  Just as a practice, write the bSearch with the iterative method int linearSearch( int array[ ], int size, int target){ for (int i=0; i<size; i++) { if ( array[i] == target ) return i; } return NOT_FOUND; } int linearSearch( int array[], int left, int right, int target){ if ( left == right ) return NOT_FOUND; if ( array[left] == target ) return left; return linearSearch( array, left+1, right, target); }

44 44 Stack Overflow (Solution 2) – Don’t write recursive functions  It seems that all algorithms we have visited so far can be written as iterative. However, the iterative version is sometimes counter-intuitive.  The recursive version is usually simpler to understand. Not all recursive algorithms can be written as iterative (without using an additional stack).  You will learn more recursive algorithms, which may not be written in iterative form, in COMP171.

45 Department of Computer Science and Engineering, HKUST 45 Recursion When Function Declaration is necessary?

46 46 When Function Declaration is necessary?  Since function definition can act as function declaration, you can then move all function definitions to the front of the program, without any function declaration. It seems that it is not necessary to declare a function first.  However, considering the following scenario: int f( int x ) { if ( x == 0 ) return 0; return g( x – 1 ); } int g( int x ) { if ( x == 0 ) return 1; return f( x – 1 ); }  f(n) = 1 if n is an odd integer, otherwise it returns 0.

47 47 When Function Declaration is necessary?  However, the above code is not compliable. Note that function f calls the function g, but the compiler haven’t read the definition nor declaration of the function g. Similar result will be obtained even if you swap the order of the two functions definitions.  Solution: Declare the two functions first and then define them in any order. int f( int x ); int g( int x ); int f( int x ) { /*... */ } int g( int x ) { /*... */ }


Download ppt "Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Recursion."

Similar presentations


Ads by Google