290 likes | 305 Views
Recursion. A recursive function is a function that calls itself either directly or indirectly through another function. The problems that can be solved by recursion must have the following characteristics:
E N D
Recursion • A recursive function is a function that calls itself either directly or indirectly through another function. • The problems that can be solved by recursion must have the following characteristics: • - One or more simple cases of the problem have a straightforward, nonrecursive solution. • - The other cases can be redefined in terms of problems that are closer to the simple cases. • - By applying this redefinition process every time the recursive function is called, eventually the problem is reduced entirely to simple cases, which are relatively easy to solve.
Recursion • Recursive algorithm: • if this is a simple case • solve it • else • redefine the problem using recursion • Problem: Multiply 6 by 3. • The problem can be split into two problems: • - 1. Multiply 6 by 2. • - 2. Add 6 to the result of problem 1. • Now we can solve problem 2 because we know our addition tables. • But we cannot solve problem 1. Problem 1 is closer to the simple case than the original problem was. • The recursive approach splits one size-n problem into n size-1 problems.
Recursion • Problem: Multiply 6 by 3. • The problem can be split into two problems: • - 1. Multiply 6 by 2. • - 2. Add 6 to the result of problem 1. • We can split problem 1 into two problems: • - 1.1 Multiply 6 by 1 • - 1.2 Add 6 to the result of problem 1.1 • Problem 1.1 is the simple case. We can solve it because we know that any number multiplied by 1 gives us the original number. • Here, we assumed that we know our addition tables. We can solve problem 1.2. • So, problem1 is solved. • We have already solved Problem 2. • Therefore, we have solved the problem.
Recursive function multiply • // Performs integer multiplication using + operator. • // Pre: m and n are defined and n > 0 • int multiply ( int m, int n ) • { • int ans; • if (n == 1) • ans = m; // simple case • else • ans = m + multiply ( m, n -1 ); // recursive step • return (ans); • }
Count the # of vowels in a string • #include <stdio.h> • #include < string.h> • int count_vowel (char *input); • int main() • { • char line[] = “Hello”; • int num, count; • printf(“num of vowels are %d\n”, count_vowels(line); • return 0; • }
Count the # of vowels in a string • int count_vowels (char *string_ptr) • { • if (*str_ptr == ‘\0’) return 0; // simple case • else • { • switch (*string_ptr) • { // redefine problem using recursion • case ‘a’ : case ‘e’ : case ‘i’ : case ‘o’ : case ‘u’: • return (1 + count_vowels ( string_ptr + 1)); • break; • default : return count_vowels (string_ptr + 1); • break; • } • } • }
Tracing a Recursive Function Three activation frames corresponding to each call of the function are generated to solve the problem. m = 6, n = 3 3 == 1 is false ans is 6 + multiply(6, 2) return (ans) 18 • Multiply(6, 3) m = 6, n = 2 2 == 1 is false ans is 6 + multiply(6, 2) return (ans) 12 m = 6, n = 1 1 == 1 is true ans is 6 return (ans) 6
Function reverse_input_words • void reverse_input_words(int n) • { • char word[WORDSIZ]; • if (n <= 1) // simple case • { • scanf(“%s”, word); • printf(“%s\n”, word); • } • else // get and print the rest of the words • { // in reverse order and print it. • scanf(“%s”, word); • reverse_input_words(n - 1); • printf(“%s\n”, word); • } • }
Tracing a Void Recursive Function Three activation frames corresponding to each call of the function are generated to solve the problem. n = 3 ** 3 <= 1 is false scan “how” into word reverse_input_words(2) display “how” return • Reverse_input_words (3) A void function’s return occurs when the closing brace of the function body is encountered. n = 2 ** 2 <= 1 is false scan “are” into word reverse_input_words(1) display “are” return n = 1 ** 1 <= 1 is true scan “you” into word display “you” return ** word is undefined
Sequence of Events for the Function • Call reverse_input_words with n = 3. • Scan the first word (“How”) into word. • Call reverse_input_words with n = 2. • Scan the second word (“are”) into word. • Call reverse_input_words with n = 1. • Scan the third word (“you”) into word. • Display the third word (”you”). • Return from third call. • Display the second word (“are”). • Return from second call. • Display the first word (“How”). • Return from the original call.
Parameter and Local Variable Stack • C uses the stack data structure to keep track of the values of n and word at any given point. • In this structure, we add data items and remove them from the same end of the list. • The last item stored is the first processed. • When executes a call to the function, the system pushes the parameter value on the top of the parameter stack. • Pushes a new undefined cell on top of the stack maintained for the local variable word. • A return from the function pops each stack, removing the top value.
? 3 3 1 2 3 1 2 3 you are How ? are How Example How • After first call to reverse_input_words • The word “How” is stored in word just before the second call • After the second call. • The word “are” is scanned and stored in word just before the third call • After the third call 2 3 ? How are How 2 3
1 2 3 you are How Example • During the execution of the function, the word “you” is scanned and stored in word, and “you”is printed immediately because • n =1 • The function return pops both stacks. After the first return: • After the second return • The third and last return exits the original function call. So there is no longer memory allocated for n and word. 2 3 Are How How 3
System Stack • We have used separate stacks for each parameter in the example. • The compiler actually maintains a single system stack • Whenever a call to a function occurs, all its parameters and local variables are pushed onto the stack along with the memory address of the calling statement. • This address gives the computer the return point after execution of the function. • Multiple copies of a function’s parameters may be saved on the stack, only one copy of the function body is in memory.
Recursive factorial Function !1 = 1 !4 = 4 * !3 = 4 * 3 * !2 = 4 * 3 * 2 * !1 = 4 * 3 * 2 * 1 = 24 • // Compute n! using recursion • // Pre: n >= 0 • int factorial (int n) • { • int ans; • if (n == 0) // simple case • ans = 1; • else // redefine problem using recursion • ans = n * factorial (n - 1); • return (ans); • }
Recursive fibonacci Function Fibonacci sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, …. • // Compute the nth Fibonacci number using recursion • // Pre: n >= 0 • int fibonacci (int n) • { • int ans; • if (n == 1 || n == 2) // simple case • ans = 1; • else // redefine problem using recursion • ans = fibonacci(n - 2) + fibonacci (n - 1); • return (ans); • }
Towers of Hanoi • Towers of Hanoi problem involves moving a specified number of disks that are all different sizes from one tower (or peg) to another. • Problem: Move n disks from peg A to peg C using peg B as an intermediate peg. • Conditions: • 1. Only one disk at a time may be moved, and this disk must be the top disk on a peg. • 2. A larger disk cannot be placed on top of a smaller disk.
Tower of Hanoi Start 1 2 3 (B) (C) (A) Goal (A) (B) (C)
Tower of Hanoi 1 (B) (C) (A) Simple case: single disk. Just move the disk 1 from peg A to peg C
Tower of Hanoi Top (n - 1) disks 1 2 3 (B) (C) (A) Move the top (n -1) disks from peg A to peg B. (C) (A) (B)
Tower of Hanoi 1 (A) (B) (C) Move the bottom disk (1) from peg A to peg C. (C) (A) (B)
Tower of Hanoi Top (n - 1) disks (A) (B) (C) Move the top (n - 1) disks from peg B to peg C. (C) (A) (B)
Data Requirements • Problem Inputs: • int n • char from_peg • char to_peg • char aux_peg • Problem Outputs: • A list of instructions for moving n disks from from_peg to to_peg using aux_peg as an intermediate peg.
Algorithm • 1. if n is 1 then • 2. Move disk 1 from the from_peg to the to_peg. • else • 3. Move n - 1 disks from the from_peg to the auxiliary peg using the to_peg. • 4. Move disk n from the from_peg to the to_peg. • 5. Move n - 1 disks from the auxiliary peg to the to_peg using the from_peg.
Recursive Function Tower • // Displays instruction for moving n disks from from_peg to to_peg // using aux_peg as an intermediate peg. • void tower ( char from_peg, char to_peg, char aux_peg, int n) • { • if (n == 1) // simple case • printf(“Move disk 1 from peg %c to peg %c\n”, • from_peg, to_peg); • else // redefine the problem using recursion • { • tower(from_peg, aux_peg, to_peg, n -1); • printf(“Move disk %d from peg %c to peg %c\n”, • n, from_peg, to_peg); • tower(aux_peg, to_peg, from_peg, n - 1); • } • }
Output of tower Function • Output generated by tower (‘A’, ‘C’, ‘B’, 3): • Move disk 1 from A to C • Move disk 2 from A to B • Move disk 1 from C to B • Move disk 3 from A to C • Move disk 1 from B to A • Move disk 2 from B to C • Move disk 1 from A to C • n - disk problem requires 2n - 1 moves.
Recursion Vs. Iteration • Both iteration and recursion are based on a control structure: iteration uses a repetition structure and recursion uses a selection structure. • Both iteration and recursion involve repetition. Iteration explicitly uses a repetition structure and recursion achieves repetition through repeated function calls. • Iteration and recursion each involve a termination test. Iteration terminates when the loop-continuation condition fails. Recursion terminates when the base case is reached. • Both iteration and recursion can occur infinitely.An infinite loop can occurs with iteration if the loop-continuation test never becomes false. Infinite recursion occurs if the recursive step does not reduce the problem each time in a manner that converges on the base case.
Recursion Vs. Iteration • Recursion has many negatives. It repeatedly invokes the mechanism, and consequently the overhead, of function calls.This can be expensive in both processor time and memory space.. • Each recursive call causes another copy of the function (actually only the function’s variables) to be created. This can consume considerable memory. • Iteration normally occurs within a function so the overhead of repeated function calls and extra memory assignment is omitted. • So why choose recursion? • In many instances, the use of recursion enables us to specify a very natural, simple solution to a problem that would otherwise be very difficult to solve (e.g. Tower of Hanoi). • For this reason, recursion is an important and powerful tool in problem solving and programming.