Title: Computing Fundamentals with C
1C and Object-Oriented Programming Recursion
2Recursion
- Chapter Objectives
- Compare iterative and recursive solutions to the
same problem - Identify the recursive case and the base (or
simple) case in a recursive algorithm - Implement recursive methods
3Understanding Recursion
- Recursion is used to describe things. Here are
some everyday examples - Show everything in a folder and all it subfolders
- show everything in top folder
- show everything in each subfolder in the same
manner - How to look up a word in a dictionary
- look up a word (use alphabetical ordering) or
- look up word to define the word you are looking
up - A waiting line is either
- empty or
- has someone at the front of the waiting line
followed by a waiting line. Can we have a
waiting line with one person?
4Recursive definition for arithmetic definition
- Arithmetic expression is defined as
- a numeric constant
- an numeric identifier
- an arithmetic expression enclosed in parentheses
- 2 arithmetic expressions with a binary operator
like - / or - The term arithmetic expression is defined using
the term arithmetic expression - but the first two possibilities dont
5Mathematical Examples
- Consider the factorial function (0!1)
- The recursive definition
- What is f(2)? ___________
- What is f(3)? ___________
6Recursive and non-recursive solutions
- // Non-recursive solution, using a loop
- int factRep(int n)
- // precondition n gt 0
- long result 1
- for(int j 2 j lt n j)
- result j result
- return result
-
- // Recursive solution
- int factRec(int n)
- // precondition n gt 0
- if(n 0)
- return 1 // Simple case
- else
- return n factRec(n - 1) // Recursive case
- // Don't call factRec(n 1)!
7Basic Recursion
- Consider this weird way to raise 2 to the 5th
- each method is a similar but simpler version of
the previous method - eventually, the method returns a value without
any further calls - static int twoRaisedTo0() return 1
- static int twoRaisedTo1() return 2
twoRaisedTo0() - static int twoRaisedTo2() return 2
twoRaisedTo1() - static int twoRaisedTo3() return 2
twoRaisedTo2() - static int twoRaisedTo4() return 2
twoRaisedTo3() - static int twoRaisedTo5() return 2
twoRaisedTo4() -
- int main()
-
- System.out.println("2 to the 5th "
twoRaisedTo5()) -
- // Output 2 to the 5th 32
8A Less Tedious and More General Option (without a
loop)
- Consider twoRaisedTo(0), which would return 1
- Make this method return 2 with twoRaisedTo(1)
- int twoRaisedTo(int n)
-
- if(n 0)
- return 1
- else
- // Express twoRaisedTo 4, 3, 2, 1, and
0 - return 2 ______________________________
____ -
- How many method calls occur with twoRaisedTo(5)?
9Recursive Methods
- The previous method is known as a recursive
method because it calls itself - Each time, the method calls a simpler version
- The answers should be
- return 2 twoRaisedTo(n-1) and 5
- Eventually no further calls are necessary
- the simple case is reached and the method simply
- returns 1 which goes back to the previous
call - return 2 twoRaisedTo(1) // 2 1
2
10Mathematical Examples
- twoRaisedTo can be written as follows
- If n gt 0, use the top line as the definition of
the method - If n 0, use the bottom line
- Notice that twoRaisedTo appears on both sides of
, which makes this a recursive definition - Recursive definitions must have a simple case
- no method name to the right of
11Complete the Substitutions
twoRaisedTo(0) ____ // simple case The
first answer you can write twoRaisedTo(1) 2
twoRaisedTo(0) 2 ____ twoRaisedTo(2) 2
twoRaisedTo(1) 2 ____ twoRaisedTo(3) 2
twoRaisedTo(2) 2 ____ twoRaisedTo(4) 2
twoRaisedTo(3) 2 ____
twoRaisedTo(5) 2 twoRaisedTo(4) 2
_____ or
- Can you evaluate f(5) before evaluating f(4)?
- Now perform back substitution
- fill in the blanks above from top to bottom
12Defining Base and Recursive Cases
- When writing recursive methods
- Make sure there is at least one base case
- at least one situation where a recursive call is
not made. There could be more than 1 - The method might return a value, or do nothing at
all - There could be one or more recursive cases
- a recursive call must be a simpler version of the
same problem - the recursive call should bring the method closer
to the base case perhaps pass n-1 rather than n.
13How Recursion works
- Method calls generate activation records
- Depending on the system, the activation record
might store - all parameter values and local values
- return point -- where to go after the method
finishes - imagine all this is in a box
- the data is stored in an activation frame and
pushed onto a stack -- one on top of the other - when the method finishes, the activation frame
stack is popped - it disappears, control returns to where it was
called
14A method that calls itself
- void forward(int n)
-
- if(n gt 1)
- forward(n - 1) // recursive call n goes
toward 0 - // RP FORWARD
- System.out.println(n)
-
- int main()
-
- int arg 3
- forward(arg)
- // RP MAIN
- arg 999
- return 0
n 1
RP FORWARD n 2
RP FORWARD n 2
RP FORWARD n 3
RP FORWARD n 3
RP FORWARD n 3
RP MAIN arg 3
RP MAIN arg 3
RP MAIN arg 3
RP MAIN arg 3
15The simple case is reached
- Several activation frames are stacked
- When parameter (n) 1, there is no recursive
call. - (1) execute the base case when n 1
- System.out.println(n)
- The output is 1 and the method is done
(1)
n 1
RP FORWARD n 2
RP FORWARD n 3
RP MAIN arg 3
16Returning back to main
- (2) Return to previous call and pop box on top
- continue from RP FORWARD, print 2
- (3) Return to previous call and pop box on top
- continue from RP FORWARD, print 3
- (4) Return to main, pop box on top
- execute arg 999 (to indicate we're back in
main) - The main method is done, Output is
123
(2)
RP FORWARD n 2
(3)
RP FORWARD n 3
RP FORWARD n 3
(4)
RP MAIN arg 3
RP MAIN arg 3
RP MAIN arg 3 999
17Too much recursion can be costly
- Consider the Fibonacci numbers
- 1, 1, 2, 3, 5, 8, 13, 21, .
-
- Demonstrate Fibonacc.cpp
long fib(int n) // counts calls to fib
methodCalls if(n 0 n 1)
return 1 else return fib(n-1)fib(n-2)
18fib(5) calls fib(4) once, fib(3) twice, fib(2)
thrice, fib (1) five times and fib(0) thrice
- fib(7) 21 methodCalls 41
- fib(8) 34 methodCalls 67
- fib(9) 55 methodCalls 109
- fib(10) 89 methodCalls 177
- fib(11) 144 methodCalls 287
- fib(12) 233 methodCalls 465
- fib(13) 377 methodCalls 753
- fib(14) 610 methodCalls 1219
- fib(15) 987 methodCalls 1973
- fib(16) 1597 methodCalls 3193
- fib(17) 2584 methodCalls 5167
- fib(18) 4181 methodCalls 8361
- fib(19) 6765 methodCalls 13529
- fib(20) 10946 methodCalls 21891
19Recursive Fibonacci grows Exponentially
20Converting Decimal Numbersto Other Bases
- Problem Convert a decimal (base 10) number into
other bases - Function Call Output
- convert(99, 2) 1100011
- convert(99, 3) 10200
- convert(99, 4) 1203
- convert(99, 5) 344
- convert(99, 6) 243
- convert(99, 7) 201
- convert(99, 8) 143
- convert(99, 9) 120
21Digits are multiplied by powers of the base 10,
8, 2, or whatever
- First converting from other bases to decimal
- Decimal numbers multiply digits by powers of 10
- 950710 9 x 103 5 x 102 0 x 101 7 x 100
- Octal numbers powers of 8
- 15678 1 x 83 5 x 82 6 x 81 7 x 80
- 512 320 48 7 88710
- Binary numbers powers of 2
- 10112 1 x 23 1 x 22 1 x 21 1 x 20
- 8 4 2 1 1510
22Converting base 10 to base 2
- 1) divide number by new base (2), write remainder
(1) - 2) divide quotient (2), write new remainder (0)
to left - 3) divide quotient (1), write new remainder (1)
to left - __2_
- 2) 5 Remainder 1
- __1_
- 2) 2 Remainder 0
- __0_
- 2) 1 Remainder 1
- Stop when the quotient is 0 510 1012
Print remainders in reverse order
23Converting base 10 to base 8
- 1) divide number by new base (8), write remainder
(1) - 2) divide quotient (2), write new remainder (0)
to left - 3) divide quotient (1), write new remainder (1)
to left - _12_
- 8)99 Remainder 3
- __1_
- 8)12 Remainder 4
- __0_
- 8) 1 Remainder 1
- Stop when the quotient is 0 9910 1348
Print remainders in reverse order
24Solutions
- We could either
- store remainders in an array and reverse it or
- write out the remainders in reverse order
- have to postpone the output until we get quotient
0 - Algorithm
- while the decimal number gt 0
-
- Divide the decimal number by the new base
- Set the decimal number to the decimal
number divided by the base - Write the remainder to the left of any
preceding remainders -
25Base CaseRecursive Case
- Base case
- if decimal number being converted 0
- do nothing
- Recursive case
- if decimal number being converted gt 0
- convert a simpler version of the problem
- use the quotient as the argument to the next call
- print the remainder
26Convert a number to other bases
- void convert(int decimalNumber, int base)
-
- if(decimalNumber gt 0)
- // This recursive call makes progress
- // towards the simple case by passing
- // the quotient in each recursive call
- convert(decimalNumber / base, base)
- // RP CONVERT
- System.out.print(decimalNumber base)
- // After the last call, write the
- // remainders in reverse order
-
Demonstrate Fibonacci.cpp
2799 base 8
- // Base case where
- // nothing happens, so return
- // print 1
- number 8 1 8 1
- // print 4
- number 8 12 8 4
- // print 3
- number 8 99 8 3
- // Output from inside the if
- 143
number 0 base 8
RP CONVERT number 1 base 8
RP CONVERT number 12 base 8
RP CONVERT number 99 base 8
IN MAIN number 99 base 8