Copyright © Curt Hill 2006-2007 Recursion A Different Way to Think and Solve Problems

Preview:

Citation preview

Copyright © Curt Hill 2006-2007

Recursion

A Different Way to Think and Solve Problems

Copyright © Curt Hill 2006-2007

Definition

• A recursively defined term is somehow defined in terms of itself

• A recursive function generally calls itself– It may also call a sequence of functions that

eventually call it back

• The term recursion comes from the mathematical term of a recurrence relation

Copyright © Curt Hill 2006-2007

Recurrence Relation

• Usually used to define the terms of series or sequence

• The recurrence relation for the Fibonacci sequence is:an = an-1 + an-2

• Typically a Fibonacci sequence starts with:a0 = a1 = 1 giving:1 1 2 3 5 8 13 21 34 55 …

Copyright © Curt Hill 2006-2007

Recursive Definition

• A recursive definition almost always has two or more parts

• One deals with startup or boundary conditions– This one makes no self reference

• The other is self-referencing– Always adds something before or after

the self-reference

Copyright © Curt Hill 2006-2007

Definition

• An item that is somehow defined in terms of itself

• Circular ≠ recursive– Circular definition adds no information

to definition– Recursive definition usually is multipart

• One part is non recursive• Even recursive parts add information

• Recursion is a generalization of looping

Copyright © Curt Hill 2006-2007

Circular vs. Recursive

• A circular definition adds nothing to the knowledge of the reader– A rose is a rose is a rose is a rose

• A recursive definition is partially based on itself– Yet it is able to completely specify the

result

• Both data and functions or methods may be recursively defined

Copyright © Curt Hill 2006-2007

Consider this Shape

Copyright © Curt Hill 2006-2007

Where Do We See These?• Recursive functions

– Function calls itself– Mutual recursion – a set of functions call

each other so that a call to another function generates a call to itself• A calls B and B calls A• A calls B, B calls C, C calls A

• Recursive data structures– Lists– Trees– Graphs

Copyright © Curt Hill 2006-2007

Consider a recursive definition

• Recursive definition of a linked list• A list may be:

– Empty– Item followed by a list

• Now consider how this becomes code

Copyright © Curt Hill 2006-2007

A Linked List• Two classes:class ListItem { Data d; // carried data ListItem * next; friend class List; ListItem(); // private default }; // everything is privateclass List{ ListItem * root; public: void add(Data d); bool isPresent(Data d); …};

Copyright © Curt Hill 2006-2007

Discussion

• The pointer represents the list– Either the next in the ListItem or – The root in List

• May be NULL– Which is the empty list

• May refer to a ListItem– Which is an item followed by a list

• Trees are more recursive but await the data structures course

Copyright © Curt Hill 2006-2007

Why Recursion?• Recursion is the generalization of

looping• This is loosely translated into this:• Any loop may be made into a

recursive function• Some recursive functions cannot

be made into loops• However, every recursive function

may be converted to a loop with an auxiliary data structure

Copyright © Curt Hill 2006-2007

Recursive Functions

• A recursive function must follow the same rules as a recursive definition– Except as translated to execution

• They are:• One or more non-recursive paths• Each recursive path moves the

function closer to a non-recursive path

• Failure to follow this cause problems

Copyright © Curt Hill 2006-2007

Recursive Functions

• A routine that calls itself or• A routine that initiates a call that

calls itself– A calls B– B calls C– C calls A– Syntax

• In C++ everything must be declared before it is used

• We then merely declare the prototype before it is used

Copyright © Curt Hill 2006-2007

Forms of Recursion

• Most recursive functions call themselves

• Two functions that call each other are called mutually recursive:int a(…);int b(…){ … x = a(…); … }int a(…){ … y = b(…); … }

• Notice the need for a prototype• The chain could be longer than two

Copyright © Curt Hill 2006-2007

Factorial

• Definition of factorial: N! =– IF n > 1 THEN N * (n-1)!– IF n <= 1 THEN 1

• This suggests a recursive definition– Code:int factorial(int n) { if (n > 1) return(factorial(n-1)*n); return(1);}

• Notice conciseness

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

parameter

20

4

First Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

parameter

7

3Second Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

parameter

7

3Second Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n); 8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

parameter

7

2Third Call

1

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

Function result

parameter1Fourth Call

7 Return address

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

Function result

n1Fourth Call

7 Return address

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

Function result

n1Fourth Call

7 Return address

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

Function result

n1Fourth Call

7

1

Return address

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

1

7

1

2

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

7

2

1

7

1

2

6

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

7

3

7

2

1

7

1

2

6

24

Copyright © Curt Hill 2006-2007

Trace through it5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Function resultgiven to f

