ch05 - Ch 5 – Recursion •  •  •  • ...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Ch 5 – Recursion •  •  •  •  •  •  •  •  •  Motivation Problem Solving Tips Factorial Array Reversal Sum Squares Calculates Fibonacci Numbers Ackerman’s Function Binary Search •  •  •  •  Linked List Traversal Towers of Hanoi Recursive Flood Fill Defining Languages –  Languages –  Palindrome –  Expressions •  8 Queens Motivation •  Recursion is a powerful problem solving tool that is based on mathematical induction •  The idea is to express solution to problem X in term of smaller versions of X •  All recursive solutions can be implemented iteratively, but they often require less code when implemented recursively •  Recursion also gives an easy way to “backtrack” if a searching algorithm reaches a dead end Problem Solving Tip •  Think like a manager! •  Take large problem and break into parts to delegate to employees •  Tell employees to think like managers and subdivide their tasks •  Always need a termination condition so employees know when to stop dividing and delegating •  For speed, try to divide the problem in half (or even smaller). Factorial Example N! = N.(N-1).(N-2)….3.2.1 Really (N-1)! N! = N.(N-1)! •  Hence, solution to N factorial can be written as a smaller factorial problem •  We need a terminating condition to stop this sequence of recursive replacements. 1! = 1 0! = 1 Common stopping conditions Factorial Implementation int

factorial
(int
num)
 {
 


//
check
termina4ng
condi4on
 


if
(num
<=
1)
 





return
1;
 


//
handle
recursive
case
 


else
 





return
(num
*
factorial(num
–
1));
 }
 a smaller problem •  The code above has same number of multiplies as an iterative solution. •  Recursive answer slightly slower due to function call overhead. Tracing Factorial Execution •  Box method tracing is helpful for showing what recursive function do •  Pretend we have multiple copies of code and draw a box for each •  Draw arrows to show when a function calls itself (pointing to new box) and when it returns to calling function. main fact(3) 6 Show return values on arrows back fact(2) 2 fact(1) 1 Tracing Factorial Execution •  Can also show values of local variables inside each box •  Normally show parameters at top of box Array Reversal Example •  Assume we are given array of N values to reverse N N-2 Algorithm:

   swap
first
and
last
elements
in
array
   recursively
reverse
N‐2
elements
in
between
 •  Problem gets smaller at each step •  Stop when asked to swap 0 or 1 elements Array Reversal Implementation void
reverse(int
data,
int
low,
int
high)
 {
 
//
check
termina4on
condi4on
 
int
size
=
high
–
low
+
1;
 
if
(size
<=
1)
 

 
return;
 }
 
//
handle
recursive
case
 
else
 
{
 

 
int
temp
=
data[low];

 

 
data[low]
=
data[high];
 

 
data[high]
=
temp;
 

 

 

 
reverse
(data,
low
+
1,
high
–
1);
 
}
 int
main
()
 {
 
int
d[9]
=
{3,
1,
4,
1,
5,
9,
2,
6,
5}
 
reverse
(d,
0,
8);
 
…
 }
 Tracing Array Reversal •  Tracing execution of reverse(d,0,8) using the box methods. main r(d,0,8) r(d,1,7) r(d,2,6) r(d,3,5) r(d,4,4) •  Each recursive call the array to reverse is two shorter •  What happens if we reverse an odd number of elements? Sum Squares Example •  Task is to compute sum of squares for given range of integers •  Could use iteration •  Could also use recursion and a divide and conquer approach Sum Squares Example SS(1,5) SS(1,3) SS(4,5) SS(4,4) SS(5,5) SS(6,10) •  Each time divide range in half and use SS to calculate each half Sum Squares Implementation int
sum_squares(int
low,
int
high)
 {
 
//
check
termina4on
condi4on
 
if
(low
==
high)
 

 
return
low*low;
 }
 
//
handle
recursive
case
 
else
 
{
 

 
int
mid
=
(low
+
high)/2;

 

 
return
(sum_squares(low,
mid)
+
 
 

 








sum_squares(mid,
high));
 
}
 •  No4ce
that
 recursion
stops
 when
we
only
have
 one
number
to
sum
 •  No4ce
that
each
 recursive
call
cuts
 the
size
of
original
 program
