AhmadLang / Java, How To Program, 2004
.pdf
The application in Fig. 15.23 defines the user interface for drawing this fractal (shown at the end of Fig. 15.24). The interface consists of three buttonsone for the user to change the color of the fractal, one to increase the level of recursion and one to decrease the level of recursion. A JLabel will keep track of the current level of recursion, which is modified by calling method setLevel, to be discussed shortly. Lines 1516 specify constants WIDTH and HEIGHT to be 400 and 480 respectively for the size of the JFrame. The default color to draw the fractal will be blue (line 18). The user triggers an ActionEvent by clicking the Color button. The event handler for this button is registered in lines 3854. The method actionPerformed displays a JColorChooser. This dialog returns the selected Color object or blue (if the user presses Cancel or closes the dialog without pressing OK). Line 51 calls the setColor method in class FractalJPanel to update the color.
[Page 767]
The event handler for the Decrease Level button is registered in lines 6078. In method actionPerformed, lines 6667 retrieve the current level of recursion and decrement it by 1. Line 70 checks to make sure that the level is greater than or equal to 0 (MIN_LEVEL). This is because the fractal is not defined for any recursion level lower than 0. The program allows the user to go up to any desired level, but it is important to note that at a certain point (level 10 and higher in this example) the rendering of the fractal becomes increasingly slower, as there is a lot of detail to be drawn. Lines 7274 reset the level label to reflect the changethe new level is set and the repaint method is called to update the image to show the fractal corresponding to the new level.
[Page 770]
The Increase Level JButton works the same way as the Decrease Level JButton, except that the level is incremented rather than decremented to show more details of the fractal (lines 9091). When the application is first executed, the level will be set to 0, which will display a blue line between two points that were specified in the FractalJPanel class.
The FractalJPanel class in Fig. 15.24 specifies the dimensions of the drawing JPanel to be 400 by 400
(lines 1314). The FractalJPanel constructor (lines 1824) takes the current level as a parameter and assigns it to its instance variable level. Instance variable color is set to the default color blue. Lines 2223 change the background color of the JPanel to be white (for visibility of the colors used to draw the fractal), and set the new dimensions of the JPanel where the fractal will be drawn.
[Page 771]
[Page 774]
Lines 2750 define the recursive method that creates the fractal. This method takes six parameters: the level, four integers that specify the x and y coordinates of two points, and the Graphics object g. The base case for this method (line 31) occurs when level equals 0, at which time a line will be drawn between the two points given as parameters. Lines 3643 calculate (xC, yC), the midpoint between (xA, yA) and (xB, yB), and (xD, yD), the point that creates a right isosceles triangle with (xA, yA) and (xC, yC). Lines 4648 make three recursive calls on three different sets of points.
In method paintComponent, line 59 makes the first call to method drawFractal to start the drawing. This method call is not recursive, but all subsequent calls to drawFractal performed from the body of drawFractal are. Since the lines will not be drawn until the base case is reached, the distance between two points decreases on each recursive call. As the level of recursion increases, the fractal becomes smoother and more detailed. The shape of this fractal stabilizes as the level approaches 11. Fractals will stabilize at different levels based on the shape and size of the fractal.
Fig. 15.24 shows the development of the fractal from level zero to six. The last image shows the defining shape of the fractal at level 11. If we focus on one of the arms of this fractal, it would be identical to the whole image. This property defines the fractal to be strictly self-similar. See Section 15.12 for further resources on fractals.
[Page 774 (continued)]
15.10. Recursive Backtracking
In this chapter's examples, our recursive methods all have a similar architectureif the base case is reached, return a result; if not, make one or more recursive calls. In this section we will explore a recursive method that is slightly more complex. This method finds a path through a maze, returning true if there is a possible solution to the maze. The solution involves moving through the maze one step at a time where moves can be made by going down, right, up or left (diagonal moves are not permitted). From the current location in the maze (starting with the entry point), the following steps are taken: A direction is chosen, the move is made in that direction and a recursive call is made to solve the remainder of the maze from the new location. When a dead end is reached (i.e., we cannot take any more steps forward without hitting a wall), we back up to the previous location and try to go in a different direction. If there is no other direction that can be taken, we back up again. This process continues until we find a point in the maze where a move can be made in another direction. Once such a location is found, we move in the new direction and continue with another recursive call to solve the rest of the maze.
To back up to the previous location in the maze, our recursive method simply returns false, moving up the method-call chain to the previous recursive call (which references the previous location in the maze). This process of using recursion to return to an earlier decision point is known as recursive backtracking. If one set of recursive calls does not result in a solution to the problem, the program backs up to the previous decision point and makes a different decision, often resulting in another set of recursive calls. In this example, the previous decision point is the previous location in the maze, and the decision to be made is the direction that the next move should take. One direction has led to a dead end, so the search continues with a different direction. Unlike our other recursive programs, which reached the base case and then returned all the way up the method-call chain to the original method call, the recursive backtracking solution to the maze problem uses recursion to return only part of the way up the method-call chain, then try a different direction. If the backtracking reaches the entry location of the maze and the paths in all directions have been attempted, the maze does not have a solution.
[Page 775]
In the chapter exercises you are asked to implement recursive backtracking solutions to the maze problem (Exercise 15.20, Exercise 15.21 and Exercise 15.22) and the Eight Queens problem (Exercise 15.15), which attempts to find a way to place eight queens on an empty chessboard so that no queen is "attacking" any other (i.e., no two queens are in the same row, in the same column or along the same diagonal). See Section 15.12 for links to further information on recursive backtracking.
[Page 775 (continued)]
15.11. Wrap-Up
In this chapter, you learned how to create recursive methodsi.e., methods that call themselves. You learned that recursive methods typically divide a problem into two conceptual piecesa piece that the method knows how to do (the base case) and a piece that the method does not know how to do (the recursion step). The recursion step is a slightly simpler version of the original problem, and is performed by a recursive method call. You saw some popular recursion examples, including calculating factorials and producing values in the Fibonacci series. You the learned how recursion works "under the hood," including the order in which recursive method calls are pushed on or popped off the program execution stack. Next, you learned the differences between recursive and iterative (non-recursive) methods. In that discussion, you learned that iterative solutions usually use a repetition statement, whereas recursive solutions usually use a selection statement. You learned how to solve more complex problems using recursion, including finding all permutations of a string and displaying fractals. The chapter concluded with an introduction to recursive backtracking, a technique for solving problems that involves backing up through recursive calls to try different possible solutions. In the next chapter, you will learn numerous techniques for sorting lists of data and searching for an item in a list of data, and under what circumstances each searching and sorting technique should be used.
[Page 775 (continued)]
15.12. Internet and Web Resources
Recursion Concepts
chortle.ccsu.ctstateu.edu/cs151/cs151java.html
Provides links to files that discuss recursion in detail, using questions to guide the reader. en.wikipedia.org/wiki/Recursion
Article from Wikipedia (an online encyclopedia) provides the basics of recursion and several resources for students.
www.cafeaulait.org/javatutorial.html
Provides a nice, brief introduction to recursion in Java, and also covers other Java topics.
Stacks
www.cs.auc.dk/~normark/eciu-recursion/html/recit-slide-implerec.html Provides slides discussing the implementation of recursion using stacks. faculty.juniata.edu/kruse/cs2java/recurimpl.htm
Provides a detailed diagram of the program execution stack and discusses how the stack works.
[Page 776]
Fractals
math.rice.edu/~lanius/frac/
Provides examples of other fractals, such as the Koch Snowflake, the Sierpinski gasket, and Jurassic Park fractals.
www.lifesmith.com/
Provides hundreds of colorful fractal images along with detailed explanation about the Mandelbrot and Julia sets, two common sets of fractals.
www.jracademy.com/~jtucek/math/fractals.html
Contains two AVI movies created by zooming in continuously on the fractals known as the Mandelbrot and Julia equation sets.
www.faqs.org/faqs/fractal-faq/
Provides answers to many questions about fractals. spanky.triumf.ca/www/fractint/fractint.html
Contains links to download Fractint, a freeware program for generating fractals. www.42explore.com/fractal.htm
Lists URLs on fractals and software tools that create fractals. www.arcytech.org/java/fractals/koch.shtml
Provides a detailed introduction to the Koch Curve fractal and provides an applet demonstrating the fractal.
www.cs.ttu.edu/~denton/fractals/Koch.html
Introduces the Koch fractals, providing source code in Java. library.thinkquest.org/26688/koch.html
Displays a Koch Curve applet and provides the source code.
Recursive Backtracking
www.cs.sfu.ca/CourseCentral/201/havens/notes/Lecture14.pdf
Provides a brief introduction to recursive backtracking, including an example on planning a travel route. www.cs.utexas.edu/users/scottm/cs307/handouts/Slides/lec11RecursiveBacktracking-4Up.pdf Demonstrates recursive backtracking and walks through several examples. math.hws.edu/xJava/PentominosSolver
Provides a program that uses recursive backtracking to solve a problem known as the Pentominos puzzle (described at the site).
cte.rockhurst.edu/burgerk/research/scramble/paper.pdf
Demonstrates using recursive backtracking to solve a scrambled squares puzzle.
[Page 776 (continued)]
Summary
A recursive method is a method that calls itself directly or indirectly through another method.
When a recursive method is called to solve a problem, the method actually is capable of solving
only the simplest case(s), or base case(s). If the method is called with a base case, the method returns a result.
If a recursive method is called with a more complex problem than a base case, the method
typically divides the problem into two conceptual piecesa piece that the method knows how to do and a piece that the method does not know how to do.
[Page 777]
To make recursion feasible, the piece that the method does not know how to do must resemble
the original problem, but be a slightly simpler or smaller version of it. Because this new problem looks like the original problem, the method calls a fresh copy of itself to work on the smaller problemthis is called the recursion step.
For recursion to eventually terminate, each time a method calls itself with a simpler version of
the original problem, the sequence of smaller and smaller problems must converge on a base case. When, the method recognizes the base case, it returns a result to the previous copy of the method.
A recursive call may be a call to another method, which in turn makes a call back to the original
method. Such a process still results in a recursive call to the original method. This is known as an indirect recursive call or indirect recursion.
Either omitting the base case or writing the recursion step incorrectly so that it does not
converge on the base case can cause infinite recursion, eventually exhausting memory. This error is analogous to the problem of an infinite loop in an iterative (nonrecursive) solution.
The Fibonacci series begins with 0 and 1 and has the property that each subsequent Fibonacci number is the sum of the preceding two Fibonacci numbers.
The ratio of successive Fibonacci numbers converges on a constant value of 1.618..., a number that has been called the golden ratio or the golden mean.
Some recursive solutions, such as Fibonacci (which makes two calls per recursion step), result in an "explosion" of method calls.
The stack is a data structure whose data objects can be added or removed only at the top of the stack.
A stack is analogous to a pile of dishes. When a dish is placed on the pile, it is always placed at
the top (referred to as pushing the dish onto the stack). Similarly, when a dish is removed from the pile, it is always removed from the top (referred to as popping the dish off the stack).
Stacks are known as last-in, first-out (LIFO) data structuresthe last item pushed (inserted) on the stack is the first item popped (removed) from the stack.
Stacks have many interesting applications. For example, when a program calls a method, the
called method must know how to return to its caller, so the return address of the calling method is pushed onto the program execution stack (sometimes referred to as the method call stack).
The program execution stack contains the memory for local variables on each invocation of a
method during a program's execution. This data, stored as a portion of the program execution stack, is known as the activation record or stack frame of the method call.
If there are more recursive or nested method calls than can be stored on the program execution stack, an error known as a stack overflow occurs.
Both iteration and recursion are based on a control statement: Iteration uses a repetition statement, whereas recursion uses a selection statement.
Both iteration and recursion involve repetition: Iteration explicitly uses a repetition statement, whereas recursion achieves repetition through repeated method calls.
Iteration and recursion each involve a termination test: Iteration terminates when the loopcontinuation condition fails, whereas recursion terminates when a base case is recognized.
Iteration with counter-controlled repetition and recursion each gradually approach termination:
Iteration keeps modifying a counter until the counter assumes a value that makes the loopcontinuation condition fail, whereas recursion keeps producing simpler versions of the original problem until the base case is reached.
Both iteration and recursion can occur infinitely: An infinite loop occurs with iteration if the loop-
continuation test never becomes false, whereas infinite recursion occurs if the recursion step does not reduce the problem each time in a manner that converges on the base case.
[Page 778]
Recursion repeatedly invokes the mechanism, and consequently the overhead, of method calls.
Any problem that can be solved recursively can also be solved iteratively.
A recursive approach is normally preferred over an iterative approach when the recursive
approach more naturally mirrors the problem and results in a program that is easier to understand and debug.
A recursive approach can often be implemented with few lines of code, but a corresponding
iterative approach might take a large amount of code. Another reason to choose a recursive solution is that an iterative solution might not be apparent.
The permutations of a string are all the different strings that can be created by rearranging the characters of the original string.
Method charAt of class String takes an integer argument and returns the character in the
String at that index. As with arrays, the first element of a string is considered to be at position
0.
Class String provides two substring methods to enable a new String object to be created by copying part of an existing String object. Each method returns a new String object.
If method substring is passed two integer arguments, the first argument specifies the starting
index from which characters are copied in the original string, and the second argument specifies the index one beyond the last character to be copied.
If method substring is passed one integer argument, the argument specifies the starting index
in the original string from which characters are to be copied. The substring returned contains a copy of the characters from the starting index to the end of the string.
A fractal is a geometric figure that is generated from a pattern repeated recursively an infinite
number of times. The figure grows by adding the pattern at different orientations and scaling to the original.
Fractals have a self-similar propertywhen subdivided into parts, each is a reduced-size copy of the whole.
The process of using recursion to return to an earlier decision point is known as recursive
backtracking. If one set of recursive calls does not result in a solution to the problem, the program backs up to the previous decision point and makes a different decision, often resulting in another set of recursive calls.
[Page 778 (continued)]
Terminology
activation record anagram backtracking base case
charAt method of String complexity theory converge on a base case Eight Queens problem exhaustive recursion factorial
Fibonacci series fractal
fractal depth fractal level fractal order golden mean golden ratio indirect recursion infinite recursion Koch Curve fractal
Koch Snowflake fractal
last-in, first-out (LIFO) data structures level of a fractal
Maze Traversal problem method call stack palindrome