20

4

7

3

7

2

1

7

1

2

6

24

Copyright © Curt Hill 2006-2007

Which is better?

• Some would argue that the iterative version is more understandable

• Others would argue the opposite• The call overhead is more expensive

than the loop overhead• The recursive version will require

more memory and more time in most cases

Copyright © Curt Hill 2006-2007

Rules

• Rules for recursive functions• There must be at least one non-

recursive path through the routine– There may be many recursive or non-

recursive paths

• Each recursive path must move us toward a non-recursive path

Copyright © Curt Hill 2006-2007

Properties• Any recursive routine can be recoded non-

recursively• Any loop can be replaced by a recursive

routine• The loop is faster and takes less space• If that is the case why use recursion?

– Elegance– Easy to write

• Use recursion only for things inherently recursive– That is, if recursion simplifies the solution

Copyright © Curt Hill 2006-2007

Should We or Should We Not?

• There are many examples of routines that– Should be recursive– Could be recursive– Should not be recursive

• Factorial could be either but loop is preferred– Generally easier to code– Somewhat more efficient, but not

dramatically

Copyright © Curt Hill 2006-2007

Fibonacci• The Fibonacci sequence

– 1 1 2 3 5 8 13 21 34 55 89 ...– Number these from zero ==> F(6) = 13

• The definition is defined as follows:a0 = 1a1 = 1 an = an-1 + an-2

• As a function this becomes:int fib (int n){ if(n<2) return 1; return fib(n-1) + fib(n-2);}

• Is this better than the iterative one?

Copyright © Curt Hill 2006-2007

The Code

• Code:int fibo(int n){ if(n < 2) return 1; return fibo(n-1)+fibo(n-2);}

• What is wrong with this code?

Copyright © Curt Hill 2006-2007

Iterative Fibonacci• The iterative function:int fib2 (int n){ int first=1, second=1; int third = 1; while(--n>0){ third = first + second; first = second; second = third; } return third;}

Copyright © Curt Hill 2006-2007

So which of these is better

• The recursive is somewhat easier to read

• The iterative is much, much better from an execution point of view

• The recursive version re-computes what it has already computed– Several times

• Consider the following call graph

Copyright © Curt Hill 2006-2007

Fibonacci Call Graph

fib(6)

fib(4)

fib(2) fib(3)

fib(0) fib(1) fib(1) fib(2)

fib(5)

fib(3) fib(4)

fib(1) fib(2) fib(2) fib(3)

fib(0) fib(1) fib(0) fib(1)

fib(1) fib(2)fib(0)fib(1)

Copyright © Curt Hill 2006-2007

Fibonacci Discussion

• No re-using of calculated values• Fibo(2) is calculated twice • Fibonacci sequence should be

calculated from zero up• Using a loop

Copyright © Curt Hill 2006-2007

Tower of Hanoi• The legend:

– Buddhist monastery near Hanoi– 64 gold discs of descending size– Three pegs– Move the discs from one peg to another

• Rules– Only one disc can be moved at a time– A larger disk can not be put on a smaller– When they are all moved, world will end

• The solution is so inherently recursive that the loop solution is hard to see

Copyright © Curt Hill 2006-2007

Moving Process

• The moving process is inherently recursive

• There are two recursive calls– One before and one after the single

plate move

• The number of single plate moves is based on the plate number: 2N-1

• How many single plate moves to move a 64 plate stack?

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2

1

A B C

Move the stack from A to B, using C as a temporary holder

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2

1

A B C

Move 1 to C

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2 1

A B C

Move 2 to B

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2

1

A B C

Move 1 to B

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 32

1

A B C

Move 3 to C

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 32

1

A B C

Move 1 to A

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 3

21

A B C

Move 2 to C

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 3

2

1

A B C

Move 1 to C

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 3

2

1

A B C

Move 4 to BThis is the halfway move.

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 3

21

A B C

Move 1 to B

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 32

1

A B C

Move 2 to A

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4 32

1

A B C

Move 1 to A

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2

1

A B C

Move 3 to B

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2 1

A B C

Move 1 to C

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2

1

A B C

Move 2 to B

Copyright © Curt Hill 2006-2007

Tower of Hanoi

4

3

2

1

A B C

Move 1 to BDone

Copyright © Curt Hill 2006-2007

Solution• To move Disk N from A to B using C:

– Move 1 to N-1 to C– Move N to B– Move 1 to N-1 to B

• Each move of multiple disks is recursive

• Two recursive calls per loop makes this more exciting

Copyright © Curt Hill 2006-2007

