Download presentation
Presentation is loading. Please wait.
Published bySusanti Rachman Modified over 5 years ago
1
ICS103 Programming in C Lecture 11: Recursive Functions
2
Outline Tracing Recursive Functions
Introducing Recursive Functions Format of Recursive Functions Tracing Recursive Functions Tracing Recursive Functions using Recursive Trees How Recursive Functions work (Optional) Some Common Errors in Writing Recursive Functions
3
Introducing Recursive Functions
We have seen so far that a function, such as main, can call another function to perform some computation. In C, a function can also call itself. Such types of functions are called recursive functions. A function, f, is also said to be recursive if it calls another function, g, which in turn calls f. In this case f and g are called mutually recursive functions. Many mathematical functions are defined recursively. For example, the factorial function is defined mathematically as: Although recursive functions are usually less efficient than iterative functions (functions that use loops) due to overhead in function calls, in many cases, recursive functions provide more natural and simple solutions. Thus, recursion is a powerful tool in problem solving and programming. n (n-1)! , n > 0 1, n = 0 n! =
4
Introducing Recursive Functions (cont’d)
Problems that can be solved using recursion have the following characteristics: One or more simple cases of the problem that have a direct and easy answer – also called base cases. Example: 0! = 1. The other cases can be re-defined in terms of a similar but smaller problem - recursive cases. Example: n! = n (n-1)! By applying this re-definition process, each time the recursive case(s) will move closer and eventually reach the base case(s). Example: n! (n-1)! (n-2)! !, 0!. The strategy in recursive solutions is called divide-and-conquer. The idea is to keep reducing the problem size until it reduces to the simple case which has an obvious solution.
5
Format of recursive Functions
Recursive functions generally involve an if statement with the following form: if this is a simple case solve it else redefine the problem using recursion The if-branch is the base case, while the else-branch is the recursive case. The recursive step provides the repetition needed for the solution and the base step provides the termination. Note: For the recursion to terminate, each recursive case must be moving closer to a base case with each recursive call.
6
Example 1: Recursive Factorial
The following shows the recursive and iterative versions of the factorial function: Recursive version long int factorial (int n) { if (n == 0) return 1; else return n * factorial (n-1); } Iterative version long int factorial (int n) { int i, product=1; for (i=n; i>1; --i) product=product * i; return product; } Recursive Call
7
The complete recursive multiply example
/* Computes the factorial of a number */ #include <stdio.h> long int factorial(int n); /* shows how to call a user-define function */ int main(void) { int num, fact; printf("Enter an integer between 0 and 7: "); scanf("%d", &num); if (num < 0) printf("Factorial not defined for negative numbers\n"); else if (num <= 7) { fact = factorial(num); printf("The factorial of %d is %d\n", num, fact); } else printf("Number out of range: %d\n", num); system("pause"); return 0; } /* Computes n! for n greater than or equal to zero */ long int factorial (int n) { if (n == 0) //base case return 1; else return n * factorial (n-1); //recursive //case }
8
Tracing Recursive Functions
Executing recursive algorithms goes through two phases: Expansion in which each recursive step is applied until reaching a base step “Substitution” in which the solution is constructed backwards starting with the base step(s) factorial(4) = 4 * factorial (3) = 4 * (3 * factorial (2)) = 4 * (3 * (2 * factorial (1))) = 4 * (3 * (2 * (1 * factorial (0)))) = 4 * (3 * (2 * (1 * 1))) = 4 * (3 * (2 * 1)) = 4 * (3 * 2) = 4 * 6 = 24 Expansion phase Substitution phase
9
Example 2: Multiplication
Suppose we wish to write a recursive function to multiply an integer m by another integer n using addition. [We can add, but we only know how to multiply by 1]. The best way to go about this is to formulate the solution by identifying the base case and the recursive case. The base case is if n is 1. The answer is m. The recursive case is: m*n = m + m (n-1). m + m (n-1), n>1 m, n = 1 m*n
10
Example 2: Multiplication (cont’d)
#include <stdio.h> int multiply(int m, int n); int main(void) { int num1, num2; printf("Enter two integer numbers to multiply: "); scanf("%d%d", &num1, &num2); printf("%d x %d = %d\n", num1, num2, multiply(num1, num2)); system("pause"); return 0; } int multiply(int m, int n) { if (n == 1) return m; /* simple case */ else return m + multiply(m, n - 1); /* recursive step */
11
Example 2: Multiplication (cont’d)
Expansion phase multiply(5,4) = 5 + multiply(5, 3) = 5 + (5 + multiply(5, 2)) = 5 + (5 + (5 + multiply(5, 1))) = 5 + (5 + (5 + 5)) = 5 + (5 + 10) = = 20 Substitution phase
12
Example 3: Power function
Suppose we wish to define our own power function that raises a double number to the power of a non-negative integer exponent. xn , n >= 0. The base case is if n is 0. The answer is 1. The recursive case is: xn = x * xn-1. x * x n-1, n>0 1, n = 0 xn
13
Example 3: Power function (cont’d)
#include <stdio.h> double pow(double x, int n); int main(void) { double x; int n; printf("Enter double x and integer n to find pow(x,n): "); scanf("%lf%d", &x, &n); printf("pow(%f, %d) = %f\n", x, n, pow(x, n)); system("pause"); return 0; } double pow(double x, int n) { if (n == 0) return 1; /* simple case */ else return x * pow(x, n - 1); /* recursive step */
14
Example 4: Fibonacci Function
Suppose we wish to define a function to compute the nth term of the Fibonacci sequence. Fibonacci is a sequence of number that begins with the term 0 and 1 and has the property that each succeeding term is the sum of the two preceding terms: Thus, the sequence is: 0, 1, 1,2,3,5,8,13,21,34 … Mathematically, the sequence can be defined as: fib(n-1) + fib(n-2) n>1 n, n = 0, 1 fib(n)
15
Example 4: Fibonacci Function (cont’d)
#include <stdio.h> int fib(int n); int main(void) { int n; printf("Enter an integer n to find the nth fibonacci term: "); scanf("%d", &n); printf("fibonacci(%d) = %d\n", n, fib(n)); system("pause"); return 0; } int fib(int n) { if (n == 0 || n== 1) return n; /* simple case */ else return fib(n-1) + fib(n-2); /* recursive step */
16
Tracing using Recursive Tree
Another way to trace a recursive function is by drawing its recursive tree. Example: The recursive tree for fib(6) is:
17
Tracing using Recursive Tree (cont’d)
Write the recursive tree generated by the call display(3) in the program below and hence determine the program output: #include <stdio.h> void display(int); int main(void){ display(3); system("pause"); return 0; } void display(int n){ if(n > 0){ printf("RIYADH\n"); display(n - 1); printf("DAMMAM\n"); The blue numbers indicate the order in which the strings are printed. Hence the output is: RIYADH DAMMAM
18
Example 5: Mutually recursive functions
The following are mutually recursive functions to determine whether a positive integer is even or odd: int isEven(int n) { if (n==0) return 1; else return(isOdd(n - 1)); } int isOdd(int n) { return (! isEven(n)); Exercise: Draw the recursive tree for: isEven(2) isEven(3) isOdd(5) 2. Write a main method to test the above mutually recursive functions
19
Example 6: Mutually recursive functions (cont’d)
Another example of mutually recursive functions: 19
20
Example 6 (cont’d) double sin(double x){ if(x < 0.0000001)
return x - (x*x*x)/6; else{ double y = tan(x/3); return sin(x/3)*((3 - y*y)/(1 + y*y)); } double tan(double x){ return sin(x)/cos(x); double cos(double x){ double y = sin(x); return sqrt(1 - y*y); 20
21
How Recursive Methods work (Optional)
Recursion is implemented using a data structure called the stack. A data structure is a grouping of related data items in memory [In this course, we will study two data structures: One-dimensional arrays and Two-dimensional arrays]. A stack is a data structure in which data is added and removed at only one end called the top. To add (push) an item to the stack, it must be placed on the top of the stack. To remove (pop) an item from the stack, it must be removed from the top of the stack too. Thus, the last element that is pushed into the stack, is the first element to be popped out of the stack. i.e., A stack is a Last In First Out (LIFO) data structure 21
22
A stack example (Optional)
2 8 1 7 8 1 7 2 top 1 7 2 top Push(8) Push(2) pop() 8 1 7 2 1 7 2 top 7 2 top pop() pop() 22
23
How Recursive Methods work (Optional)
Part of the RAM memory allocated to a program is a stack data structure called the run-time stack. When a function is called a data structure called an Activation Record is created and pushed at the current top of the run-time stack. It contains: The values of the parameters. The values of the local variables. The return address (The address of the statement after the call statement). The previous activation record address. A location for the return value, if any, of the activation record. When a function returns: The return value, if any, of its activation record is passed to the previous activation record or it is passed to the calling statement if there is no previous activation record. The Activation Record is popped entirely from the run-time stack. Recursion is handled in a similar way. Each recursive call creates a separate Activation Record. As each recursive call completes, its Activation Record is popped from the run-time stack. Ultimately control passes back to the original calling statement. 23
24
Computing Factorial factorial(0) = 1; factorial(n) = n*factorial(n-1);
The following animation is taken from Introduction to Java Programming, 6th edition by Liang factorial(0) = 1; factorial(n) = n*factorial(n-1); 5/20/2019
25
Trace Recursive factorial
animation Trace Recursive factorial Executes factorial(4) 5/20/2019
26
Trace Recursive factorial
animation Trace Recursive factorial Executes factorial(3) 5/20/2019
27
Trace Recursive factorial
animation Trace Recursive factorial Executes factorial(2) 5/20/2019
28
Trace Recursive factorial
animation Trace Recursive factorial Executes factorial(1) 5/20/2019
29
Trace Recursive factorial
animation Trace Recursive factorial Executes factorial(0) 5/20/2019
30
Trace Recursive factorial
animation Trace Recursive factorial returns 1 5/20/2019
31
Trace Recursive factorial
animation Trace Recursive factorial returns factorial(0) 5/20/2019
32
Trace Recursive factorial
animation Trace Recursive factorial returns factorial(1) 5/20/2019
33
Trace Recursive factorial
animation Trace Recursive factorial returns factorial(2) 5/20/2019
34
Trace Recursive factorial
animation Trace Recursive factorial returns factorial(3) 5/20/2019
35
Trace Recursive factorial
animation Trace Recursive factorial returns factorial(4) 5/20/2019
36
factorial(4) Stack Trace
5/20/2019
37
Some Common Errors in Writing Recursive Functions
1. The function does not call itself directly or indirectly. 2. Non-terminating Recursive Functions (Infinite recursion): (a) No base case. (b) The base case is never reached for some parameter values. int badFactorial(int x) { return x * badFactorial(x-1); } int anotherBadFactorial(int x) { if(x == 0) return 1; else return x*(x-1)*anotherBadFactorial(x - 2); // When x is odd, we never reach the base case!! } 37
38
Some Common Errors in Writing Recursive Functions (cont’d)
3. Using a local variable (a variable declared within a function) wrongly to accumulate the result of a recursion int sumFirst_n_PositiveInts(int n) { int sum = 0; if(n == 1) return sum; else{ sum = sum + n; return sumFirst_n_PositiveInts(n – 1); } In the above example, the local variable sum is reinitialized to zero in each recursive call (each call has its own sum). Hence, the returned value is 0. One correct solution is: int sumFirst_n_PositiveInts(int n) { if(n == 1) return n; else return n + sumFirst_n_PositiveInts(n – 1); } 38
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.