RecursionChapter 4 – Self-Reference
Recursive Definitions
Inductive Proofs
Implementing Recursion
© schmiedecke 09 In2-2-Algor.Paradigms 2
Imperative Algorithms
• Based on a basic abstract machine model
- linear execution model
- storage
- control structures
� von Neumann architecture
// Factorial
public int fac1 (int i) {
int result = 1;
for (int j=i; j>1; j--) result *= j;
return result;
}
i
result
j
© schmiedecke 09 In2-2-Algor.Paradigms 3
Applicative Algorithms
• Based on the principle of inductive definition
• Evaluation pattern taken from mathematical function evaluation
• Recursion is a basic step
f(1) = 1
f(n) = n*f(n-1)// Factorial recursive
public int fac1 (int i) {
if (i<2) return 1;
return i*fac1(i-1);
}
© schmiedecke 09 In2-2-Algor.Paradigms 4
Applicative Algorithms
• Idea is to apply a function to an argument
• so you need a function definition like
f(1) = 1
f(n) = n*f(n-1)
• and a method for evaluating that function for a given argument:
- Replace variable(s) by arguments
- Replace left hand side by right hand side
- Repeat until termination
So evaluation is described as a set of rewriting rules.
© schmiedecke 09 In2-2-Algor.Paradigms 5
More Formally
• An applicative algorithm is a list of functions
f0(v01,...,v0n) = t0(v01,...,v0n)
...
fm(vm1,...,vmn) = tm(vm1,...,vmn)
function name function expression
function parameters
• A function evaluation for a given set of arguments
- replace the parameters in the function expression by the corresponding arguments
- evaluate all basic operations and functions in the function expression from left to right according to basic priorities
• The semantics of the applicative algorithm is the evaluation of the first function.
© schmiedecke 09 In2-2-Algor.Paradigms 6
Notation
• To make functions conform the algorithm definition, we introduce the conditional operator:
• Instead of:
f(1) = 1
f(n) = n*f(n-1)
• We write:
f(n) = if n=1 then 1 else n*f(n-1) fi
• In Java, the conditional operator is (?:)
int f(int n) { return n==1 ? 1 : n*f(n-1); }
© schmiedecke 09 In2-2-Algor.Paradigms 7
The Semantics of Recursive Functions
• is the successive application of rewriting rules:
fac(5) = 5 * fac(4)
= 5 * 4 * fac(3)
= 5 * 4 * 3 * fac(2)
= 5 * 4 * 3 * 2 * fac(1)
= 5 * 4 * 3 * 2 * 1
© schmiedecke 09 In2-2-Algor.Paradigms 8
The Execution of Recursive Functions
• uses a stack to save "partial rewritings":
call fac(5) ���� push 5, call method
call fac(4) ���� push 4, call method
call fac(3) ���� push 3, call method
call fac(2) ���� push 2, call method
call fac(1)���� push 1, call method
base case
contin.fac(2���� pop, pop, mult(2,1), push 2
contin.fac(3) ���� pop, pop, mult(3,2), push 6
contin.fac(4) ���� pop, pop, mult(6,4), push 24
contin.fac(5) ���� pop, pop, mult(24,5),push 120
© schmiedecke 09 In2-2-Algor.Paradigms 9
Recursion is Natural
• To learn downhill skiing
- climb up a few metres and learn to plow a right bend to the
bottom
- climb up a few metres and learn to plow a left bend to the
bottom
- take the lift all the way up and combine right and left bends until you reach the bottom
• Let's apply this technique to the Towers of Hanoi...
© schmiedecke 09 In2-2-Algor.Paradigms 10
Towers of Hanoi
� For a stack of 1:move disc from start to dest
� For a stack of 2:move disc from start to auxmove disc from start to destmove disc from aux to dest
Call this: move 2 from start to dest using aux
� For a stack of 3:move 2 from start to aux using destmove disc from start to destmove 2 from aux to dest using start
source:http://www.cut-the-knot.org/recurrence/hanoi.shtml
Puzzle invented by Edouard Lucas
in 1883:
• Put an ordered stack of discs
from one peg to another, using athird peg,
• such that all stacks are ordered
at all times.
© schmiedecke 09 In2-2-Algor.Paradigms 11
Inductive Aproach
• A stack of one can be moved directly to the target pole.
• Apparently, we can move a bigger stack of discs correctly to the target pole if we have an auxiliary pole. We have tested it for 2 and 3.
• Use this idea to first move the top of the stack to the auxiliary pole
• leaving the biggest disc behind to be moved directly to the target.
• move it to the target.
• Then move the top of the stack back to the origin, leaving the auxiliary pole empty again for the next step.
• ... until the stack size is 1.
• move the top of the stack is a recursive application of hanoi to a smaller stack!
• Parameters?
© schmiedecke 09 In2-2-Algor.Paradigms 12
Express Hanoi in Java
public class Hanoi extends Applet {
private Tower start, aux, dest;
private int size;
public Hanoi () {
this.size = 8; // replace by reading from ComboBox..
start = new Tower(size);
aux = new Tower(0);
dest = new Tower(0);
}
public void start() { move(size, start, dest, aux); }
private void move(int number, Tower s, Tower d, Tower a) {
if (number==1) { d.add(s.remove()); repaint(); }
else { move(n-1,s,a,d); move(1,s,d,a); move(n-1,a,d,s); }
}
public void paint(Graphics g) {
start.display(g, size, 50, 10);
aux.display(g, size, 100, 10);
dest.display(g, size, 150, 10);
}
}
© schmiedecke 09 In2-2-Algor.Paradigms 13
Properties of Hanoi
• Is it correct?
Theorem: if a disc is moved to a stack, it is smaller that ist base
Inductive proof:
� Any movement of a partial stack ends with one stack empty (Lemma1)
� if a disc is moved which is bigger than the top of a stack, it is moved to an empty stack (Lemma2)
- true for first step.
- true for second step.
- if a partial stack of n-1 discs has been moved properly, a biggerdisc is moved to an empty stack in the nth step.
© schmiedecke 09 In2-2-Algor.Paradigms 14
Towers of Hanoi - Complexity
© schmiedecke 09 In2-2-Algor.Paradigms 15
Complexity Argument
© schmiedecke 09 In2-2-Algor.Paradigms 16
© schmiedecke 09 In2-2-Algor.Paradigms 17
Recursion is Powerful
• It can be shown that every algorithm that can be specified
can be specified recursively!
• So, recursion has maximum computation potential.
• In most situations, you can replace recursion by iteration.
• Can you do so for Hanoi?
• Later we will learn about a certain type of complex
recursion which cannot be expressed iteratively (so-called
R Gramars).
© schmiedecke 09 In2-2-Algor.Paradigms 18
Replacing Recursion by Iteration
• Lets consider two "famous" recusive algorithms:
• the factorial n!
fac(n) = if n=1 then 1 else n*f(n-1) fi
• and the Fibonacci numbers (rabbit generations)
fib(n) = if n<=1 then 1 else fib(n-1)+fib(n-2)
© schmiedecke 09 In2-2-Algor.Paradigms 19
Replacing Recursion by Iteration
• the factorial n!
fac(n) = if n=1 then 1 else n*f(n-1) fi
int fac(int n) {
int result = 1;
for (int i=2; i<=n; i++) result *= i;
return result;
}
Solution is straightforward:
use local variable to store intermediate results
otherwise returned by recursive calls
© schmiedecke 09 In2-2-Algor.Paradigms 20
Replacing Recursion by Iteration
• and the Fibonacci numbers (rabbit generations)
fib(n) = if n<=1 then 1 else fib(n-1)+fib(n-2)
int fib(int n) {int current = 1, prev = 0, temp;
for (int i=1; i<n; i++)
{ temp = current;
current = current + prev;
prev = temp;
}
return current;
}
Same idea,
use local variables to store values that
would result from recursive calls
© schmiedecke 09 In2-2-Algor.Paradigms 21
The Termination Problem
• What is the domain of the factorial function?
fac(n) = if n=1 then 1 else n*fac(n-1) fi
int fac(int n) {int result = 1;
for (int i=2; i<=n; i++) result *= i;
return result;
}
• Oh dear, the applicative agorithm will run indefinitely for negative n.....
• Generally, algorithms must be suspected of being partial functions,
• i.e they may not terminate on certain arguments.
• It may be hard to find out, or even impossible....
• There are algorithms for which we do not know whether they terminate for all valid input data. (There is one in your lab assignments ☺)
© schmiedecke 09 In2-2-Algor.Paradigms 22
Tail Recursion
• There is a "nice" type of recursion, called tail recursion:
• Such functions have a direct solution for a fixed set of arguments.
• For every recursive call, a monotonic modification is made to the parameters moving them "towards" the direct solution arguments.
• Tail recursion is easily translated into iteration.
• The termination of tail recursive functions can generally be determined easily (but not always).
• So, in programming, try to use tail recursion and make itobvious.
© schmiedecke 09 In2-2-Algor.Paradigms 23
Recursion and Complexity
• How do you determine the complexity of a recursive function?
• Use recursive call as basic step.How much does it cost except for the rec. call?How many recursions occur?
the number of calls depends on the parameter value.
• Multipy step cost by recursion depth.
• Fac is called n times, each step has constant complexity, so the complexity is O(n):fac(n) = if n=1 then 1 else n*f(n-1) fi
• Fib is called 2*n times, so the complexity is O(n) too:fic(n) = if (n=1|n=2) then 1 else fib(n-1)+fib(n-2) fi
• Those were easy to assess, because the parameter is decreasing steadily. But what about this function:f(x) = if x>100 then x-10 else f(f(x+11)) fi
© schmiedecke 09 In2-2-Algor.Paradigms 24
Incredible Growth:The Ackermann Function
• Here is the worst example ever known:
ack(x,y) = if x<=0 then y+1
else if y<=0 then f(x-1,1)
else f(x-1,f(x, y-1))
• Some function results
f(0,0) = 1f(1,0) = 2
f(1,1) = 3
f(2,2) = 7
f(3,3) = 61
f(4,4) = 2**2**2**2**2**2**2 –3 = 2**2**2**65536 –3
• As this growth is achieved by adding 1, the time consumption, i.e. the functions comlexity, is just as incredible
© schmiedecke 09 In2-2-Algor.Paradigms 25
Mathematics of Applicative Programming:The Lambda Calculus
• Inventend by Church and Kleene in the 1930ies.
• Mathematical model of function application (to arguments)
• Computability question: Which problems can be solved algorithmically, and can it be decided whether a problem has a solution.
• Abstract machine for applicative algorithms: If it terminates on a given algorithm, it is said to be computable.
© schmiedecke 09 In2-2-Algor.Paradigms 26
Lamda Calculus Simplified
• Lambda stands for argument binding:
• Function f applied to argument a results r:Lambda x f a � r
• If f is x+2, and a is 7 we getLambda x x+2 7� 9
• With nesting:Lambda x x+(Lambda y y+3 4) 5
� Lambda x x+7 5� 12
• So, computation is described as rewriting of one list of terms by another
© schmiedecke 09 In2-2-Algor.Paradigms 27
The LISP language
• Describing computation as a set of rewriting rules has many offsprings
in artificial intelligence.
• Here is a very early rewriting language, invented in 1958 by John Mc
Carthy:
• LISP – List Programming
- The idea is that every program is a list of rewriting rules.
- The result of a rewriting is again a list.
- Terminal symbols can be specified to be excluded from
rewriting.
- Lambda binding is applied to introduce parameters.
- Special list operations cons, car and cdr allow list
manipulation and traversion.
• LISP is still in use – implemented in Unix, control language in the EMACS editior.
© schmiedecke 09 In2-2-Algor.Paradigms 28
Some LISP
(DEFUN THROW-DIE ()
(+ (RANDOM 6) 1) )
(DEFUN THROW-DICE ()
(LIST (THROW-DIE) (THROW-DIE)) )
(DEFUN BOXCARS-P (A B)
(AND (EQUAL '6 A) (EQUAL '6 B) ) )
(DEFUN SNAKE-EYES-P (A B)
(AND (EQUAL '1 A) (EQUAL '1 B) ) )
© schmiedecke 09 In2-2-Algor.Paradigms 29
Factorial in LISP
(DEFUN FAC (N)
(COND ((EQUAL N 1) 1)
((MULT N (FAC (MINUS N 1)) ) ) )
Evaluate Call of FAC (3) by Rewriting:
����FAC (3)
�MULT 3 (FAC (MINUS 3 1))
�MULT 3 (FAC (2))
�MULT 3 (MULT 2 (FAC (MINUS 2 1))
�MULT 3 (MULT 2 (FAC (1)))
�MULT 3 (MULT 2 1)
�MULT 3 2
�6
© schmiedecke 09 In2-2-Algor.Paradigms 30
What's so Charming about LISP?
• Fully declarative – non-operational.
• Completely general.
• Easy to extend programs by adding definitions.
• Results can be added as (redundant) definitions,such as
(DEFUN FAC(3) 6)
• � Supports artifical "learning"
© schmiedecke 09 In2-2-Algor.Paradigms 31
Recursive Fun:Fractals
• If you draw a simple shape repeatedly with decreasing
size and changing orientation, you get a pattern.
• The simplest way to do that is recursion:
- draw a pattern
- spawning a similar pattern of smaller size and modified position and orientation
- which again spawns another smaller pattern
- until the size is below a certain limit
• Such patterns are usually called fractals...
• And they are fun doing ☺
© schmiedecke 09 In2-2-Algor.Paradigms 32
Two Fractal Classics
• Pythagoras Tree:- Draw a square starting from ist base line
- then, for a given angle, draw a pair of
Pythagoras squares opposite to its base line
- continue for each sqare until too small.
• Sierpinski's Triangle:- Take a line between two points
- Replace it by three lines of half that length,
turning -60°, +60°,+60° left
- Replace each of these lines again by three,
turning +60°, -60°, -60° for the first line,
-60°, +60°, +60° for the middle line,
+60°, -60°, -60° for the last line.
- Repeat for each line, changing orientation every time
until too small
© schmiedecke 09 In2-2-Algor.Paradigms 33
Famous simple Fractals
Pythagoras Fractal Szerpinsky Triangle
© schmiedecke 09 In2-2-Algor.Paradigms 34
Pythagoras
public void paint (Graphics g) {
g.clearRect(0,0,500,500);
paintTree(g, double x1, double y1, // tree defined
double x2, double y2); // by base line
}
// paint tree paints the major square
// determines the base lines of the smaller squares
// and calls itself recursively for the two smaller squares
© schmiedecke 09 In2-2-Algor.Paradigms 35
Everybody loves
Mandelbrodt Fractals.
Have Fun...
That's enough for today!!
Next time, we will return to recursion toconsider the most general problem solver:
Recursive Backtracking,
Before Jumping into the Tradition of Sorting and Searching