The codevoid move(int disk, char from, char to, char temp) {/* move disk from peg from to peg to using temp as temporary peg */if (1 < disk)

move(disk-1,from,temp,to);cout << "Move disk "<<disk <<" from "<<from << " to " << toif (1 < disk)

move(disk-1,temp,to,from);}

Copyright © Curt Hill 2006-2007

Recursive Loops

• Recursion is the generalization of looping

• Normal loops– The body of the loop is executed in a

straight line fashion– No opportunity to re-execute until end

• Recursive loops– Always start at the beginning and end at

end– A new loop can be inserted anywhere in

the loop

Copyright © Curt Hill 2006-2007

Discussion• Consider the following function:int recfun(int p1){ A; B; C; return …; }– Where A, B, C are arbitrary

sequences of statements• Where could recursive calls to

recfun be put and what would happen?

Copyright © Curt Hill 2006-2007

Before or After•Inserting the call before or

after the sequence gives the following sequence:–ABCABCABCABC

•Similar to a loop around the seqence

•This is what was used in factorial

•Called head or tail recursion

Copyright © Curt Hill 2006-2007

Head and Tail Recursion

• The factorial function is an example of tail recursion

• Tail recursion is when the routine does everything and then the recursive call last

• Head recursion is doing the call first• Usually head and tail recursion may

be easily converted to loops

Copyright © Curt Hill 2006-2007

One Call Elsewhere•Suppose the call is between A

and B•Then we get:

–AAABCBCBC•Still one recursive call•Now it will take two different

loops•However any one call can be

replaced by one or more loops

Copyright © Curt Hill 2006-2007

Two Calls • Suppose there is a call between A

and B and another between B and C• Twice through we would get

something like:– AABCBABCC

• Two recursive calls• How would you loop this?• Three times through:

– AAABCBABCCBAABCBABCCC– Although other orders are possible

• These two calls are similar to Hanoi

Copyright © Curt Hill 2006-2007

Maze Searching

• In maze searching the problem is that we often come to a fork in the road

• The looping solution must pursue one of these and save the other for future exploration

• The recursive solution goes both ways at once

• Like pouring water into the maze

Copyright © Curt Hill 2006-2007

Maze Searching

• Suppose we have a maze• How does one search a maze?• There are always multiple ways to go• Multiple paths may work

X

Copyright © Curt Hill 2006-2007

How do you do it?

• The solution is like pouring water into the maze

• If you will pour water into a 3D version of this it will move it all directions at once

• Make a recursive calls that advances one square– One recursive call for each possible

direction

Copyright © Curt Hill 2006-2007

Maze Representation• Assume a 25 by 25 array of

character– Maze may be smaller with a count– Positions 0 through count actually

constitute array• Getting to row or column 0 or row or

column count indicates success• Blanks are paths• Asterisks or other non-blanks are

obstacles• May only move up, down, left or right

Copyright © Curt Hill 2006-2007

Overview Algorithm• The search function will be a boolean

function– True indicate a path found

• Parameters are the position• If the position is not blank return

immediately• Replace position with ‘0’• If on edge return true• If any of the four ways from here

works replace with ‘= ’

Copyright © Curt Hill 2006-2007

Quick Sort

• Uses both looping and recursion– Just like we should use all types of loops

• The sort function has two distinct phases:

• Partition the array into three pieces– The upper part of array– An item– The lower part of array

• Recursively sort upper and lower part

Copyright © Curt Hill 2006-2007

Partitioning• Initialize

– Choose a pivot, typically the first array element

– Set an index to top and bottom values of remaining array

• Loop until indices collide– Find an element that is larger than

pivot in top– Find an element that is smaller than

pivot in bottom– Swap the two

• Move pivot to correct location

Copyright © Curt Hill 2006-2007

At the end of partition phase

• The array has three pieces:• All the elements smaller than the

pivot are together at the top• All the elements larger than the pivot

are together at the bottom• The pivot is between them and in its

sorted position– It will never be moved or looked at again

• Then recursively sort the two pieces

Copyright © Curt Hill 2006-2007

Quick Sort

83

19

14

26

Start, pivot is 8start looking

83

19

14

26

83

19

142

6

1st exch2nd exch

83

1

914

2

6

Pivot exch

23

1

914

8

6

Donefound

Three partitions

Copyright © Curt Hill 2006-2007

Efficiency

• Quick sort does well because of this partitioning

• 2 * (½ N)2 < N2

• Since the partitioning is repeatedly applied, it collapses to N log2N

• The partitioning fails if the pivot is the smallest or largest

• Degenerates to N2 for sorted or inversely sorted arrays

Copyright © Curt Hill 2006-2007

Determinants

• A determinant is a single number computed from a square matrix

• It is used in Cramer’s rule to find solutions to systems of equations

