Well, I've finally found the time to write another essay. The last week or two has been spent jabbering the ears offa (former) friends about fractals, it's about time they learnt how super leeto they are. Just keep reading...
WARNING: This article requires a high school knowledge of algebra, and an interest in fractals, otherwise you might find yourself bored to tears. Oh yeah, it's kinda long too.
What is a Fractal?
A fractal is a rough geometric shape which can be subdivided into parts, each of which is an approximation of the whole. Fractals tend to be self-similar, meaning that if you pick an area within a fractal and 'zoom' in on it, you'll more than likely discover a replica of the whole fractal.
Fractals tend to describe many real world objects, such as rivers, blood vessels, branches, clouds, mountains, i.e, those that seem to exhibit no obvious pattern. For example, it's more than possible to use fractals to draw realistic looking mountain ranges, complete with trees, clouds and rivers. Cool, huh?
Unfortunately, we'll need a little background in maths before we can proceed on our journey, so lets get it over with... t'isn't that bad, honest.
Complex Numbers
One of the earliest references, of to a problem leading to imaginary (complex) numbers is in Stereometrica by Heron of Alexandria, from around AD 50. Later in 1777, Euler introduced the concept of i, and the now used a + bi notation (see below).
Finally, Hamilton in 1833 put complex numbers on a sound mathematical footing, by working out what the hell everyone else had been babbling about for the previous 1,800 years...
Complex numbers, by their very definition, have no true place in 'normal' mathematics. The imaginary unit, i, is defined as the square root of -1, which your calculator will tell you is blatantly impossible. Anyhow, enough waffle.
As mentioned a complex number is composed of two parts, a real and imaginary part.
The real part is just a normal floating point number. The imaginary part is composed of another floating point number, multiplied by the imaginary unit i. The notation is as follows:
| a | + | bi |
| Real | Imaginary |
Therefore 3 - 2i is a complex number, as is -1 + i.
As I said, i is defined as the square root of -1, such that i2 = -1.
It's important for us to remember this fact when performing multiplication of complex numbers.
Complex Arithmetic
Arithmetic with complex numbers is pretty simplistic. Thankfully, we can use good old high school algebra.
If you can't remember how to multiply out factorials, grab a textbook =]
Addition
To add two complex numbers, we add the two a components, and the two b components, thus:
| 3+4i + 1-2i | = | (3+1) + (4+-2)i |
| = | 4+2i |
Subtraction
Subtraction follows the rules for addition, only in reverse, subtract a components, and b components:
| 3+4i - 1-2i | = | (3-1) - (4--2)i |
| = | 2-6i |
Multiplication
Here we just use factorial expansion, remembering that i * i = -1
| (3+4i)(1-2i) | = | (3*1) + (3*-2i) + (4i*1) + (4i*-i2) |
| = | 3 - 6i + 4i + 8 N.B.(4i*-2i = 8) | |
| = | 11-2i |
The Mandelbrot Set
The Mandelbrot set, discovered by Benoit B. Mandelbrot, is defined as the set of all complex numbers, that when iterated through a specific recursive formula do not tend toward infinity. Obviously we can never be certain if a number will reach infinity, since this would take an infinite number of iterations, so we have to approximate.
Lets look at that in english, the formula which defines the Mandelbrot set is Z n = { Z n-1 } 2 + C, where:
| Zn | - | The result of the formula for this iteration. |
| Zn-1 | - | The result of the formula from the previous iteration. |
| C | - | A complex number we supply. ( constant ) |
This is known as a recursive formula, since Z n depends upon the previous outcome of the equation. { Z n-1 }
The first time through the equation, we don't have a previous outcome to use, so we use zero, which is defined as the critical point of this equation. There is a proof for this, but it's beyond the scope of this article.
Lets try to clear up any confusion with an example:
Pick any point on the complex plane, for example 2+3i. This is our C, put this value into the equation:
| Z1 | = | 02 + C |
| = | 2+3i |
Now we move onto the next iteration:
| Z2 | = | Z12 + C |
| = | (2+3i)2 + (2+3i) | |
| = | (4+6i+6i-9) + (2+3i) | |
| = | -11+15i |
For the third interation:
| Z3 | = | Z32 + C |
| = | (-11+15i)2 + (2+3i) | |
| = | (121-165i-165i-225) + (2+3i) | |
| = | -104-330i |
Essentially what we're trying to do, is to see the behavior of a complex number C when it's repeatedly put through the formula. The behavior can be classified depending on whether or not the result stays within the bounds of the Mandelbrot set, or rapidly moves away from it.
So how do we check whether or not that the result is contained within the Mandelbrot set?
Peasy. In this set, we can say that if the distance between the origin (0+0i) and the number, exceeds the value of two, then it will keep growing, toward infinity.
Visualisation
To be able to visualise our fractal, we need to associate the notation a + bi with screen (x , y) co-ordinates. The way we do this is to look at the complex number as x + yi, where x and y are our screen co-ordinates.
A complex number C is said to be in the Mandelbrot set if, after a certain number of iterations, its distance from the origin (x = 0 , y = 0 or 0 + 0i) is < 2. How do we calculate the distance from the origin? Pythagoras Theorem.
Lets look again at our example complex number, 2 + 3i
| Distance from origin | = | sqr-root( x2 + y2 ) |
| = | sqr-root( 22 + 32 ) | |
| = | sqr-root( 13 ) | |
| = | 3.605551275 |
Hence, we know that 2 + 3i isn't in the Mandelbrot set, since 3.61 > 2
To create an image of the entire Mandelbrot set, we need to repeat the iterative process described above with all complex numbers (C) in the range ( -2.5 < x < 1.5 ) and ( -1.5 < y < 1.5 ), or, ( -2.5-1.5i to 1.5+1.5i ).
To begin with we'll colour all points that are in the Mandelbrot set black, and all those outside, white.
Since we can't feasibly see if a number goes to infinity (this takes an infinite number of iterations) we set a maximum amount of iterations, after which we assume that the number lies within the set.
For the sake of speed we'll use 64 as our maximum iteration limit to start with. Note that the iteration count has a direct effect on image detail, the higher the better. Lets look at how to code this:
unsigned char iterate( float re, float im )
{
float oZre;
float Zre = 0,Zim = 0;
int iterations = 0;
float odist = 0;
do
{
oZre = Zre;
Zre = ( Zre * Zre ) - ( Zim * Zim ) + re;
Zim = ( 2 * oZre * Zim ) + im;
odist = ( Zre * Zre ) + ( Zim * Zim); // calculate distance from origin
iterations++; // number of completed iterations
}
while( ( iterations < MAX_ITERATIONS ) && ( odist < 4 ) );
if( iterations == MAX_ITERATIONS )
return( 0 ); // Points inside the set (black)
else
return( 15 ); // All other points (white)
}
Here we have the function that does all of the work. The first three lines inside the do while loop execute the formula we concentrated on earlier, using the suppiled values re and im as the real and imaginary parts of our C. This calculates our new Zn for the next iteration.
The next line calculates the distance from the origin, using Pythagoras Theorem. Note that we can speed up our algorithm considerably by excluding the square root and checking to see if the value exceeds four, rather than two.
Once we've escaped the loop, we check to see the condition that was met. If iterations equals MAX_ITERATIONS then our point is within the Mandelbrot set, so we return zero (black). Otherwise, we return fifteen (white)
Now we need to write a function to call iterate( ), passing all values in the range. Since we want to generate an image of the entire Mandelbrot set, we use the range ( -2.5 < x < 1.5 ) and ( -1.5 < y < 1.5 )
void mandelbrot( float MinX,float MaxX,float MinY,float MaxY )
{
float dx,dy; // delta x, and delta y
int x,y; // screen coords
dx = (float)(MaxX - MinX) / 320;  // Amount to increment x by each time
dy = (float)(MaxY - MinY) / 200; // Amount to increment y by each time
for( y = 0 ; y < 200 ; y++ )
for( x = 0 ; x < 320 ; x++ )
pixel( x,y,iterate( (float)(MinX + (x * dx)),(float)(MaxY - (y * dy)) ) );
}
This function is fairly self explanatory. We calculate delta x (dx) and delta y (dy), which gives us the amount to increment x by each time through the loop. This scales the limits of the Mandelbrot set to the screen. Then we call iterate for each screen pixel to see where the resultant point falls.
Once we've finished our colouring exercise, we'll should have something similar to this:

Fig. 1
Admittedly this doesn't look very exciting, but lets say that instead of assigning a colour depending on whether the number is within the set (black) or not (white), we assign a different colour to those outside the set, depending on the number of iterations taken to escape the set? Things get a lot more interesting:
Change the return value for points outside the set from return( 15 ) to return( iterations ). We get this:

Fig. 2
Finally, we change the palette to something more visually appealing.

Fig. 3
Exploration
Now that this is done, we can begin to see what everyone is getting so excited about. I'm sure you've already looked at the example, so lets explore the fractal a little. To look at any point in closer detail, change the intialisation points to frame the desired area. Lets Rock.
Load up the source (MANDEL.C) and change our initialisation points for the set to ( -1.5 , 0 , -1.5 , 0 ).
Recompile, and run. Look at the bottom right area at the lines forming an inverted Y shape. See the tiny black shape about midway up the left branch? Wonder what that is? Take a look...
This time, initialise with ( -0.285338074 , -0.0331700332 , -1.18591327 , -0.933745218 )
Hmmmm... that black smudge looks kinda familiar, lets look a little closer...
Initialise with ( -0.180809051 , -0.129280129 , -1.06525023, -1.00372109 )
Hey! wait a minute! thats the same shape as the original mandelbrot, on a much, much, smaller scale!