in
half
 (much
beYer
than
 subtrac4ng
1).
 Tracing Sum Squares Execution •  Tracing execution of SS(1,5) using the box methods. •  Notice that at each level of recursion the number of boxes doubles SS(1,1) SS(1.2) 1 SS(2,2) SS(1,3) SS(3,3) 5 main SS(1,5) 4 9 14 55 •  This is typical for solution that use divide and conquer •  More on the analysis of this recursion later … SS(4,4) SS(4,5) 41 16 SS(5,5) 25 Calculation Recursively •  Assume we are given X and p and must compute •  Could do this iteratively by multiplying X by itself p-1 times (p is integer) •  Could also solve recursively using the following: when p is even when p is odd (assume p/2 truncates downward) terminating conditions Implementation float
power(float
x,
int
p)
 {
 
//
check
termina4on
condi4on
 
if
(p
==
0)
 

 
return
1;
 
else
if
(p==1)
 

 
return
X;
 }
 
//
handle
recursive
case
 
else
if
(p%2
==
0)
 
{
 

 
float
temp
=
power
(X,
p/2);
 

 
return
temp
*
temp;
 
}
 
else
if
(p%2
==
1)
 
{
 

 
float
temp
=
power
(X,
p/2);
 

 
return
X
*
temp
*
temp;
 
}
 or we could call power(X, 1+p/2) •  Does
this
code
work
 for
all
p?
 Tracing •  Trace execution of power(2,8) main p(2,8) 256 p(2,4) 16 P(2,2) 4 p(2,1) 2 •  Calculated solution with 3 multiplies in stead of 7 •  In general, we get answer after steps, which is much better than simple iterative solution Fibonacci Numbers •  Sequence of numbers that model an explosive growth rate (like rabbit reproduction) F(N) 1 1 2 3 5 8 13 21 34 55 N 1 2 3 4 5 6 7 8 9 10 •  Notice that F(N) = F(N-1) + F(N-2) except at start where F(1) = F(2) = 1 •  Possible to write an iterative program to compute F(N) (actually my first program) •  Can also implement recursively Fibonacci Implementation int
Fib(int
num)
 {
 
//
check
termina4on
condi4on
 
if
(num
<=
2)
 

 
return
1;
 }
 
//
handle
recursive
case
 