• It is a measure of goodness of the matrix of numbers

• It is also computed recursively

Copyright © Curt Hill 2006-2007

Computation

• Use the method of sums of values times the products of smaller determinants– Expanded minors

• Definition: A minor– A smaller matrix where one row and

column are eliminated

Copyright © Curt Hill 2006-2007

The minor of a 4 by 4• Suppose that we have the following:

4 2 1 -36 -2 7 -49 -7 8 -55 0 3 10

• The minor eliminating position 1,1 is:

4 1 -39 8 -55 3 10

Copyright © Curt Hill 2006-2007

Computing the Determinant

• The determinant of a 1 by 1 is just the value of that cell– This is the non-recursive version

• For a larger determinant:– Take each member of a row – Multiply that member times the

determinant of the minor with this row and column eliminated

– If the sum of subscripts of the member are even then add otherwise subtract

Copyright © Curt Hill 2006-2007

A Determinant of a two by two

326462

34

Copyright © Curt Hill 2006-2007

A Determinant of a three by three

51

335

79

334

79

512

795

514

332

Copyright © Curt Hill 2006-2007

Trees

• A tree is a dynamic data structure• A tree is:

– Empty– Node with one or more sub-trees

• The sub-trees could be empty

Copyright © Curt Hill 2006-2007

Tree Processing• Trees are always ordered• There are several linear routines in

processing a tree– Adding a node to a tree– Searching a tree

• The iterator is a different story • While finding all of the items it must

go both ways – a classic reason for a recursive routine

• This is a better topic for the next class

Copyright © Curt Hill 2006-2007

Common trees

• Binary search trees – used in memory

• BTrees – disk based search trees• Trie – A combination of tree and

array• Parse trees

Copyright © Curt Hill 2006-2007

Recursive Descent Parsing

• Consider this portion of a grammar

function: header compound stmt

compound stmt: { }stmt

stmt:

expression

if stmt

compound stmt

for stmt

Copyright © Curt Hill 2006-2007

The Parser• A recursive descent parser typically

has a function for each of the major constructs

• Thus for this language it would have:– Statement– Expression– CompoundStatement– IfStatement– ForStatement

• These would involve chains of recursive calls

Copyright © Curt Hill 2006-2007

Common Features

• The examples have this in common:• The recursive function often

generates more than one recursive call

• This makes it hard to generate an iterative solution

• However, there are one call functions that are hard to do iteratively as well

Copyright © Curt Hill 2006-2007

Languages

• Certain languages disallow recursion– FORTRAN– Older BASICs– COBOL

• Certain languages allow– The Pascal family– The C family

• Certain languages encourage– The LISP family

Copyright © Curt Hill 2006-2007

Automatic Variables

• Recursion is enabled by automatic variables

• Older versions of FORTRAN disallow recursion because it only had static variables

• Newer languages have automatic variables

• Each call of a function creates a separate copy of the parameters and local variables

• Recall the trace from before

Copyright © Curt Hill 2006-2007

Recall this trace5 int factorial(int n) { 6 if (n > 1) 7 return(factorial(n-1)*n);8 return(1);9 }...20 int f = factorial(4);

Return address

Function result

n

20

4

First Call

Return address

Function result

n

7

3Second Call

Return address

Function result

n

7

2Third Call

Function result

n1Fourth Call

7 Return address

Copyright © Curt Hill 2006-2007

Discussion

• In the previous trace there were four copies of n

• The ability to store local values is why recursion cannot be replaced by just looping

• Instead an auxiliary data structure• Recursion has that extra data

structure – the run-time stack

Copyright © Curt Hill 2006-2007

Converting Recursion to Looping

• The advantage that recursion has over looping is the storage of local variables and parameters on the stack

• In order to convert recursion to looping a secondary data structure is needed to hold the multiple processing opportunities

• Thus when the maze searcher wants to go four ways, it must save all four and then pick out one to pursue

• It is done when there is nothing left to check

Copyright © Curt Hill 2006-2007

Infinite Recursion

• Infinite recursion is similar to an infinite loop

• Just faster• What happens is that the stack space

is exhausted and rather quickly• What happens is that the recursive

paths to not move us closer to the non-recursive path

Copyright © Curt Hill 2006-2007

When to use Recursion

• When recursion is natural– The problem is inherently recursive– It should be easy to understand in this

situation

• No duplicate computation occurs• Iteration ends up being much more

complex– Needs an auxiliary data structure

• You are coding in LISP

Copyright © Curt Hill 2006-2007

Summary

• When it is easy always use looping• Looping can always be made faster• Recursion should be used when the

problem is inherently recursive• Properly used recursion will generally

be much more elegant than looping

Recommended