Exploring The Mandelbrot Set
Well, I think it frickin rocks anyway. Lets look at a different fractal type, The Julia Set.
The Julia Set
Althought fractals produced with the Julia set look radically different from those of the Mandelbrot, there is a strict connection between the two. The iterative formula used to produce Julia's is the same as that of the Mandelbrot, that is:
Zn = Zn-12 + C
Each point on the complex plain corresponds to a unique Julia set, therefore the Mandelbrot can be thought of as an overview of all Julia sets, each point of the Mandelbrot is associated with a particular Julia set.
If the formula is the same, what's the difference? Well, the Mandelbrot set iterates with a starting Z of zero, and varying C. For Julia, we vary Z for a fixed C. If C is in the Mandelbrot set, then the corresponding Julia will be connected. If C isn't in the Mandelbrot set, the Julia will be a 'Cantor dust'.
The Julia set holds the same boundaries as the Mandelbrot set, that is that C < 2, though their range on the complex plain is different ( -2 < x < 2 ) and (-1.5 < y < 1.5 )

Above we have our Mandelbrot 'index'. Point A (-0.083724754 + 0.673277736i) is inside the Mandelbrot set, as such the resulting Julia (Pic A) is connected.
Point B (-0.496870240 - 0.692066610i) is outside the Mandelbrot set, and takes the form of a 'Cantor dust'. It's easy to guess why this name was given =]
Anyway, lets draw our own. The mandelbrot function is perfectly reusable for the Julia set, just rename it to julia, we've managed to save some hassle anyway =]
The iterate function needs a little more work.
unsigned char iterate( float Zre, float Zim )
{
float oZre;
int iterations = 0;
float odist = 0;
do
{
oZre = Zre;
Zre = ( Zre * Zre ) - ( Zim * Zim ) + cRe;
Zim = ( 2 * oZre * Zim ) + cIm;
odist = ( Zre * Zre ) + ( Zim * Zim); // calculate distance from origin
iterations++; // number of completed iterations
}
while( ( iterations < MAX_ITERATIONS ) && ( odist < 4 ) );
if( iterations == MAX_ITERATIONS )
return( 0 ); // Points inside the set (black)
else
return( iterations ); // All other points (white)
}
In this case, we actually supply Z, since, as mentioned it varies. cRe and cIm are the complex components of the point on the complex plane that we want to calculate the Julia from, these are constant for any Julia. Changing these values can radically change the produced image, experiment... =]
Julia sets are fully explorable using the same method as that of the Mandelbrot, and from my own experience are probably a better example of self similarity.

Exploring The Julia Set
Thats about all there is to it. =]
And With That, I Bid Ye Goodnight
If, after all that, you feel bored, confused or both, then I've failed, I apologise. Fractals really are a fascinating topic, look around for other articles -you won't regret it...
On the other hand, if you're now thinking "This rocks" then we've got a happy customer, It was all worth it.
Where to go from here? Welp, my next fractal article will explore modelling real life objects (mountains,trees,etc.), until then?
Well, your best bet is to download Fractint and explore...
Send me any interesting fractals you might find =]
NRoC