Title: Recursion
1Recursion
Self-Referencing Functions
2Recursion
Functions can call other functions, including
themselves . Certain problems are best solved
with recursive functions For Example Factorial
, Fibonacci Series Fig.3.14 FactorialTest.
3Recursion
The Fibonacci Series, 0, 1, 1, 2, 3, 5, 8, 13,
21, ... Begins with 0 and 1 and has the property
that each subsequent Fibonacci number is the sum
of the previous two Fibonacci numbers. The series
occurs in nature and , in particular, describes a
form of spiral.The ratio of successive Fibonacci
numbers converges on a constant value of
1.618This number, too, repeatedly occurs in
nature and has been called the golden ratio or
the golden mean.
4Recursion
Humans tend to find the golden mean aesthetically
pleasing. Architects often design windows, rooms
and building whose length and width are in the
ratio of the golden mean. The Fibonacci series
may be defined recursively as follows fibonacci
( 0 ) 0 fibonacci ( 1 ) 1 fibonacci ( n )
fibonacci ( n -1 ) fibonacci ( n - 2)
5Recursion
Example The program of Fig. 3.15 calculates the
Fibonacci numbers recursively using function
fibonacci.
6Problem 1
- Write a function that, given n, computes n!
- n! 1 2 ... (n-1) n
- Example
- 5! 1 2 3 4 5 120
- Specification
- Receiven, an integer.
- Precondition n gt 0 (0! 1 1! 1).
- Return n!, a double (to avoid integer overflow).
7Preliminary Analysis
- At first glance, this is a counting problem,
so we could solve it with a for loop - double Factorial(int n)
-
- double result 1.0
- for (int i 2 i lt n i)
- result i
- return result
-
- But lets instead learn a different approach...
8Analysis
- Consider n! 1 2 ... (n-1) n
- so (n-1)! 1 2 ... (n-1)
- substituting n! (n-1)! n
- We have defined the ! function in terms of
itself. - Historically, this is how the function was
defined before computers (and for-loops) existed.
9Recursion
- A function that is defined in terms of itself is
called self-referential, or recursive. - Recursive function are designed in a 3-step
process - 1. Identify a base case -- an instance of the
problem whose solution is trivial. - Example The factorial function has two base
cases - if n 0 n! 1
- if n 1 n! 1
10Induction Step
- 2. Identify an induction step -- a means of
solving the non-trivial (or big) instances of
the problem using one or more smaller
instances of the problem. - Example In the factorial problem, we solve the
big problem using a smaller version of the
problem - n! (n-1)! n
- 3. Form an algorithm from the base case and
induction step.
11Algorithm
- // Factorial(n)
- 0. Receive n.
- 1. If n gt 1
- Return Factorial(n-1) n.
- Else
- Return 1.
12Discussion
- The scope of a function begins at its heading
and ends at the end of the file. - Since the body of a function lies within its
scope, nothing prevents a function from calling
itself.
13Coding
- / Factorial(n), defined recursively
- ...
- /
- double Factorial(int n)
-
- if (n gt 1)
- return Factorial(n-1) n
- else
- return 1.0
14Behavior
- Suppose the function is called with n 4.
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
15Behavior
- The function starts executing, with n 4.
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
16Behavior
- The if executes, and n (4) gt 1, ...
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
17Behavior
- and computing the return-value calls Factorial(3).
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
18Behavior
- This begins a new execution, in which n 3.
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
19Behavior
- Its if executes, and n (3) gt 1, ...
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
20Behavior
- and computing its return-value calls Factorial(2).
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
21Behavior
- This begins a new execution, in which n 2.
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
22Behavior
- Its if executes, and n (2) gt 1, ...
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
23Behavior
- and computing its return-value calls Factorial(1).
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
24Behavior
- This begins a new execution, in which n 1.
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
25Behavior
- The if executes, and the condition n gt 1 is
false, ...
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
26Behavior
- so its return-value is computed as 1 (the base
case)
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
27Behavior
- Factorial(1) terminates, returning 1 to
Factorial(2).
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
28Behavior
- Factorial(2) resumes, computing its return-value
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
1 2
29Behavior
- Factorial(2) terminates, returning 2 to
Factorial(3)
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
30Behavior
- Factorial(3) resumes, and computes its
return-value
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
2 3
31Behavior
- Factorial(3) terminates, returning 6 to
Factorial(4)
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
32Behavior
- Factorial(4) resumes, and computes its
return-value
6 4
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
33Behavior
- Factorial(4) terminates, returning 24 to its
caller.
int Factorial(int n) if (n gt 1) return
Factorial(n-1) n else return 1
34Recursion or Iteration?
EFFICIENCY
CLARITY
35Why use recursion?
These examples could all have been written
without recursion, by using iteration instead.
The iterative solution uses a loop, and the
recursive solution uses an if statement. However,
for certain problems the recursive solution is
the most natural solution. This often occurs
when structured variables are used.
36Discussion
- If we time the for-loop version and the recursive
version, the for-loop version will usually win,
because the overhead of a function call is far
more time-consuming than the time to execute a
loop. - However, there are problems where the recursive
solution is more efficient than a corresponding
loop-based solution.
37Problem 2
- Consider the exponentiation problem
- Given two values x and n, compute xn.
- Example 33 27
- Specification
- Receive x, n, two numbers.
- Precondition n gt 0 (for simplicity)
- n is a whole number.
- Return x raised to the power n.
38Analysis
- The loop-based solution requires _at_ n steps
(trips through the loop) to solve the
problem - double Power(double x, int n)
-
- double result 1.0
- for (int i 1 i lt n i)
- result x
- return result
-
- There is a faster recursive solution.
39Analysis (Ctd)
- How do we perform exponentiation recursively?
- Base case
- n 0
- Return 1.0.
40Analysis (Ctd)
- Induction step n gt 0
- We might recognize that
- xn x x ... x x // n factors of
x - and
- xn-1 x x ... x // n-1 factors of
x - and so perform a substitution
- Return Power(x, n-1) x.
- However, this requires _at_ n steps (recursive
calls), which is no better than the loop version.
41Analysis (Ctd)
- Induction-step n gt 0.
- Instead, we again begin with
- xn x x ... x x // n factors of
x - but note that
- xn/2 x ... x // n/2 factors of x
- and then recognize that when n is even
- xn xn/2 xn/2
- while when n is odd
- xn x xn/2 xn/2
42Analysis (Ctd)
- Induction step n gt 0.
- We can avoid computing xn/2 twice by doing it
once, storing the result, and using the stored
value - a. Compute partialResult Power(x, n/2)
- b. If x is even
- Return partialResult partialResult.
- Else
- Return x partialResult partialResult.
- End if.
- This version is significantly faster than the
loop-based version of the function, as n gets
larger.
43Algorithm
- 0. Receive x, n.
- 1. If n 0
- Return 1.0
- Else
- a. Compute partialResult Power(x, n/2)
- b. if n is even
- Return partialResult partialResult
- else
- Return x partialResult partialResult
- end if.
44Coding
- We can then code this algorithm as follows
- double Power(double x, int n)
-
- if (n 0)
- return 1.0
- else
-
- double partialResult Power(x, n/2)
- if (n 2 0)
- return partialResult partialResult
- else
- return x partialResult partialResult
-
-
- which is quite simple, all things considered...
45How Much Faster?
- How many steps (recursive calls) in this
version? - Power(x, i) i Steps Steps
- (loop version) (this version)
- Power(x, 0) 0 0 0
- Power(x, 1) 1 1 1
- Power(x, 2) 2 2 2
- Power(x, 4) 4 4 3
- Power(x, 8) 8 8 4
- Power(x, 16) 16 16 5
- Power(x, 32) 32 32 6
- ...
- Power(x, n) n n log2(n)1
46How Much Faster? (Ctd)
- The larger n is, the better this version
performs - Power(x, i) i Steps Steps
- (loop version) (this version)
- Power(x, 1024) 1024 1024 11
- Power(x, 2048) 2048 2048 12
- Power(x, 4096) 4096 4096 13
- Power(x, 8192) 8192 8192 14
- ...
- The obvious way to solve a problem may not be the
most efficient way!
47Summary
- A function that is defined in terms of itself is
called a recursive function. - To solve a problem recursively, you must be able
to identify a base case, and an induction step. - Determining how efficiently an algorithm solves a
problem is an area of computer science known as
analysis of algorithms. - Analysis of algorithms measures the time of an
algorithm in terms of abstract steps,
as opposed to specific statements.