16
Chapter 5: Recursion as a Problem-Solving Technique BACKTRACKING RECURSIVE GRAMMARS MATRIX OPERATIONS MATHEMATICAL INDUCTION RECURRENCE RELATIONS CS 240 1

# Chapter 5: Recursion as a Problem-Solving Technique BACKTRACKING RECURSIVE GRAMMARS MATRIX OPERATIONS MATHEMATICAL INDUCTION RECURRENCE RELATIONS CS 240

Embed Size (px)

Citation preview

Chapter 5: Recursion as a Problem-Solving Technique

BACKTRACKING

RECURSIVE GRAMMARS

MATRIX OPERATIONS

MATHEMATICAL INDUCTION

RECURRENCE RELATIONS

CS 240 1

CS 240 2

Recursion can be a very effective technique for solving what would otherwise be extremely elaborate problems.Sample recursive applications include: Backtracking algorithms.

Programming language definition. Matrix operations.

In addition, a solid understanding of recursion is helpful in analyzing the time complexity of algorithms.

Mathematical induction. Recurrence relations.

Recursion-related analysis techniques include:

CS 240 3

Recursive Backtracking Example:Flood Fill AlgorithmStarting with a “seed” pixel that’s inside a polygonal region, recursively visit the four adjacent pixels, coloring any that haven’t been colored, and that aren’t on the polygon’s boundary. Reaching the boundary is the recursion’s termination condition.

Seed pixelSeed pixel

Boundary pixelBoundary pixel

LRUD-visited pixelLRUD-visited pixel

