Title: Recursion chapters 3
1Recursion (chapters 3 6)
- Concept defining a solution in terms of a
simpler version of the solution
- Recursion is often used instead of iteration
- The Benefits
- often your code will be shorter
- it is usually easier to define the solution
recursively
- and writing the code is just a matter of
implementing the definition
- more elegant solution
- you may not need local variables to implement
your solution
- some solutions are only expressible recursively
(or at least are only easily expressible
recursively)
- this is true of some tree and graph operations,
and search problems that require backtracking
- The Cost
- recursive solutions are often harder to debug
- recursion requires numerous function calling
- this can lead to much poorer run-time
performance
- takes up more memory space (on the run-time
stack)
- in a few cases, solution may be much less
efficient than an iterative solution
2Writing Recursive Solutions
- Four questions for construction recursive
solutions
- how can you define the problem in terms of a
smaller problem?
- how does each recursive call diminish the size of
the problem?
- what instance of the problem can serve as the
base case?
- as the problem size diminishes, will you reach
this base case?
- Factorial
- fact(n) if(n
- Fibonacci
- fib(n) if (n 2)
- Finding largest item in an array
- largest(a, n) if (n 0) return a0 else
return max(an, largest(a, n-1))
- We can also find the largest using a binary
search like strategy
- largest(a, low, high)
- if(low high) return alow
- else return max(largest(a, low, (lowhigh)/2)),
largest(a, (lowhigh)/21, high))
- Writing elements backwards
- output_backward(S, n) if(n0)
- output_backwards(S, n-1)
- output character in S at position (n)
3Mr. Spocks Dilemma
- To see how to set up a definition recursively, we
will consider selecting k things (in any order)
out of n items
- if k n, then there is only one choice, you
select all k items
- if k n, then there are no choices
- if k 0, then there is only 1 choice (choose
nothing)
- these 3 conditions represent our base cases
- otherwise, you must make some choices
- lets consider some arbitrary element, i
- you can either choose i or not choose i
- if you choose i, then there are n-1 things left
to choose between and k-1 choices to make
- if you do not choose i, then there are n-1 things
left to choose between and k choices to make
- Our recursive definition for C(n, k) C(n-1,
k-1) C(n-1, k)
- Our base cases are
- C(n, k) 1 if k 0 or k n
- C(n, k) 0 if k n
- writing a method to compute C(n, k) is now
trivial!
4A Function to Compute c(n, k)
public static int c(int n, int k)
// how many combinations of k items can
be chosen out of a set of n? // this func
tion determines this value recursively
// Precondition n and k are non-negative
integer values // Postcondition returns
c(n, k) if ( (k 0) (k n) ) r
eturn 1 else if (k n) return 0
else return c(n 1, k 1) c(n 1, k)
c(5, 3) c(4, 2) c(4, 3) c(3, 1) c(3, 2)
c(3, 2) c(3, 3) c(2, 0) c(2, 1) c(2
, 1) c(2, 2) c(2, 1) c(2, 2) 1
1 c(1, 0) c(1, 1) c(1, 0) c(1, 1)
1 c(1, 0) c(1, 1) 1 1 10
How would you implement this iteratively? Since
we have defined c(n, k) in terms of itself, it i
s not easily apparent how to compute c(n, k)
through an iterative-based formula
5Towers of Hanoi
- The Emperor of Hanoi (a city in Vietnam) invented
this problem to find the wisest person to succeed
him
- given n disks and 3 poles (we will call them A, B
and C) where each disk is of a different
weight/size such that only a smaller disk can sit
on top of a heavier/bigger disk - find a pattern of movements to move the n disks
from pole A to pole B using pole C as an
intermediate, moving 1 disk at a time
towers(int n, char a, char b, char c)
if(n 1) System.out.println(move from
tower a to tower b)
else towers(n-1, a, c, b)
System.out.println(move from
tower a to tower b)
towers(n-1, c, b, a)
Run-trace of the recursive calls for n 3
6Finding the Kth Smallest Item
- Rather than searching for the largest int in an
array, what if you wanted to find the kth largest
or smallest?
- You could sort the array and then return ak-1
(kth largest) or an-k1 (kth smallest)
- However, sorting is generally reserved for a
situation where you want all of the items in
order, here you only want to find a particular
item - You could also solve this by implementing part of
the selection sort algorithm
- for(i0i
- find the smallest item and swap it with ai
- when done, the kth smallest item is in ak-1
- As straight-forward as both of these approaches
are, a recursive solution might be more efficient
- or it may not be more efficient, but it will not
be less efficient
7Partitioning an Array
- Imagine that you have an array of int values in
no particular order
- Select the element at the beginning, we will call
this the pivot value
- Work your way through the array moving all
elements pivot to the right side of the array
and all elements
array - Place the pivot in the space between those those
- Now the array is partitioned so that the pivot is
in its proper place (no other values are in their
proper place though)
- Now, if the pivot is in ak-1 then the pivot is
the kth smallest value
- But if the pivot is in some position am where m
k-1 then we recursively repeat the partitioning
with the upper half of the array
- and if m partitioning with the lower half of the array
8Example
- If you have learned the quick sort algorithm, the
partition routine is the same
- If not, we will cover this in chapter 10
- We will find that this routine of finding the kth
smallest can work in as little as n instructions,
but may take as many as sorting the whole array
depending on the order of the data
Array 4, 7, 3, 6, 8, 1, 9, 2
We want to find the 3rd smallest value
pivot 4, partition array to become 2, 1, 3,
4, 8, 6, 9, 7 The pivot value was inserted at
a3 (the 4th smallest item) So we continue with
the lower part of the array 2, 1, 3, p
ivot 2, partition the lower part of the array
to become 1, 2, 3, The pivot value was in
serted into a1 (the 2nd smallest item)
So we continue with the upper part of what were
just working on , 3, The pivot is 3 at
position a2, so this is the 3rd smallest value,
we are done, third smallest a2 3
9Recursive Definition and Solution
Assume A is an array, first is initially 0, last
is initially n-1 pivotIndex is the location whe
re the pivot has been inserted
after performing partition, p is the pivot value
kSmall(k, A, first, last) kSmall(k, A, firs
t, pivotIndex-1) if k p if k pivotIndex first 1
kSmall(k (pivotIndex first
1), A, pivotIndex 1, last) if k piv
otIndex first 1
- Notice how easy it is to go from the recursive
definition to the Java code
- Partition will be covered in chapter 10
- Partition itself is implemented iteratively
public int kSmall(int k, int a, int first, int
last) // choose pivot p from afirst..la
st and partition array // around p with p
ivotIndex being the location p was inserted
if(k kSmall(k, a, first, pivotIndex 1)
else if (k pivotIndex first 1)
return p else return kSmall(k (pivotIndex
first 1), a, pivotIndex 1, last)
108 Queens Problem
- We continue our examination of recursion (in
chapter 6) with a class search problem that
requires backtracking
- The idea of backtracking is that if you have made
a wrong selection in your search, back up to the
point of your latest decision and try a new
choice - in the 8 queens problem, you want to position 8
queens on a chessboard so that no queen can
capture any other
- where do you place them?
How many possible choices are there?
Using c(n, k), n 64 (64 places for queens) and
k 8 (8 queens to place), c(64, 8) 4 billion
! We could try every possible board configurati
on of c(64, 8) but many are irrelevant (we wont
place a queen in a column that already has a que
en)
118-Queen Solution
- A better solution is to work column-by-column
- For the first column, place a queen in a row
- Now expand the solution
- expansion requires that you move onto the next
column and iterate through each row until you
find a row where the newly placed queen cannot be
captured by any previous queen, then you expand - the expand part is recursive
- if you reach a column and search all 8 rows and
cant find a place for your queen, then you must
backtrack go back to a previous recursive level
and continue searching
This strategy requires at most 8! boards before
it has a solution and determines that there isn
t one
(8! 40,320)
128-Queens Solution Pseudocode
placeQueens(column) if(column 8) problem is
solved, print solution and return that
the problem has been solved else
row 1 while(row yet solved) tentatively place
queen in square de
termine if queen can be captured at this
position if queen can not be cap
tured, than insert
into board and call
placeQueens(column1) else remov
e queen from and
continue searching by doing
row try another row (if a queen could
not be placed at the next column, recursion r
eturns us here where we backtrack by removing
the queen from the board, adding 1 to row, an
d trying again
see pages 295-299 for a partial implementation in
Java
13Defining a Language
- A language is a set of symbols (from some finite
alphabet) and rules (called a grammar) for
combining the symbols
- A recognition algorithm is an algorithm that,
given a string of symbols, determines if the
string is part of that language or not
- Example positive twos complement is a language
composed of 1s and 0s such that the leading digit
is not a 1
- We can write a program that given an input String
can determine if the String is an element of the
positive twos complement language
- Most commonly, languages are more complex than
binary
- so we need more complex grammars that might
entail dozens or more rules
- to recognize whether a String is part of the
language or not, our recognition algorithm will
commonly be implemented recursively because of
the complexity - Example The blue can was used to hold 20
gallons of paint.
- When parsing this sentence and you reach can,
is it a noun, verb or auxiliary verb?
- Assume you select verb. You will discover that
can is not the verb since was used to hold is
the verb, so you have to backtrack and revise
your choice of can from verb to noun
14Language Recognizers
- Consider a language to recognize legal Java
identifiers (identifiers must contain only
letters, digits, _ and and must start with a
letter, _, ) - isLegal(String s)
- if s.length( ) 1 and s.charAt(0) is a letter,
_ or then return true
- else if s.length( ) 1 then return false
- else if s.charAt(s.length( )-1) is a letter, _,
or digit then return isLegal(s) where s is s
without the last character
- else return false
- Another recognizer might look for palindromes
(something of the form abcdcba) and for languages
of the form AnBn
isAnBn(w) if(length of w is 0) return true
else if(w.charAt(0) A
and w.charAt(last) B) return isA
nBn(w) where w is w without first and last c
haracters else return false
isPal(w) if(w is the empty string or w is of
length 1) return true else if(w.charAt(0
) w.charAt(last)) return isPal(w) where w
w without the first and last characters
else return false
15Prefix Notation Recognizer
- You have most likely covered pre and postfix
notation in 262
- This is also covered here in part in chapter 5
and in more detail in chapter 6
- Given a string S, is it in legal prefix
notation?
- Our grammar can be written as follows
-
- this notation is known as BNF grammar
- Notice how it is defined recursively
- a prefix notation is either an identifier, or it
is an operator (, -, , /) followed by two
prefix expressions, which themselves are either
identifiers or operators followed by two prefix
expressions, etc - so A / B C E F is a legal prefix expression
- and A / B C E F is not
16Prefix Recognizer Pseudocode
isPre( ) size length of expression strEx
p lastChar endPre(0, size 1) if
(lastChar 0 lastChar size 1)
return true else return false en
dPre(first, last) if(first
last) return -1 ch character at position
first of strExp if (ch is an identifier) r
eturn first else if (ch is an operator)
firstEnd endPre(first1, last)
if (firstEnd -1) return
endPre(firstEnd1, last) else return
-1 else return -1
- A prefix expression is either an identifier or an
operator followed by two prefix expressions
- If, when parsing the expression, we find an
operator, then we will recursively seek two
prefix expressions
- If there are two expressions such that the first
starts right after the operator (at first) and
the second ends at the end of the string, then we
have a legal prefix expression - Finding the end of the two prefix expressions may
require that we seek prefix expressions
recursively such that the first starts at first
and the second ends at last - A more complete explanation is given in the
textbook on pages 308-312
17Postfix Expressions
- Below are two sets of code to evaluate a postfix
expression and to convert an expression from a
prefix to a postfix expression
convert(String prefix) ch first char
of prefix delete first char of prefix
if(ch is identifier) return ch else
item1 convert(prefix)
delete next char of prefix
item2 convert(prefix)
return item1 item2 ch
postfix(String expr) if(exp is a sing
le value) return expr.getValue( ) else
return item1 expr0 i
tem2 expr1 operator expr2
if(operator )
return(postfix(item1) postfix(item2))
else if(operator )
return(postfix(item1) postfix(item2))
else if(operator -) return(postfix(item
2) postfix(item1)) else return(postifx(item2
) / postfix(item1))
18Cost of Hanoi and Fibonacci
- If you examine the Hanoi algorithm, you can
easily identify the recurrence
- H(n) H(n-1) 1 H(n-1)
- 2H(n-1) 1
- That is, to perform Hanoi with n disks, you must
solve the problem of Hanoi with n-1 disks, do one
move, and then solve the problem of Hanoi with
n-1 disks - Lets try this out for n 4
- H(4) 2H(3) 1
- H(3) 2H(2) 1
- H(2) 2H(1) 1
- H(1) 1
- H(2) 211 3
- H(3) 23 1 7
- H(4) 27 1 15
- In general, the solution for n is twice the
solution for n-1 plus 1 extra step
- The Towers of Hanois solution is 2n-11
- for n 64, this is a LARGE number!
- The Fibonacci algorithm has the recurrence
- F(n) F(n-1) F(n-2)
- This is not quite the same as Hanoi, but in
essence, to solve the problem for F(n), we need
two more solutions that are close to F(n)
- Both of these problems have recursive solutions
that are known as intractible it takes too much
time to solve them when n is large