Building Fractals using Dynamical Systems:  Julia Sets

Comp110 Course Module

Note: The fractal modules in part 8 are fairly short.  Therefore, each module will be worth 50 points.

Complex numbers

Many times you may have wondered how we can solve problems that involve the computation of a square root of a negative number.  For example, how can we find the solution x to the equation

27 (x^2)+13==0

The answer to this question lies in using complex numbers. To introduce complex numbers, we first define a new "imaginary" constant i such that

(i^2)==-1.

A complex number is a quantity of the form z=a + i b, where a and b are real numbers.  The magnitude of a complex number is the expression (Sqrt[(a^2)+(b^2)]).  Note that the magnitude of a real number is simply its absolute value.  (In fact, in Mathematica, the function Abs computes the magnitude of a complex number.)

Mathematica is very adept at dealing with complex numbers.  In particular, the standard Solve function can be used to find the complex solutions to the equation given above:

Solve[27 (x^2)+13==0]

Note that the solution contains two substitutions which are both complex numbers, i.e. they involve the complex unit i.    One of the fundamental theorems of algebra is a that a polynomial of degree n always has exactly n complex roots (counting multiplicities).

Our goal in this module is to examine the behavior of complex functions -- i.e. functions that take complex numbers as input and produce complex numbers as output.  As we shall, these functions can be used to define an interesting set of shapes known as fractals.

Julia Sets

The basis of almost all methods for constructing fractals using complex functions is to examine the effect of iterating a complex function f[z] via the recurrence

(Subscript[z, i+1])=f[(Subscript[z, i])].

Iterated systems of this form are referred to as dynamical systems.  Given any initial value (Subscript[z, 0]), this recurrence defines a sequence of complex numbers (Subscript[z, i]).  Close examination of the behavior of this sequence for various functions f and initial values (Subscript[z, 0]) as i->∞ leads to several interesting shapes.  In general, the resulting sequence of points (Subscript[z, i]) exhibits one of three types of behavior:

(Subscript[z, i]) diverges to infinity,

(Subscript[z, i]) converges to a point called an attractor,

(Subscript[z, i]) neither converges to a point nor diverges to infinity.

For example, consider iterating the complex function f[z]=(z^2). For any (Subscript[z, 0]) with a magnitude less than one, the sequence (Subscript[z, i]) converges to the origin.  For (Subscript[z, 0]) with magnitude greater one, the sequence diverges towards infinity.  Finally, for (Subscript[z, 0]) with magnitude one, the sequence (Subscript[z, i]) neither converges to the origin nor diverges to infinity -- instead the sequence traces out a number of points on the unit circle.

The set of starting points for which a complex function f neither converges to a single point nor diverges to infinity is called the Julia set of f.  Some functions are more interesting than others. The remainder of this section will focus on complex functions of the form

f[λ_][z_]:=λ z (1-z)

If we visualize the complex number x+ i y as the point {x,y} in a 2D plane, then the plot below shows the Julia set of this function for λ=2+i.

Note that the definition of f above has split the arguments of f into two parts.  The partial expression f[λ] is itself a function of a single parameter z.  For example, the function f[2] is the function that maps z to 2z(1-z). This splitting process is called currying f.   The advantage of currying f is that we can pass f[λ] to functions in Mathematica that themselves expect functions as input.  For example, the Mathematica function NestList takes a function g, a starting value z, and an integer n and generate the list

{z,g[z],g[g[z]],g[g[g[z]]],…,g[g[…g[z]…]]}

where in the last element of the list g is applied n times.  Variants of Nest will prove useful in the problems that follow.

Recall that the function f[λ] defines a sequence of values (Subscript[z, 0]), (Subscript[z, 1]), … via the recurrence (Subscript[z, i+1])=f[λ][(Subscript[z, i])].  As i->∞, this function f[λ] pushes the point (Subscript[z, i]) further away from the Julia Set.  To render a Julia set, our approach will be to compute an inverse function g[λ] that reverses this process -- that is

g[λ][(Subscript[z, i+1])]=(Subscript[z, i.])

Iterating the function g[λ] will push the points (Subscript[z, i]) towards the fractal, the curve with the property that the points (Subscript[z, i]) as i->-∞ lie on the Julia set.