else
 

 
return
(Fib(num
–
1)
+
Fib(num
–
2);
 •  No4ce
two
recursive
calls
in
Fib.
 (This
will
cause
an
explosion
in
func4on
calls)
 Tracing Fibonacci Fib(2) Fib(3) main •  What happens if we call Fib(4) 1 Fib(1) Fib(4) 2 3 Fib(2) 1 1 Fib(4) main •  What happens if we call Fib(5) Calls like above Fib(5) 2 3 Fib(3) 1 Calls like above Fibonacci Analysis •  How many recursive function calls are needed to compute F(N)? •  Based on our trace of execution calls(N) = calls(N-1) + calls(N-2) + 1 calls(2) = calls(1) = 1 calls(N) 1 1 3 5 9 15 25 41 67 N 1 2 3 4 5 6 7 8 9 •  This is slightly worse than the actual Fibonacci sequence itself Ackerman’s Function •  Function designed to be very recursive and grow rapidly A (m, n) = n+1 if m = 0 A(m – 1, 1) if n = 0 A(m – 1, A(m, n-1)) otherwise Examples: A(0,3) = 4 A(1,0) = A(0,1) = 2 A(1,1) = A(0, A(1,0)) = A(0, 2) = 3 A(2,0) = A(1, 1) = 3 A(2,1) = A(1,A(2,0)) = 2 = A(1,3) = A(0, A(1,2) etc … Ackerman Implementation Int
Ack(int
m,
int
n)
 {
 
//
check
termina4on
condi4on
 
if
(m
==
0)
 

 
return
n+1;
 
//
handle
simple
recursion

 
else
if
(n
==
0)
 

 return
(Ack(m‐1,
1));
 }
 
//
handle
messy
recursion
 
else
 
{
 

 
int
temp
=
Ack(m,
n‐1);
 

 
return
(Ack(m‐1,
temp));
 
}
 •  No4ce
that
the
m
value
decreases
in
recursive
 calls,
but
n
oden
increases
 Tracing Ackerman •  Consider execution of Ack(2,1) Ack(1,0) Ack(2,0) Ack(2,1) 2 Ack(0,2) 3 3 5 Ack(1,1) 3 Ack(1,3) Ack(1,2) Ack(1,1) Ack(1,0) 5 4 5 Ack(0,4) 3 4 2 Ack(0,3) Ack(0,2) 3 •  Notice that certain values are recalculated during this process (Ack(1,1)). •  Systems will crash with stack over for medium values of m, n above Binary Search •  Assume we are given an array of data values in sorted order. •  What is the fastest way to search for a given value? •  Brute force search scans from L->R •  Better approach is to divide and conquer •  Algorithm: –  Look at value in middle of the array –  If less than desired value, search half to right –  If greater than desired value, search half to left •  Because problem is half as large in each recursive step, the algorithm is very fast Binary Search Implementation int
search(int
data,
int
value,
int
low,
int
high)
 {
 
int
mid
=
(low
+
high)/2;
 
//
check
termina4on
condi4on
 
if
(low
>
high)
 

 
return
‐1;
//not
found
 
else
if
(data[mid]
==
value)
 

 
return
mid;
//found
 
//
handle
recursive
case
 
else
if
(data[mid]
>
value)
 

 
return
search(data,
value,
low,
mid
–
1);
 }
 
else
if
(data[mid]
<
value)
 

 
return
search(data,
value,
mid
+
1,
high);

 •  No4ce
that
we
use
mid
–
1
and
mid
+
1
in
 recursive
calls
to
avoid
looking
at
mid
again.
 Tracing Binary Search •  Search for value 7 in array below data: 1 3 4 4 5 6 6 7 9 14 16 0 1 2 3 4 5 6 7 8 9 10 mid = (0 + 10)/2 = 5, data[5] <7, so search right 6 7 9 14 16 6 7 8 9 10 mid = (6 + 10)/2 = 8, data[8] >7, so search left 6 7 6 7 mid = (6 + 7)/2 = 8, data[6] <7, so search right 7 7 mid = (7 + 7)/2 = 7, data[7] <7, so found data! Linked List Traversal •  A linked list is sometimes called a recursive data type head first node a smaller linked list •  Hence to traverse a list, we can visit first node and then call the traverse function recursively to process nodes after first node. •  Need to terminate process when we have an empty list. Recursive List Print •  Assume
we
have
data
node
declared
as
a
struct
with
“value”
 and
“next”
fields
 void
print(Node
*prt)
 {
 
//
handle
termina4ng
condi4on
 
if
(ptr
==
NULL)
 

 
return;

 
//
print
value
 
cout
<<
“value=“
<<
prt‐>value
<<
endl;
 }
 
//
handle
recursion
 
print(ptr‐>next);

 •  This
func4on
will
make
one
recursive
call
for
each
node
in
 the
linked
list.
(No4ce
there
is
no
while
loop
above!).
 •  What
happens
if
we
reverse
the
cout
and
recursive
print
call
 above??
 Towers of Hanoi •  A
classic
puzzle
 •  Start
with
three
pegs
and
a
stack
of
disks
on
first
peg.
 •  Disks
are
of
increasing
sizes
with
the
smallest
on
top
and
 largest
on
boYom.
 •  Goal
is
to
move
1
disk
at
a
4me
from
one
peg
to
another
and
 end
up
with
all
disks
on
last
peg.
 •  Rule:
You
cannot
put
a
larger
disk
on
top
of
a
smaller
disk.
 A B C Example A 321 A A B B 21 B C C3 C 321 A 32 A1 done
!
 B B2 C1 C3 A3 A1 B2 B C1 C 32 A3 B 21 C •  No4ce
that
at
one
point
 we
moved
the
2,
1
pile
 out
of
the
way,
moved
 the
3
and
then
put
2,
1
 back
 •  This
is
key
to
a
 recursive
solu4on
 because
2,
1
pile
is
 smaller
 Hanoi Algorithm •  Assume
task
is
to
move
N
disks
from
peg
A
onto
peg
C.
 1.
Move
N
–
1
disks
from
A
to
B
 N-1 N A 2.
Move
Nth
disk
from
A
to
C
 B C B C N-1 N A 3.
Move
N‐1
disks
from
B
to
C
 A B C done!
(just
need
to
find
someone
to
move
the
N‐1
disks
around
 N-1 Hanoi Implementation void
Tower(int
count,
char
A,
char
B,
char
C)
 {
 
//
handle
termina4ng
condi4on
 
if
(count
==
1)
 

 
cout
<<
“move
disk
from”
<<
A
<<
“to”
<<
B;

 }
 
//
handle
recursive
case
 
else
 
{

 

 
Tower(count
–
1,
A,
C,
B);
 

 
Tower(1,
A,
B,
C);
 

 
Tower(count
–
1,
C,
B,
A);
 
}

 •  Code
above
will
move
N
disks
from
A
to
B
using
C
as
 temporary
peg.
 •  Instruc4ons
on
how
to
move
disks
are
printed
as
program
 executes.
 Tracing Hanoi •  Consider execution of Tower(3, ‘A’,’B’,’C’) T(1,A,B,C) 3 T(2,A,C,B) 2 T(3,A,B,C) 1 T(1,A,B,C) 6 T(2,C,B,A) 7 •  Order functions executed indicated by numbers inside each box. •  Instructions are printed when count = 1 T(1,A,C,B) 4 T(1,B,C,A) 5 T(1,C,A,B) 8 T(1,C,B,A) 9 T(1,A,B,C) 10 Hanoi Output move disk from A to B (3) ……………… A to C (5) ……………… A to C (6) ……………… A to C (7) ……………… A to C (8) ……………… A to C (9) ……………… A to C (10) •  First 3 lines move 2 disk from A to C •  Next line moves 1 disk from A to B •  Last 3 lines move 2 disks from C to B Hanoi Analysis •  How many moves are needed to move N disks? N Moves(N) 1 1 2 2.Moves(1) + 1 = 3 3 2.Moves(2) + 1 = 7 4 2.Moves(3) + 1 = 15 5 2.Moves(4) + 1 = 31 •  In general, Moves(N) = –1 •  Thus if N = 20, over a million moves are needed to move disks! •  This is a classic exponential algorithm Recursive Flood Fill •  In
computer
graphic
objects
are
oden
modeled
by
a
 collec4on
of
polygons
(surfaces
defined
by
a
sequence
of
 endpoints)
 •  To
draw
the
object,
programs
are
wriYen
to
project
3D
 points
to
2D
screen
coordinates,
and
the
region
inside
each
 polygon
is
filled
with
color
 •  The
recursive
flood
fill
algorithm
is
one
way
to
color
the
 polygon
once
the
polygon
boundary
is
drawn
 •  We
need
a
seed
point
inside
the
polygon
to
start
algorithm.
 It
stops
when
all
pixels
inside
have
been
colored
 Drawline Algorithm •  If
we
are
given












and












,
the
start
and
end
points
of
 a
line,
how
can
we
fill
the
pixels
in
between?

 S S E E •  The
task
is
to
mask
all
pixels
the
line
intersects
between







 S
and
E
 •  When
∆x
>
∆y
it
is
beYer
to
loop
over
x
and
calculate
y
 coordinates
of
intersec4on
 •  When
∆y
>
∆x
it
is
beYer
to
loop
over
y
and
calculate
x
 coordinates
of
intersec4on
 •  In
both
cases,
we
round
to
the
nearest
integer
and
plot
the
 points
 Drawline code void
drawline(int
color,
int
Xs,
int
Ys,
int
Xe,
int
Ye)
 {
 
//
calculate
slope
 
int
dX
=
Xe
–
Xs;
 
int
dY
=
Ye
–
Ys;
 
float
slope
=
(float)dY
/
(float)dX;
 }
 
//
handle
∆x
>
∆y
case
 
if
(dX
>
dY)
 
{ 

 

 
for
(int
x
=
Xs;
x
<=Xe;
x++)
 

 
{
 

 
 
y
=(int)(0.5
+
(x
–
Xs)
*
slope
+
Ys);
 

 
 
pixel[y][x]
=
color;
 

 
}
 
} 

 
else
//
handle
∆y
>
∆x
case
 

 
for
(int
y
=
Ys;
y
<=Ye;
y++)
 

 
{
 

 
 
x
=
(int)(0.5
+
(y
–
Ys)
*
slope
+
Xs);
 

 
 
pixel[y][x]
=
color;
 

 
}
 
}
 Testing Drawline •  Assume













=
(1,
1)
and












=
(7,
3)
 •  dX
=
6,
dY
=
2,
slope
=
0.333
 x 1 1 2 1 3 2 4 2 5 2 6 3 7 •  Using
the
code
where
dX
>
dY
 y 3 y * * * 0 2 3 4 5 6 7 * * 1 * Line
is
filled
in
 with
no
gaps
 * x •  We
assume
that
line
and
points
are
within
the
array
bounds
 of
pixel
array.
 Testing Drawline •  Assume













=
(1,
1)
and












=
(‐2,4)
 •  dX
=
‐3,
dY
=
3,
slope
=
‐1
 x y 1 1 2 0 3 -1 4 -2 •  Using
the
code
where
dY
>=
dX
 y * Line
is
filled
in
 with
no
gaps
 * * * -2 -1 0 1 x •  We
assume
that
nega4ve
array
indices
are
handled
 somehow
by
magic
….
 Flood Fill Algorithm * Seed point •  Assume
we
have
drawn
the
boundary
of
polygon
by
drawing
 lines
P1‐>P2,
P2‐>P3,
etc…
 •  Assume
we
are
given
(x,y)
coordinates
of
seed
point
inside
 the
polygon.
 •  We
can
grow
the
region
by
adding
points
that
are
connected
 to
seed
point.

 •  These
four
neighbors
can
then
be
treated
as
 T seed
points
and
we
can
grow
the
region
 L * R recursively
 B •  We
must
take
care
to
stop
on
boundary
or
a
previously
marked
 pixel
in
the
region.
 Flood Fill Code void
fill(int
color,
int
x,
int
y)
 {
 
//
handle
termina4ng
condi4on
 
if
(pixel[y][x]
==
color)
 

 
return;
 }
 
//
handle
recursive
case
 
else
 
{

 

 
pixel[y][x]
=
color;
 

 
fill(pixel,
color,
x
+
1,
y);



//
R
 

 
fill(pixel,
color,
x
–
1,
y);



//
L
 

 
fill(pixel,
color,
x
,
y
+
1);


//
T
 

 
fill(pixel,
color,
x
,
y
‐
1);


//
B
 
}

 •  This
code
does
no
array
bounds
checking
and
will
die
unless
 boundary
is
properly
drawn.
 Testing Flood Fill •  Assume
we
have
2D
array
with
polygon
boundary
drawn
as
 show.
 y 5 X 4 X 3 X X 2 X X X S X 1 X X 0 X X X X X X X X 0 1 2 3 4 5 6 7 •  Start
by
calling
fill(4,2)
 •  Will
recursively
call
fill(5,2)
 •  Where
will
it
go
next?
 x Testing Flood Fill •  Final
result
ader
recursive
flood
fill
 •  Polygon
filled
in
with
zigzag
paYern.
 y 5 X X 4 X 13 X 3 X 12 10 11 X 2 X 8 9 S 1 X 1 X 7 6 5 4 2 3 X 0 X X X X X X X X 0 1 2 3 4 5 6 7 x •  May
cause
stack
overflow
if
large
polygons
are
rendered
this
 way.
 Defining Languages •  We can define languages by specifying the vocabulary and a grammar that states the rules for the language. •  For programming languages we design the grammar to be simple (so we can write compilers and interpreters). •  A terminal is a single word in the language •  A non-terminal is a symbol that stands for zero or more terminals (e.g: verb_phrase) •  A production rule tell us how we can replace a nonterminal with zero or more terminals/non-terminals •  The start symbol is a non-terminal that is used to start the derivation of all sentences in the language. Language Example •  We can formally specify the “language” of all valid C++ identifiers as follows. <id> = <letter>|<id><letter>|<id><digit> <letter> = a|b|…|z|A|B|..|Z| <digit> = 0|1|…|9 •  The non-terminals are <id>, <letter>, <digit>, and individual characters are terminals •  The RHS of production rules show the sets of terminals/nonterminals that can be used to replace terminal on LHS (we use 1 to separate choices to save space). <id> → <id><letter> •  Sample
 deriva4on
of
 → <id><letter><letter> variable
“foo”.
 → <letter><letter><letter> → foo Identifier Code bool
id(char
str,
int
pos)
 {
 
//
handle
single
char
case
 
if
((pos
==
0)
&&
leYer(str[pos]))
 

 
return
true;
 }
 
//
handle
recursive
case
 
else
if
(leYer(str[pos])
||
digit(str[pos]))
 

 
return
(
id(str,
pos
–
1));
 
 
else

 

 
return
false;
//illegal
character
 •  Code
above
checks
last
digit
in
str
and
makes
recursive
call
 only
if
leYer
or
digit
found.
 •  It
terminates
if
single
leYer
is
found
or
an
illegal
character
is
 read.
 •  Would
be
beYer
to
process
str
from
L
to
R
but
this
takes
 more
elaborate
grammar.
 Tracing Identifier Code •  Trace execution of id(“num42”,4) main id(“num42”,4) true id(“num42”,2) true id(“num42”,3) true id(“num42”,1) true id(“num42”,0) true •  Each recursive call processes one character on right hand side of str. •  What happens if pos < 0 in first call? Palindrome Example •  We can formally specify the “language” of palindrome as follows. <pal> = Ɛ|<ch>|a<pal>a|b<pal>b|…|z<pal>z <ch> = a|b|…|z •  Because the production rules introduce characters in “pairs” on either end of an existing palindrome, the word will be the same read forwards or backwards <pal> → <a><pal><a> •  Ɛ is empty string → ab<pal>ba so can be removed
 → abƐba → abba •  Sample derivation of “abba”, a musical palindrome from way back … Palindrome Code bool
pal(char
str,
int
low,
int
high)
 {
 
//
handle
single
char
case
 
if
(high
‐
low
<=
0)
 

 
return
true;
 }
 
//
handle
recursive
case
 
else
if
(str[low]
==
str
[high])
 

 
return
(
pal(str,
low
+
1,
high
‐
1);
 
//otherwise
no
palindrom 

 
else

 

 
return
false;

 •  Code
can
also
be
adapted
to
strings
 •  Recursive
call
looks
a
liYle
like
the
<pal>
produc4on
rule.
 Tracing Palindrome Code •  Trace execution of pal(“abcba”,0,4) main pal(“abcba”) true pal(“bcb”3) true pal(“c”) true •  Trace execution of pal(“xyzzx”,0,4) main pal(“xyzzx”) false pal(“yzz”3) false •  String is rejected because y ≠ z in substring. Expression Example •  In order to process expression of the form “3+2*5” we need a grammar that can recognize a sequence of numbers and operators. S → S + S | S * S | num S→S+S •  Derivation for “3 + 2 * 5”
 →S+S*S → num +S * S → num + num * S → num + num * num Expression Example •  Can draw derivation in tree form too S S 3 S + S S or * S S S+S 2 5 3 S 5 2 •  The grammar above is ambiguous because two parse trees are possible (hence two values possible if evaluated this way)
 Improved Expression Example •  Can extend grammar to include brackets and impose “multiple before addition” rule S→S+T|T •  Three non-terminal T→T*F|F now, S, T, F. 
 F → num | (S) S→S+T →T+T →T+T*F •  Only one possible →F+T*F derivation for “3 + 2 * 5” →F+F*F now
 → num + num * num •  This grammar can be easily extended to include “-” and “÷” operations by adding rules for expanding S and T •  Writing a recursive program to recognize expressions is a little tricky because grammar above is “left recursive”. Recursive Expression Parser •  We can rewrite previous grammar to be “right recursive” as follow •  Here ɛ stands for empty string.
 •  Now we can write recursive descent parser with functions called S, R1, T, R2, and F as follows. S() – call T and R1 R1() – check for +, call T and R1 T() – call F and R2 R2() – check for *, call F and R2 F() – check for digits and bracketed expression Recursive Parser Implementation bool
S()
 {
 
if
(
T()
)
 

 
return
R1();
 
else
 

 
return
false;
 }
 bool
R1()
 {
 
if
(
nextchar()
==
‘+’)
 
{
 

 
readchar(); 
 

 

 
if
(
T()
) 

 

 
 
return
R1();
 

 
else
 

 
 
return
false;
 
}
 
return
true;
 }
 bool
F()
 {
 
if
(
(nextchar()
>=
‘0’
)
&&
(nextchar()
<=
‘9’))
 
{ 
 
readchar();
return
true; 
}
 
else
 

 
return
false;
 }
 bool
T()
 {
 
if
(
F()
)
 

 
return
R2();
 
else
 

 
return
false;
 }
 bool
R2()
 {
 
if
(
nextchar()
==
‘*’)
 
{
 

 
readchar(); 
 

 
if
(
F()
) 

 

 
 
return
R2();
 

 
else
 

 
 
return
false;
 
}
 
return
true;
 }
 

 Tracing Recursive Expression Parser •  Trace execution as S() processes “3+2*5” S T true F true R1 readchar R2 readchar 3 no * found + T F true R1 no + found readchar 2 R2 readchar * F readchar 5 R2 no * found Languages Discussion •  Clearly the task of parsing and compiling an entire program is beyond the scope of this class. •  Important formal methods for defining and processing grammars is discussed in “formal languages” •  The implementation of programming languages is typically covered in graduate level “compiler writing” classes or capstone projects. 8 Queens Problem •  Given an 8x8 chess board and 8 queens, position the queens 1 per row/column so no queen can take another * * Q * * * * * * * * * * Q * * * * * * * * * * * * * * * * * * * * * •  After we place two queens a large number of spaces are no longer “safe”. * * •  In general, there are 8! possible combinations to consider (40,320) •  Is there a way to shorten this search? •  Yes, using recursion and backtracking … 8 Queen’s Algorithm •  Assume that columns 1 to k-1 are solved and queens are placed properly. •  Pick one position in column k that is “safe” from all other queens. •  Recursively try to solve remaining columns with a queen in this position. •  If recursive solution returns success then we are done and can return success. •  Else we need to move column k queen to next “safe” position and try to solve remaining columns again. (this is the backtracking part) •  If no “safe” positions can be found on column k then return failure (and let one of the lower columns backtrack). •  By limiting our search to “safe” positions the search time is much less than 8! 8 Queens Code bool
solve(int
col)
 {
 
//
check
termina4ng
condi4on
 
if
(col
>=
SIZE)
 

 
return
true;
 }
 
else
//
handle
recursive
case
 
{
 

 
//
try
all
possible
rows
 

 
for
(int
row
=
0;
row
<
SIZE;
row
++)
 

 
{
 

 
 
if
(safe(row,
col))
 

 
 
{
 

 
 
 
board[row][col]
=
‘Q’;
//move
 

 
 
 
if
(solve(col
+
1))
 

 
 
 
 
return
true;
 

 
 
 
else
 

 
 
 
 
board[row][col]
=
‘


’;
//backtrack

 

 
 
}
 

 
}
 

 
//return
false
if
no
solu4on
found
 

 
return
false;

 
}
 Testing 8 Queens Q * * * * * * * * * * * * * * * * * * * * Q * * * * * * * * * * * * * * Q * * * * * * * * * * * * * * * * * * * * * * col = 0 * * * col = 1 * * * * * * * Q * * * * * * Q * * * * * * * * * * * * * * * * * * * * * * col = 2 * * * * * * * * * * * * * * Q * * * * * Q * * * * * * * * * * * Q * * * * * * * * * * * * * * * * * Q * * * Q * * * * * * * * * * * * * col = 3 * * Testing 8 Queens Q * * * * * * * Q * * * * * * * * * * Q * * * * * * * Q * * * * * Q * * * * * * * Q * * * * * * * * * * Q * * * * * * * * * * * Q * * * * * * * Q * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Q * * * * * * * col = 4 col = 5 * * * * * * * * * Q * * * * * * * * Q * * * * * * * * * * * Q * * * * * * * * * * * * * * * col = 3 * * * * * * * * * Q * * Q * * * * * * * * * * Q * * * * * * * * * * * * * * * * * Q * Q Still no safe rows in col = 5, so back track on col = 3 now * No safe rows in col=5, so backtrack on col = 4 * * Q * * * * * * * * * * col = 4 * * * * Now col = 5 has openings so we can continue search ...
View Full Document

Ask a homework question - tutors are online