Today’s Lecture SOFT1902 Week 8a: Recursion SOFT1002 (Sem2, 2005)

SOFT1002 (Sem2, 2005)
Today’s Lecture
SOFT1902 Week 8a:
Recursion
Definition and explanation
Recursive
Sequences, and how to code them
Functions, and how to code them
Famous recursive algorithms
Mechanism of recursion
Discussion of usefulness of recursion
School of Information Technologies
Readings
Kingston Text Book: Ch 18 “Recursion”
Big Java (2nd ed): Ch 18 “Recursion”
Further wonderful reading: “Gödel, Escher, Bach” by
D. Hofstadter
1
SOFT1002 © University of Sydney, 2005
2
What is recursion?
Recursion
Something that allows you to define
something in terms of itself, e.g.,
factorial n! = n (n – 1)! (a function)
Fibonacci sequence Fn = Fn–1 + Fn–2 (a
(noun. see “Recursion”)
sequence of numbers)
rooted binary tree (a definition)
T = root {+ left subtree TL} {+ right subtree
TR} where TL and TR are rooted binary trees
3
What else is recursion?
SOFT1002 © University of Sydney, 2005
4
Any recursion
...has a base case and a recurrence rule.
The base case is also known as the trivial or
simplest case.
allows a function to call itself
(with different arguments!)
describes relationships
It gives an explicit answer for this case
The recurrence rule governs how we break
the problem into simpler ones until we hit the
base case.
my.ancestor = parent or parent.ancestor
It shows how the answer in other cases is made
up from answers of simpler cases
No recursive definition or recursive method is
complete without these two!
SOFT1002 © University of Sydney, 2005
5
SOFT1002 © University of Sydney, 2005
6
1
SOFT1002 (Sem2, 2005)
Important point
Recursive functions & sequences
Factorial
Recursion can be very useful:
1, 2, 6, 24, 120, 720, 5040, ...
n! = n( n–1)(n–2)...(2)(1) = n(n–1)!
code is usually clean –
easy to write/read/understand,
Fibonacci
some problems are much harder to solve in other ways.
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
But recursion is not always ideal:
Fn = Fn–1 + Fn–2
LRRCC
code may be very inefficient (and it can be hard to spot)
(linear recurrence relation with constant coefficients)
Recursion is never essential:
fn = a1f n–1 + a2fn –2 + ... akfn –k
with enough effort, it can be replaced by iteration
(NB: LRRCCs can be solved exactly by factorizing a
characteristic polynomial: see a mathematics text for details!)
7
SOFT1002 © University of Sydney, 2005
SOFT1002 © University of Sydney, 2005
Direct conversion of recursive
sequence definition
Any recursive sequence
...has a starting point (e.g. 0!, F0)
and an expression of the form
f(n) = a function of f(n–1), f(n–2), ...
Note that recursion need not be on just
one variable: e.g., binomial, multinomial
coefficients recursive on n and k:
8
int fact(int n) {
if (n==1)
return 1;
else
return n*fact(n-1);
}
int fib(int n) {
if ((n==1)||(n==2))
return 1;
else
return (fib(n-1)+fib(n-2));
}
FIBONACCI
FACTORIAL
factn =
9
SOFT1002 © University of Sydney, 2005
Binomial recurrence
1 if n=1;
n . factn-1 if n>1.
n>1.
fibn =
1 if n=1,2;
fibn-1+fibn-2 if n>2.
SOFT1002 © University of Sydney, 2005
10
Pascal's Triangle
Base case: when k=n or when k=0,
result is 1
The binomial coefficients appear in the
expansion of
SOFT1002 © University of Sydney, 2005
11
SOFT1002 © University of Sydney, 2005
12
2
SOFT1002 (Sem2, 2005)
Pascal's Triangle
SOFT1002 © University of Sydney, 2005
13
recursive binomial
public static long binomial(int n, int k)
{
if (n==k)
return 1;
else if (k==0)
return 1;
else
return (binomial(n-1,k) +
binomial(n-1,k-1));
}
SOFT1002 © University of Sydney, 2005
15
Recursive methods
A sequence is a function with integer
argument
Eg fib(n)
Recursion can also be used for methods with
other arguments, provided there is some way
of expressing the result in terms of the same
method on argument(s) which are somehow
simpler
(binomial coefficients are quite normal...)
SOFT1002 © University of Sydney, 2005
14
Pascal would be proud
Printing Pascal's Triangle
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
1 10 45 120 210 252 210 120 45 10 1
SOFT1002 © University of Sydney, 2005
16
Recursive code
In code, recursive methods often solve
the base case first,
then move to the sub-problem (smaller
n) if it exists, using the recurrence rule.
Eg result for a String in terms of result(s) for
shorter Strings
Eg result for a collection in terms of result(s) for
smaller collections
SOFT1002 © University of Sydney, 2005
17
SOFT1002 © University of Sydney, 2005
18
3
SOFT1002 (Sem2, 2005)
Using Recursion to
Reverse a Sentence (P18.1 from Big Java)
reverse(“1234”) = “4321”
public class Sentence{
private String phrase;
public Sentence(String phrase) {
this.phrase = phrase;
}
public String reverse() {???}
public String toString() {
return phrase;
}
}
SOFT1002 © University of Sydney, 2005
reverse(“1234”) = reverse(“234”) + “1”
reverse(“”) = “”
Remember: base case is first in code!
19
A (non-recursive) recursion checklist
Do I need to return anything?
if (phrase.equals("")) return "";
An empty string.
When the string is not empty.
What is the “thing” that gets closer and closer to the base case? How
can this be done?
The variable: phrase
Removing one character at a time.
What should be returned by the recursive case?
Base Case
When the original string comes to an end.
What is the condition for the recursive case?
A string.
What is the information returned by the base case?
Remember
to return
char c;
String rest;
What is the condition for the base case?
Using Recursion to Reverse a Sentence
public String reverse() {
Yes.
Making
the
sentence
smaller
each time
}
c = phrase.charAt(0);
rest = phrase.substring(1);
Sentence tailSentence = new Sentence(rest);
return tailSentence.reverse() + c;
The returned string from the recursive call + current character.
SOFT1002 © University of Sydney, 2005
Recursive Case
21
Towers of Hanoi
At a remote temple
somewhere in Asia, a
group of monks is working
to move 64 disks from the
leftmost peg (peg A) to the
rightmost peg (peg C). After
all 64 disks have been
moved, the universe will
dissolve! When will this
happen if each disk takes 1
second to move?
Rules of the puzzle:
Only one topmost
disk may be moved
at a time.
Larger disks may not
be placed on top of
smaller disks.
SOFT1002 © University of Sydney, 2005
20
SOFT1002 © University of Sydney, 2005
What is to return?
Reversing a string
23
Call reverse again
But a different target
Return with concatenated info
SOFT1002 © University of Sydney, 2005
22
Towers of Hanoi
For n=64, number of moves = 264-1 1.844 1019.
Astronomers estimate the present age of the
universe to be 20 billion (0.2 1011) years.
Recall that one move takes one second.
Time left in universe = 1.844
1.844 1019 seconds =
5.85
5.85 1011 years (585 billion years).
SOFT1002 © University of Sydney, 2005
24
4
SOFT1002 (Sem2, 2005)
Tower of Hanoi (Non-Recursive)
At the beginning of the subroutine, code is inserted which declares stacks associated with each formal
parameter, each local variable, and the return address for each recursive call. Initially all stacks are
empty.
The label 1 is attached to the first executable program statement.
If the subroutine is a function, i.e., returns some value, then we must replace all return statements
with assignment statements, i.e., we introduce a fresh variable, say z, which has the same type as that
of the function, and replace each return e statement with a z=e statement. Now, each recursive call is
replaced by a set of instructions which do the following:
Store the values of all parameters and local variables in their respective stacks. The stack pointer is the
same for all stacks.
Create the i-the label, i, and store i in the address stack. The value i of this label will be used as the
return address. This label is placed in the subroutine as described in rule 8.
Evaluate the arguments of this call (they may be expressions) and assign these values to the
appropriate formal parameters.
Insert an unconditional branch to the beginning of the subroutine.
If this is a procedure, add the label created above to the statement immediately following the
unconditional branch. If this is a function then follow the unconditional branch by code to use the value
of the variable z in the same way a return statement was handled earlier. The first statement of this
code is given the label that was created above. These steps are sufficient to remove all recursive calls
from a subroutine. Finally, we need to append code just after the last executable statement to do the
following:
If the recursion stacks are empty, then return the value of z, i.e., return z, in case this is a function, or
else simply return.
If the stacks are not empty, then restore the value of all parameters and of all local variables. These
are at the top of the each stack. Use the return label from the corresponding stack and execute a
branch to this label. This can be done using a switch statement.
SOFT1002 © University of Sydney, 2005
1. Move N-1 top disks from source to aux using dest
2. Move (bottom) disk from source to dest
3. Move the N-1 disks from aux to dest using source
1
2
25
Recursive Algorithm
for Hanoi Towers
Hanoi(disks, source, dest, aux): move top disks from source to
dest using aux
Hanoi(disks, source, dest, aux)
If disks is zero
All done, return
Else
Hanoi(disks - 1, source, aux, dest)
Move the single disk from source to dest
Hanoi(disks - 1, aux, dest, source)
SOFT1002 © University of Sydney, 2005
Towers of Hanoi recursively
N disks to be moved from source to dest
using aux for temporary moves
3
SOFT1002 © University of Sydney, 2005
26
Recursive algorithms
“Divide and conquer” is a widely-used
way to solve algorithmic problems
To solve the problem in one case X
find some smaller cases (X1, X2 , etc) of the
same problem
solve them
combine the solutions somehow to get a
solution of the problem for X
27
Recursive binary search
To search for a value in a segment of a
sorted array,
look at the middle element, and then search
only half the segment.
If array[middle] < x, we know that x can’t
occur in part of array before middle, so
search in half-segment above middle;
if array[middle] > x, search in half-segment
below middle.
SOFT1002 © University of Sydney, 2005
28
Recursive Binary Search code
// return an index between left and right inclusive where
// the sortedarray has value x
// return -1 if there is no such index
int binsearch (int x, int[] sortedArray, int left, int right) {
if (left > right) return -1; // empty segment of the array
if (left == right) {
// array segment with one element
if (sortedarray[left] == x) return left;
else return -1;
}
else { // segment of the array has 2 or more elements
int middle = (left + right) / 2;
if (x == sortedArray[middle])
return middle;
else if (x < sortedArray[middle])
return binsearch (x, sortedArray, left, middle-1);
else if (x > sortedArray[middle])
return binsearch (x, sortedArray, middle+1, right);
}
}
Warning: this algorithm is easy to code wrongly!
Always think about boundary cases carefully.
SOFT1002 © University of Sydney, 2005
29
SOFT1002 © University of Sydney, 2005
30
5
SOFT1002 (Sem2, 2005)
Mechanism for recursion
When writing recursive code, we should
not think about how it executes
Mechanics of Method Calls
When any method is called:
Just make sure base case and recurrence
calculation are done properly
However, it is good to know how the
code will execute
Hand trace (very small examples) as desk
Parameters are assigned the values given in the call
Local variables are created
return value of called method can be used in calling
expression
local storage for called method is no longer accessible
The method body is executed
After body completes, execution resumes in the
caller
check or during debugging
SOFT1002 © University of Sydney, 2005
Fresh space (memory) is set aside for the
method’s parameters and local variables
Recursive methods are no different.
31
The program stack
Each time a recursive method calls
itself, current information is pushed
onto the program stack.
The maximum depth of the recurrence
affects how much memory we need.
The number of calls affects how long it
will take!
32
SOFT1002 © University of Sydney, 2005
Executing factorial(2)
public static void main(String [ ] args) {
System.out.println(factorial(2));
}
public int factorial(int n) {
if(n==0) return 1;
else return n * factorial(n-1);
}
Memory
public static void main(String [ ] args) {
System.out.println(factorial(2));
}
public int factorial(int n)
2) {
if(n==0)
if(2
==0)return
return1;1;
else return 2
n * factorial(2
factorial(n-1);
-1);
}
public int factorial(int n)
1) {
if(n==0)
if(1
==0)return
return1;1;
else return 1
n * factorial(1
factorial(n-1);
-1);
}
1
public int factorial(int n)
0) {
if(0
if(n==0)
==0)return
return1;1;
else return 0
n * factorial(0
factorial(n-1);
-1);
}
SOFT1002 © University of Sydney, 2005
33
SOFT1002 © University of Sydney, 2005
Executing factorial(2)
public static void main(String [ ] args) {
System.out.println(factorial(2));
}
public int factorial(int n) {
if(n==0) return 1;
else return n * factorial(n-1);
}
Memory
public static void main(String [ ] args) {
System.out.println(factorial(2));
}
public int factorial(int n)
1) {
if(n==0)
if(1
==0)return
return1;1;
else return 1
n * factorial(1
factorial(n-1);
1 -1);
}
SOFT1002 © University of Sydney, 2005
Executing factorial(2)
public static void main(String [ ] args) {
System.out.println(factorial(2));
}
public int factorial(int n) {
if(n==0) return 1;
else return n * factorial(n-1);
}
public int factorial(int n)
2) {
if(n==0)
if(2
==0)return
return1;1;
else return 2
n * factorial(2
factorial(n-1);
-1);
}
34
Memory
public static void main(String [ ] args) {
System.out.println(factorial(2));
}
public int factorial(int n)
2) {
if(n==0)
if(2
==0)return
return1;1;
else return 2
n * factorial(2
factorial(n-1);
-1);
1
}
2
1
35
SOFT1002 © University of Sydney, 2005
36
6
SOFT1002 (Sem2, 2005)
Executing factorial(2)
public static void main(String [ ] args) {
System.out.println(factorial(2));
}
Memory
public static void main(String [ ] args) {
System.out.println(
2
);
}
Tracing the stack
We now trace the method calls for a
Fibonacci number...
public int factorial(int n) {
if(n==0) return 1;
else return n * factorial(n-1);
}
SOFT1002 © University of Sydney, 2005
37
SOFT1002 © University of Sydney, 2005
The recursion tree for F7
38
Stack analysis
For Fibonacci sequence, what is the maximum depth
of our code?
Fn calls Fn –1, Fn–2, right down to F1: depth n
How long will it take?
Suppose Fn calls the Fibonacci method g(n) times.
Then g(n) = g(n–1) + g(n–2), and g(1) = g(0) = 1 so
g(n) = Fn – yuck!
This recursive method for Fibonacci takes AGES
(there is a faster way, using iteration).
SOFT1002 © University of Sydney, 2005
39
Conquering the stack
The stack analysis shows that if a
recursive function for f(n) has f(n–1) in
it, the depth will be O(n).
Recursive methods that reduce a
problem's size by a constant amount
will always suffer this way.
Methods that divide a problem into
parts which are much smaller usually
run faster and can require less space.
SOFT1002 © University of Sydney, 2005
41
SOFT1002 © University of Sydney, 2005
40
Recursion and Scalability
Bad recursion may lead to exponential
running times (example: fibonacci)
Often much worse than the obvious iterative code
Sensible recursive solutions may have cost
comparable to iterative solutions.
Recursion is often easy to code
But also easy to code wrongly
Often recursive code is easier to understand;
it may offer a neat answer.
SOFT1002 © University of Sydney, 2005
42
7
SOFT1002 (Sem2, 2005)
Recursion for general
programming
Recursion vs Iteration
/**
* Recursive Solution
*/
public int factorial(int n) {
if(n==0) return 1;
else return n * factorial(n-1);
}
Some “functional” programming
languages don’t use iteration at all, but
instead use recursion for these
purposes
/**
* Iterative Solution
*/
public int factorial(int n) {
int result = 1;
for(int i=n; i>0; i - -)
result = result * i;
return result;
}
eg Haskell, ML, Scheme, LISP
They treat a loop as “do one turn, then
do rest of loop”. Weird but true.
As a general rule, use recursion over iteration when recursion
provides a significantly more elegant solution.
43
SOFT1002 © University of Sydney, 2005
SOFT1002 © University of Sydney, 2005
Exercise
Recursive definitions
Write a program that checks whether a
word is a palindrome
Examples:
tattarrattat – the longest palindrome in the
Oxford English Dictionary, coined by James
Joyce in Ulysses for a knock on the door
aibohphobia – a joke word meaning "fear
of palindromes", deliberately constructed
so as to be one
SOFT1002 © University of Sydney, 2005
45
Lists, recursively
A list of objects:
a list
also a list
Recursive definition:
list
is
or
44
A rooted tree is
a node called the root
whose child nodes, if they exist, are also
rooted trees.
A list is
empty, or
an object followed by another list
SOFT1002 © University of Sydney, 2005
46
Data structure recursion
Many complex structured objects can be understood as built up
from sub-objects.
Therefore calculating functions on these objects can easily be
coded recursively:
sometimes code as a method of another class, with a parameter
which is the data structure
(recursive calls will have substructures as parameters);
sometimes code as a method of the data structure class, so the
target object is the data structure
<> (empty)
(recursive calls will have substructures as targets)
The base case is for empty or trivial structure.
Recursive calls reduce the effective size of the structure.
list
SOFT1002 © University of Sydney, 2005
47
SOFT1002 © University of Sydney, 2005
48
8
SOFT1002 (Sem2, 2005)
Any recursive structure
What have we done?
Recursion
concept
recursively defined sequences
And recursive code to calculate them
Recursive methods
And how to code them
Famous recursive Algorithms
...has a simplest case (e.g., empty
structure),
and a way of composing the structure
from similarly defined substructures.
Mechanism for recursive execution
For next week: read Kingston ch 19 on Trees
SOFT1002 © University of Sydney, 2005
49
SOFT1002 © University of Sydney, 2005
50
Recursion and trees
Later we will see how recursion is a
crucial part of functions on trees, and
parsing.
Bear this in mind!
SOFT1002 © University of Sydney, 2005
51
9