320 likes | 417 Vues
Lecture 25. What will I learn in this lecture?. Recursive Functions Related Chapter: ABC 5.14, 5.15. Recursive Functions.
E N D
What will I learn in this lecture? • Recursive Functions • Related Chapter: ABC 5.14, 5.15
Recursive Functions Recursive functions are defined in terms of themselves; i.e., a function is recursive if it contains calls back to itself or to another function that calls the original function. Recursive programming usually requires more memory and runs more slowly than non-recursive programs. This is due to the cost of implementing the “stack”. However, recursion is often a natural way to define a problem.
Example - Factorial Function 1. Problem Definition Write a factorial function. 0! = 1 and n! = n*(n-1)!. Use “recursion” to implement the factorial function. 2. Refine, Generalize, Decompose the problem definition (i.e., identify sub-problems, I/O, etc.) Input = Non-negative integers as input. Output= return the factorial of the input value. Note: that 69! = 1.711224524... x 1098 so our function will only work for small integer values. It would be better to return a value of data-type double (Why?)
Example - (Recursive) Factorial Function double fact(int n) { if (n ==0) /* Voom! */ return 1.0; else return (n * fact(n - 1)); /* recursive call */ } In main we can call the factorial function by the following command: printf("%lf", fact(3)); or by (if x is of data-type int and y is of data-type double) : x = 3; y = fact(x);
(STACK) return 1.0;(n = 0)return ( 1 * fact(0));(n = 1) return ( 2 * fact(1)); (n = 2) return ( 3 * fact(2)); (n = 3) (push) return ( 3 * fact(2)); (n = 3) (push) return ( 2 * fact(1)); (n = 2) (push) return ( 1 * fact(0)); (n = 1) (push/pop) return 1.0; (n = 0) (pop) return ( 1 * 1.0); (n = 1) (pop) return ( 2 * 1.0); (n = 2) (pop) return ( 3 * 2.0); (n = 3) return ( 1 * 1.0); (n = 1) return ( 2 * fact(1)); (n = 2) return ( 3 * fact(2)); (n = 3) return ( 2 * 1.0); (n = 2) return ( 3 * fact(2)); (n = 3) return ( 3 * 2.0); (n = 3) printf("%lf", fact(3) );
Example - Traversing a Maze 1. Problem Definition Write a solve_maze function. Read the maze from a file, “maze.txt” and display one path that traverses the maze. Use “recursion” to implement the maze function. 2. Refine, Generalize, Decompose the problem definition (i.e., identify sub-problems, I/O, etc.) Input = The file “maze.txt” contains the following *********** ***** * *O* * * **** * *X** ** *** ** * ** *** ** ** * *********** Your program will have to find its way through a 10x10 maze where the symbols “ * ” , “O” and “X” denote: “ * ” are solid walls through which you cannot travel "O" denotes the starting position, "X" is the exit for which you are looking for
Example - Traversing a Maze 2. Refine, Generalize, Decompose the problem definition Input (continued): Read the maze into the 2D array, *********** ***** * *O* * * **** * *X** ** *** ** * ** *** ** ** * *********** char maze[NUMROWS][NUMCOLS]; where NUMROWS and NUMCOLS are constants with value 10. The upper left-handcorner of the maze has the value maze[0][0] . If we want to test whether the cell in the fourth row and fourth column contains a wall then it's enough to use an if statement like this: if (maze[3][3] == ‘ * ')
Example - Traversing a Maze 2. Refine, Generalize, Decompose the problem definition Output : Display a solution as follows: *********** OOOOO*****O* *O* * *O* **** *O* *X** **O***O** OO *O** ***O**O** * OOOO***********
Example - Traversing a Maze 3. Develop Algorithm (processing steps to solve problem) Step 1 Read in the “maze” and find the starting Row and Column (the position of the “O”). Use variables “curRow” and “curCol” to keep track of the current position as we traverse through the maze (the 2D matrix “maze”). Step 2 Display the maze. Step 3 Check to see if current position is an “X” then we are done. Otherwise first try to go up and if not then down and if not then left and if not then right and if you can go up/down/left/right then go back to Step 2. Otherwise, go back to the previous position [curRow,curCol] and try another direction.
#include <stdio.h> #include <stdlib.h> #define NUMROWS 10 #define NUMCOLS 10 /* prototypes */ int read_maze (char[][], int *, int *); void display_maze (char[][]); void solve_maze (char[][], int, int);
void main (void) { int startRow, startCol; /* Starting point in maze. */ char maze[NUMROWS][NUMCOLS]; /* Stores maze read from input file. */ if (read_maze(maze, &startRow, &startCol) == 0) { printf ("Error reading maze from file maze.txt!\n"); return; } solve_maze(maze, startRow, startCol); } /*end of main */
void solve_maze(char maze[][NUMCOLS], int curRow, int curCol){ int i; display_maze(maze); /* Check if solution found. */ if ((maze[curRow - 1][curCol] == 'X') || (maze[curRow + 1][curCol] == 'X') || (maze[curRow][curCol + 1] == 'X') || (maze[curRow][curCol - 1] == 'X')) exit (0); /* Recurse in each possible direction that is empty. */ /* Move up */ if (maze[curRow - 1][curCol] == ' ') { maze[curRow - 1][curCol] = 'O'; solve_maze(maze, curRow - 1, curCol); maze[curRow - 1][curCol] = ' '; } /* continued on next slide */
/* Move down */if (maze[curRow + 1][curCol] == ' ') { maze[curRow + 1][curCol] = 'O'; solve_maze (maze, curRow + 1, curCol); maze[curRow + 1][curCol] = ' '; }/* Move left */if (maze[curRow][curCol - 1] == ' ') { maze[curRow][curCol - 1] = 'O'; solve_maze (maze, curRow, curCol - 1); maze[curRow][curCol - 1] = ' '; } /* Move right */if (maze[curRow][curCol + 1] == ' ') { maze[curRow][curCol + 1] = 'O'; solve_maze (maze, curRow, curCol + 1); maze[curRow][curCol + 1] = ' '; } usleep (600000); return; }
/* Display the maze passed as a parameter to standard output. */ void display_maze (char maze[ ][NUMCOLS]) { int i, row, col; for (row = 0; row < NUMROWS; row++) { for (col = 0; col < NUMCOLS; col++) { printf ("%c", maze[row][col]); } printf ("\n"); } usleep (600000); printf ("\n"); }
int read_maze (char maze[ ][NUMCOLS], int *sRow, int *sCol){ FILE *fpMaze; int row, col; char endofline; /* end of line character */ /* Open maze text file, make sure it opens OK. */ if ((fpMaze = fopen ("maze.txt", "r")) == NULL) return 0; for (row = 0; row < NUMROWS; row++) /* Loop through the rows. */ { for(col=0;col<NUMCOLS;++col) /* Loop through columns */ { fscanf(fpMaze,"%c",&maze[row][col]); if (maze[row][col] == 'O') /*Check if this is the starting position.*/ { *sRow = row; *sCol = col; } } /* end of for(col=... loop */ fscanf(fpMaze,"%c",&endofline); } /* end of for(row=... loop */ fclose(fpMaze); return 1; }
Towers of Hanoi • According to legend, in the great temple of Benares, beneath the dome which marks the center of the world, rests a brass plate on which are fixed three diamond needles. On one of these needles at creation, there were placed 64 discs of pure gold, the largest disc resting on the brass plate and the others getting smaller up to the top one. This is the TOWERS OF HANOI. Day and night, the people on duty move the discs from one needle to another, according to the two following laws: • Law 1: Only one disc at a time may be moved. • Law 2: A larger disc may never rest on a smaller disc. • The workers labor in the belief that once the tower has been transferred to another needle there will be heaven on earth, so they want to complete the task in the least number of moves.
Towers of Hanoi Actually, the Tower of Hanoi puzzle was invented in 1883 by the French mathematician Edouard Lucas (1842-1891), who made up the legend to accompany it.
Towers of Hanoi An elegant and efficient way to solve this problem is to think recursively. Suppose that you, somehow or other, have found the most efficient way possible to transfer a tower of n-1 disks one by one from one pole to another obeying the restriction that you never place a larger disk on top of a smaller one. Then, what is the most efficient way to move a tower of n disks from one pole to another?
Pseudo-code Assume we know how to move n-1 disks from one peg to another.Then can we move n disks from peg 1 to peg 3 ? 1. Move n-1 disks from peg 1 to peg 2, peg 3 is just a temporary holding area 2. Move the last disk(the largest) from peg 1 to peg 3 3. Move the n-1 disks from peg 2 to peg 3, peg 1 is just a temporary holding area. 1 2 3 n disks
Example - Animation 1 2 3 Click on Picture to start game
Example - Towers of Hanoi /* function prototype*/ void hanoi( int origin, int dest, int spare, int how_many); void main(void){ int how_many; printf("\n\tHow many disks initially on peg1? "); scanf(“%i”, &how_many); hanoi(1, 3, 2, how_many); }
Example - Towers of Hanoi void hanoi( int origin, int dest, int spare, int how_many) { if(how_many == 1) { printf(“\n\n\tMove top disk from peg %i to peg %i.”, origin, dest); return; } hanoi(origin, spare, dest, how_many - 1); printf("\n\n\t Move top disk from peg %i, to peg %i.\n ", origin, dest); hanoi(spare, dest, origin, how_many - 1) ; }
Computational Complexity Going back to the legend, suppose the workers rapidly move one disk every second. As shown earlier, the minimum sequence of moves must be : The minimum number of moves needed to transfer n-1 disks from peg2 topeg3 on top of the n th disk The minimum number of moves needed to transfer a tower of n disks from peg1 to peg3 The minimum number of moves needed to transfer n-1 disks from peg1 to peg2 The minimum number of moves needed to transfer the n th disk from peg1 to peg3 = + + Therefore, the recurrence relation is moves(n) = 2*moves(n-1) + 1 and initial case is moves(1) = 1 second. For example, moves(2) = 2*moves(1) + 1 = 3, moves(3) = 2*moves(2) + 1 = 7, or in general, moves(n) = 2n-1 Then, the time to move all 64 disks from one peg to the other, and end the universe would be moves(64) seconds or 584.9 billion years!!
Pointers to Functions Since the executable(program) for a function is located in memory it is logical that the value of function name is an address, that is, function names are pointers. Similarly we can define a pointer to a function in C.
Example - trapz Function 1. Problem Definition Write a function named trapz that computes the definite integral using trapezoids. 2. Refine, Generalize, Decompose the problem definition (i.e., identify sub-problems, I/O, etc.) Input = the trapz has five parameters, first: the function (an address), second: the lower limit of integration, third: the upper limit of integration. fourth: the relative tolerance (error) fifth: the largest length of a sub-interval
Example - trapz Function 3. Design---Develop Algorithm We will use the formula for the area of a trapezoid: area = ((b-a)/2)*(f(b)+f(a)) (area under solid red line) y=f(x) f(b) f(a) a b
Example - trapz Function 3. Design---Develop Algorithm We will use recursion. The algorithm: Find the midpoint between a and b and sum the area of the two trapezoids. If the difference between the area of the original (large) trapezoid minus the sum of the area of the two trapezoids is less than some specified tolerance then we are done. (Voom!) Else, call the trapz function for each of the two smaller trapezoids and sum the area of these two. y=f(x) a midpoint b
Example – trapz Function double trapz(double (*f)(double), double a , double b, double tol, double h) { double midpoint = (a+b)/2; double delta = (b-a)/2; double area = delta * (f(a)+f(b)); double area_left_half = delta * (f(a)+f(midpoint))/2; double area_right_half = delta * (f(midpoint)+f(b))/2; if (fabs(area-(area_left_half+area_right_half)) < tol && (b-a) < h ) return area_left_half + area_right_half; /* Voom!!! */ else return trapz(f,a,(a+b)/2,tol) + trapz(f,(a+b)/2,b,tol); } In main we can call the trapz function by the following command: printf("\n %lf \n", trapz(sin,0,3.14159265/2, 1.0e-12,.1));
Example – pointer to a function We defined a pointer named f, double (*f)(double); Note how this mirrors the prototype for sin, double sin(double);