### Chaos and BigDecimals

“Relativity eliminated the Newtonian illusion of absolute space and time; quantum theory
eliminated the Newtonian dream of a controllable measurement process; and chaos eliminates the
Laplacian fantasy of deterministic predictability.” ^{1}Jonathan Allin is a Senior Java Consultant with Origin IT, a multi-national solutions provider based in Cambridge, UK. He can be contacted at jonathan.allin@uk.origin-it.com. WE TEND TO think that, in general, random results can only arise from complex systems and that
significant changes can only be brought about by a large stimulus. Both ideas are wrong. Most simple
systems are capable of producing complex, chaotic results, and, conversely, a small change can produce
an arbitrarily large consequence. The butterfly that flapped its wings in Peking did produce a
storm in New York. All that is needed is a bit of feedback and a bit of nonlinearity.What is chaos? In the mathematical sense chaotic behavior has two requirements: (1) results that are sensitive to initial conditions and (2) results that are non-repetitive or random. Chaos should not be confused with noise, which is an unwanted influence on a system. The simplest equation that produces chaotic behavior is the logistic equation. This equation has been used to model the behavior of various systems from banking to the movement of planets. It can also be used as a simple model of population growth, where this year's population is dependent on last year's (e.g., animals such as birds, which have an annual breeding cycle).
To model population growth we might start off with the naïve equation: i.e., this year's population is last year's population multiplied by the rate of breeding. This equation leads to unrestricted exponential growth but quickly breaks down in the real world because, in practice, growth is limited by lack of food, predation, lack of living room, limited energy supply, etc. We therefore introduce a new term that reflects this restriction, and this gives us the so called “logistic equation”: This also normalizes the population to a value between 0 (extinction) and 1 (saturation). This simple equation holds unbelievable riches. It cannot be solved analytically, only numerically: if I want to know the population in 10 generations time, I have to apply the equation 10 times. I originally implemented the equation using doubles, however, I was concerned that rounding errors would introduce noise, and therefore invalidate the results. What I needed was unlimited precision: this was provided by Java's BigDecimal class. Here,
I review the BigDecimal class, and use it in an application with which you can explore the
logistic equation.
THE BigDecimal CLASSBigDecimal, and its companion BigInteger, are part of the java.math package.
Both classes extend Number, which means that, along with Integer, Float, Double, etc.,
they have methods to return an integer, float, or double, representation. A BigDecimal can be
used to represent an arbitrary precision rational number, and a BigInteger to represent an
arbitrary precision integer.
A BigDecimal should be used when a double does not provide the necessary precision or when
more control over rounding is required.BigDecimals and BigIntegers are immutable, which means that once created, they
cannot be modified. “Modifying” a BigDecimal involves creating a new BigDecimal
with the correct characteristics (this is generally transparent to the user).The BigDecimal class defines eight constants, which specify the rounding mode. Seven of these
are described in Table 1. (When I was at school we were taught only one method of rounding: round half up!)
The numbers have been rounded to two places.The eighth constant is ROUND_UNNECESSARY. This indicates that truncating the number to the
desired number of places will not lose precision. An exception is thrown if this is not true. In practice
this means that truncating with ROUND_UNNECESSARY should only remove trailing zeroes.BigDecimal represents a number as an arbitrary large integer, plus a scale factor that
indicates the number of decimal digits. So 1234.568901 is represented as 1234568901 and a scale
factor of 6.Truncating numbers becomes essential. If I multiply a 5-digit number by a 10-digit number, then I will generate a 14- or 15-digit number. Nor does it matter if the number has trailing zeroes, so multiplying 1.0 by 1.0 produces 1.00, i.e., the scale factor is 2. To illustrate this, I printed out the first few values from the logistic equation, with no truncation set. The initial value was 0.1, and the ratio was 3.9 (see Table 2). A few more iterations and my PC would have given up! It is therefore necessary to limit the maximum number of decimal places, though in practice this can be quite high: My PC is nothing special, but 100 decimal places calculates about 100 iterations per second. setScale() is used to set a BigDecimal's scale. It comes in two
flavors: setScale(int scale) and setScale(int scale, int roundingMode). The first
is generally used to increase the scale. It can be used to decrease the scale but will throw an
ArithmeticException if a rounding error occurs, e.g., an exception will be thrown if this
method is used to reduce the scale of 3.45 from 2 to 1.It's often desirable to reduce the scale to remove unwanted trailing zeroes, e.g., 45.3400 has a scale of 4, while a scale of two would be sufficient. However, there is no direct way of doing this. The following code relies on the fact that BigDecimal throws an ArithmeticException if
an attempt is made to reduce the scale, when this would result in a loss of precision due to rounding.
So it starts with a scale of zero and increases the scale until an exception is no longer
generated.The second variation, setScale(int scale, int roundingMode), is generally used to decrease
the scale of a number. (e.g., in the following code):
d2 will have the value 2.4, with a scale of 1.As you would expect, BigDecimal provides the basic arithmetic functions: add(), subtract(),
multiply(), and divide(). A rounding mode must always be specified with divide().
The scale of the quotient will be the scale of this BigDecimal, or as specified in the method
call. e.g.,:
gives a quotient with a value of 2.7 and a scale of 1. To specify the scale of the quotient use divide(BigDecimal val, int scale, int roundingMode).Figure 1.
The Chaos user interface. Controls are in the lower panel, results are plotted in the upper panel. Other methods to watch are hashCode() and equals(). A BigDecimal's hash
code is dependent not just on the number's value but also on its scale. This means that 1.5 and 1.50 have
different hash codes, and because of the duality between hashCode() and equals(), a test
using equals() will return false. However, compareTo() will return 0, because it ignores
the scale. In the following code:
areEqual is false because the numbers have different scales but comparison is 0, because they
have the same value.Figure 2.
Class diagram for the Chaos application. THE CHAOS APPLICATIONUser InterfaceThe Chaos application allows the logistic equation to be plotted and explored. Different initial populations can be chosen as can different growth rates or ratios. To understand the code, it's best to start with a look at the application (Figure 1). The lower panel contains the controls, the upper panel is used to plot the results. From left to right we can set the initial population (0 to 1), the growth rate or ratio (typically 0 to 4), and the number of decimal places (i.e., the scale) to which the calculations will be restricted (a value of 0 will remove any limit, but you won't be able to calculate more than the first few points). “Zoom” is not really a zoom factor, but the number of pixels (plus one) between successive points. A value of one plots one pixel per point, greater values will join up successive points. If you set the number of decimal places to zero, i.e., unlimited, then set the zoom to something like 80 to 100. This will ensure that only the first few points are plotted. Plot color is used to set the color of the next plot. Having set the required values, pressing the Plot button will cause plotting to commence. Multiple plots can be made with the Clear button being used to clear the panel as required.Experiment with different values and see what happens. Later, I list some interesting values to use and think about. Program StructureFigure 2 is a class diagram for the Chaos application. It consists of three classes: GraphPanel,
Chaos, and ColorChoice. In the interests of simplicity I have not included any input
checking, nor provided axes markers. The complete program is given in
Listing 1.GraphPanel ClassGraphPanel extends Panel and is the class responsible for creating a plot and
presenting it.The plotting is carried out in a for loop (lines 35 to 54), within the makePlot() method.
This loops once for each point plotted. The plotting is done into an off-screen image buffer,
image. The width of the buffer, and hence the number of points plotted, is determined by the
size of the window. g is the buffer's graphics context.The actual calculation for the logistic equation is carried out on line 36: It's a shame that Java doesn't support operator overloading. The meaning would be much clearer if we could write: It's worth noting that, because BigDecimals are immutable, three new BigDecimals are
created for every point.Next is a try-catch block that limits newPop's scale (i.e., the number of digits after the
decimal point) if decimalPlaces is non zero.Line 45 converts the new population (a number between 0 and 1) to a y coordinate that can be plotted. The height of the plot is scaled to the size of the window. Figure 3.
The effect of increasing the ratio or rate. Black = 1.5, green = 2.0, red = 2.9, Zoom = 5. The actual plotting is done in lines 47 to 50: this will plot a single point if the zoom (the point-to-point step size) is 1 or will draw a line from the old point to the new point if the zoom is greater than 1. At the end of the loop the old values for the population and its plotted value are set to the new values. Figure 4.
At a ratio of 3.5, the population cycles among four distinct values. Single plot, zoom = 1. repaint() is called at the end of makePlot(). repaint() schedules a
call to paint(), which in turn simply copies the image buffer into the Panel. If an
image buffer wasn't used then every time the application was re-sized, or partially obscured, the last
graph would be re-plotted, and the others lost. The downside of double buffering like this is that a
plot does not become visible until sometime after it has been plotted.Figure 5.
Chaos! The ratio is 3.9. clear() is called when the Clear button is pressed. Its main task is to create a new
image buffer that matches the size of the GraphPanel.update() is overridden to prevent the Panel being cleared to its background color, to save a
little time.The Chaos class is the application's main class. The constructor (lines 108 to 139) is responsible
for creating the control panel (Chaos ClasscontrolPanel), which contains all the UI widgets and an instance
of our GraphPanel (gp).The control panel uses a grid layout to get some control over the layout of the UI components. It's added to the south of the Frame. The GraphPanel (gp) is added to the center.
All the action is handled by Figure 7.
The effect of increased precision: The green plot was plotted to 30 decimal places, the red plot to 20. The two plots diverge after about 100 points. EXPLORING THE LOGISTIC EQUATIONNow it's time to experiment with the logistic equation! Use Table 3 as a guide: some of the results are shown in Figures 3, 4, and 5. Ratios above 3 start to produce some interesting results. Figure 6 shows how sensitive the results are at high ratios to a small change in the initial value. So, did I need the extra precision that BigDecimal provided? Yes and no. Figure 7 shows that,
in a chaotic situation, increased precision gives different results. However, the critical values
(e.g., the values at which bifurcation occurs) appear to be independent of precision. RECOMMENDED READING1. Gleick, J., Chaos,Sphere Books, London, 1988. A very readable, non-mathematical, introduction
to chaos. It reviews everything from the Mandelbrot set to Jupiter's great red spot.2. Williams, G. P. Chaos Theory Tamed,Taylor & Francis, London, 1996. A more mathematical look
at chaos. It reviews the basic math needed prior to a more detailed analysis. |