void floodfill(int x, int y){ if (!filled(x,y)) { color(x,y); floodfill(x-1,y); // Left floodfill(x+1,y); // Right floodfill(x,y-1); // Up floodfill(x,y+1); // Down }}

CS 240 4

Recursive Grammar Example: A Calculator GrammarLanguages (e.g., programming languages) are often defined by their grammars, sets of recursive rules which provide syntax.program: END

expr_list ENDexpr_list: expression ;

expression ; expr_list

expression: term + expressionterm – expressionterm

term: primary / termprimary * termprimary

primary: NUMBERNAMENAME = expression- primary( expression )

Using this grammar, the following is a syntactically correct calculator

program:

pi = 3.1416; rad = 2.5; ht = 10; area = pi * rad * rad; surfacearea = 2 * (area + pi * rad * ht); END

Recursive grammars are also prominent in more sophisticated languages, making the following language features possible:• nested loops, conditionals, function calls• cascaded operators (<<, >>, =, etc.)• multiple cases in a switch statement

CS 240 5

Many matrix operations may be defined recursively, with combinations of submatrix operations used to implement the large matrix operation.

Note that a matrix’s determinant is used to determine, among other things, whether the matrix is invertible.

Recursive Matrix Operation Example: Determinants

CS 240 6

////////////////////////////////////////////// Class definition file: Matrix.h //// The matrix class has two data members: //// a square array of integer values (all //// between 0 and 9), and an integer indi- //// cating the array size. Its member //// functions include constructors, func- //// tions to set and access specific array //// elements, a function to access a sub- //// array, a recursive determinant func- //// tion, and an output operator. //////////////////////////////////////////////

#ifndef MATRIX_H#include <fstream>using namespace std;

typedef int elementType;const int MAX_GRID_SIZE = 7;

class matrix{ public: // Class constructors matrix() {size = 0;} matrix(const matrix &m); matrix(int sz);

// Member functions int getSize() const { return size; } void setElement(int i, int j, elementType item); elementType getElement(int i, int j) { return table[i][j]; } elementType determinant(); friend ostream& operator << (ostream &os, const matrix &m);

protected: // Data members elementType table[MAX_GRID_SIZE] [MAX_GRID_SIZE]; int size;

// Member function matrix minor(int i, int j);};

#define MATRIX_H#endif

Recursive Determinant Program Example

CS 240 7

////////////////////////////////////////////// Class implementation file: Matrix.cpp //// The implementation of the copy and //// initializing constructors, the setEle- //// ment, determinant, and minor member //// functions, and the output operator. //////////////////////////////////////////////#include <stdlib.h>#include <assert.h>#include <fstream>#include <iomanip>#include <math.h>#include "Matrix.h"using namespace std;

// Copy constructor: Copies existing mat.//matrix::matrix(const matrix &m){ size = m.size; for (int row = 0; row < m.size; row++) for (int col = 0; col < m.size; col++) table[row][col] = m.table[row][col];}

// Initializing constructor: Sets *this //// up as a sz x sz matrix of zeros. //matrix::matrix(int sz){ size = sz; for (int row = 0; row < sz; row++) for (int col = 0; col < sz; col++) table[row][col] = 0;}

// SetElement Member Function: Sets the //// (i,j) element of the matrix to item. //void matrix::setElement(int i, int j, elementType e){ assert ((0<=i) && (i<size) && (0<=j) && (j<size)); table[i][j] = e;}

// Determinant Member Function: Calculates //// & returns the determinant of the matrix. //elementType matrix::determinant(){ elementType value = 0; if (size == 1) return table[0][0]; for (int col = 0; col < size; col++) { value += (elementType)pow(-1, 0+col) * table[0][col] * minor(0, col).determinant(); } return value;}

Notice how the size×size matrix is being evaluated recursively by using the

top row to expand into size (size-1)×(size-1)

submatrices.

CS 240 8

// Output Operator: Outputs matrix //// as a grid of size rows with //// size columns in each row. //ostream& operator << (ostream &os, const matrix &m){ for (int row = 0; row < m.getSize(); row++) { for (int col = 0; col < m.getSize(); col++) os << setw(4) << m.table[row][col]; os << endl; } return os;}

// Minor Member Function: If *this is // // an nXn matrix, then this returns //// the (n-1)X(n-1) matrix that is //// *this w/row i & column j removed. //matrix matrix::minor(int i, int j){ int subrow, subcol; assert (size > 1); matrix submat(size-1); subrow = 0; for (int row = 0; row < size; row++) { subcol = 0; if (row != i) { for (int col = 0; col < size; col++) if (col != j) { submat.setElement(subrow, subcol, table[row][col]); subcol++; } subrow++; } } return submat;}

CS 240 9

////////////////////////////////////////////////////// Program file: matrixDriver.cpp //// This program tests the matrix class by //// creating a random matrix of a user-specified //// size, outputting it, & taking its determinant. //////////////////////////////////////////////////////#include <iostream>#include <iomanip>#include <ctime>#include "Matrix.h"using namespace std;

int generateRandomNumber(int lowerBound, int upperBound);

// The main function randomly generates & outputs //// a square matrix,, & determines its determinant.//void main(){ int gridSize; cout << "SPECIFY THE MATRIX SIZE (a positive " << "integer less than " << MAX_GRID_SIZE+1 << "): "; cin >> gridSize; while ((gridSize<1) || (gridSize>MAX_GRID_SIZE)) { cout << "SORRY, ONLY VALUES BETWEEN 1 AND " << MAX_GRID_SIZE << " ARE ACCEPTED>\n "; cout << "SPECIFY THE MATRIX SIZE (a positive " << "integer less than " << MAX_GRID_SIZE+1 << "): "; cin >> gridSize; }

matrix grid(gridSize); for (int row=0; row<gridSize; row++) for (int col=0; col<gridSize; col++) grid.setElement(row, col, generateRandomNumber(0,9)); cout << endl << "MATRIX:" << endl << grid << endl << endl; cout << "DETERMINANT: " << grid.determinant() << endl << endl;}

// The generateRandomNumber function //// randomly generates an integer in the //// range between the parameterized low- //// erBound & upperBound values (inclu- //// sive). The first time it is called, //// it seeds the rand() random number //// generation function. //int generateRandomNumber(int lowerBound, int upperBound){ static bool firstTime = true; time_t randomNumberSeed; if (firstTime) { time(&randomNumberSeed); srand(randomNumberSeed); firstTime = false; } return (lowerBound + int((upperBound - lowerBound) * (float(rand()) / RAND_MAX)));}

CS 240 10

CS 240 11

A mathematical “cousin” to recursion is the concept of induction.

Step One: Prove The Base Case

Formally demonstrate that it’s true for that smallest value, n0.

Step Two: Assume For Some General Case

Assume that the it’s been proven true for all values through k, where k is at least n0.Step Three: Prove For The Next Case

Use the assumption that it’s been proven true for smaller cases to prove that it’s also true for k+1.

When you want to prove that something is true for all integer values, beginning at a specific value n0, perform the following steps:

CS 240 12

Induction Example

Theorem A: i = 1,n i = ½n(n + 1) for all n 1.

Proof (by induction):

Step One (Prove for the base case):

For n = 1, i = 1,1 i = 1 = ½(1)(1 + 1).Step Two (Assume for some general case):

Assume for n = k: i = 1,k i = ½k(k + 1).Step Three (Prove for the next case):

Prove for n = k + 1: i = 1,k+1 i = (k + 1) + i = 1,k i

= (k + 1) + ½k(k + 1) (by the assumption for

case k)= ½(2)(k + 1) + ½k(k + 1)= ½(k + 1)(k + 2)= ½(k + 1)((k + 1) + 1).

i=1,1i = 1 = ½(1)(1+1)

i=1,2i = 1+2 = 3 = ½(2)(2+1)

i=1,3i = 1+2+3 = 6 = ½(3)(3+1)

i=1,4i = 1+2+3+4 = 10 = ½(4)(4+1)i=1,5i = 1+2+3+4+5 = 15 = ½(5)(5+1)i=1,6i = 1+2+3+4+5+6 = 21 = ½(6)(6+1)i=1,7i = 1+2+3+4+5+6+7 = 28 = ½(7)(7+1)

CS 240 13

To prove that i = 1,n i = ½n(n + 1) for all n 1, we started by proving that it was true for n = 1.

Once we accomplished that, we assumed that it was true for some arbitrary value k and then proved that that made it true for the next value: k + 1.

In essence, this last proof causes the truth of the theorem to “cascade” through all remaining values.

Why Does Induction Work?

TRUEFOR

n = 1:1 =

½(1)(2)

Letting k = 1

TRUEFOR

n = 2:1 + 2 =½(2)(3)

Letting k = 2

TRUEFOR

n = 3:1+2+3 =½(3)(4)

Letting k = 3

Proof that if it’s true for n = k, then it’s also true for n = k + 1

TRUEFOR

n = 4:1+2+3+4 =

½(4)(5)

Letting k = 4

TRUEFOR

n = 5 :1+2+3+4+5 =½(5)(6)

And so on...

CS 240 14

Theorem Z: For any group of n people, all n have the same height.

What’s Wrong With This Induction?

Proof (by induction):

Step One (Prove for the base case):For n = 1, the group consists of a single

person, so the entire group obviously has the same height.

Step Two (Assume for some general case):

Assume for n = k: Any group of k people have the same height.Step Three (Prove for the next case):

Prove for n = k + 1:Given a group of k + 1 people, remove

one person. The resulting group of k people must, by the inductive hypothesis, have the same height.

Reinsert the person that was removed and then remove a different person. The resulting group of k people must also have the same height.

Thus, all k + 1 people must have the same height!

CS 240 15

int powerOf2(const int &n){ if (n == 0) return 1; return powerOf2(n-1) + powerOf2(n-1);}

One of the bridges between recursion and induction is the recurrence relation, which can be used to determine the execution time of a recursive function.

Assuming that the execution time for arithmetic operations, condition checking, and returning are all the same, let’s also assume that there is a function T(n) such that it takes T(k) time to execute powerOf2(k). Examination of the code above allows us to conclude the following two facts:

T(0) = 2

T(k) = 5 + 2T(k-1) for all k > 0

Using mathematical induction, we can prove that:

Using mathematical induction, we can prove that:T(k) = 7(2k)-5 for all k 0T(k) = 7(2k)-5 for all k 0

Step One (Prove for the base case):For n = 0, we already know

that T(0) = 2, and 7(20)-5 also evaluates to 2.

Step One (Prove for the base case):For n = 0, we already know

that T(0) = 2, and 7(20)-5 also evaluates to 2.Step Two (Assume for some

general case):Assume for n = k: T(k) =

7(2k)-5.

Step Two (Assume for some general case):Assume for n = k: T(k) =

7(2k)-5.Step Three (Prove for the next

case):We need to prove it for n =

k + 1:We know that T(k+1) = 5 +

2T(k), and we’re assuming that T(k) = 7(2k)-5, so we can conclude that T(k+1) = 5 + 2(7(2k)-5) = 7(2k+1)-5, which is what we wanted.

Step Three (Prove for the next case):We need to prove it for n =

k + 1:We know that T(k+1) = 5 +

2T(k), and we’re assuming that T(k) = 7(2k)-5, so we can conclude that T(k+1) = 5 + 2(7(2k)-5) = 7(2k+1)-5, which is what we wanted.

CS 240 16

int powerOf2(const int &n){ if (n == 0) return 1; return 2*powerOf2(n-1);}

Let’s try the same approach on this alternate form of the function.

Using the same assumptions as before, we get the following recurrence relation:

T(0) = 2

T(k) = 4 + T(k-1) for all k > 0

This time, however, mathematical induction tells us that:

T(k) = 4k+2 for all k 0

1248

163264

128256512

1024204840968192

1638432768