Fractals are interesting stuff! Today I’ll be talking about the Mandelbrot Set (or at least, my understanding of it..).
Before reading, please note that I do not take credit for the formulas in this article. I simply provide an explanation, and a implementation in Java.
The mandelbrot set is a so-called fractal.
There is no clear and unambiguous definition of what a fractal actually is. Fractals are best described according to the properties they possess:
Fractals have a structure with irregular details on any small scale. Thus, the more you zoom in on a fractal, the more new details you’ll be able to see.
Fractals can’t be described with regular calculus or geometry.
Fractals are often self-similar – the same pattern is repeated, over and over.
The mathematical definition of the Mandelbrot Set M is as follows:
f_{c }(z) = z^{2} + c, where Z and C are complex number. The value is considered to be in the Mandelbrot Set if it never exceeds a given value, in our case 2.
Generating the Mandelbrot Set
The Mandelbrot Set M is generated by iterating over the mathematical function given above. Let’s try to illustrate this with an example where only real numbers R are being used:
- First, we set the initial value of Z, Z = 0
- Second, we set the initial value of C.
- Third, we iterate over the function.
Let’s start of with an example. Before iterating, we set the initial value of C = 1.
Z_{n} | C | Z = Z^{2 }+ C | Z_{n + 1} |
0 | 1 | 0^{2 }+ 1 | 1 |
1 | 1 | 1^{2 }+ 1 | 2 |
2 | 1 | 2^{2 }+ 1 | 5 |
5 | 1 | 5^{2 }+ 1 | 26 |
As you might have noticed, Z is constantly increasing. As per definition, this means that Z are converging towards infinity.
Let’s do it one more time. We set the initial value of C = -1.
Z_{n} | C | Z = Z^{2 }+ C | Z_{n + 1} |
0 | -1 | 0^{2 }+ (-1) | -1 |
-1 | -1 | -1^{2 }+ (-1) | 0 |
0 | -1 | 0^{2 }+ (-1) | -1 |
-1 | -1 | –^{2 }+ (-1) | 0 |
In this example, Z is not converging towards infinity (Z is not constantly increasing).
The two examples above are simplified; the Mandelbrot Set are composed of complex numbers, not real numbers R.
Now that we’ve looked at some examples, let’s provide a definition of how we’ll implement the Mandelbrot Set in Java:
The Mandelbrot Set is composed of all values of C, where the function Z = Z² + C never exceeds 2.
The Mandelbrot Set vs Computer Graphics
So, how do we translate the above definition to a visual representation on a computer screen?
Let’s start off with a brief rehearsal of the structure of a computer screen (or any screen..):
A PC screen contains pixels, horizontal and vertical. Each pixel on a PC screen can thus be seen as a pair of numbers (a, b) – that is, the position on the horizontal axis and position on the vertical axis. In other words, we can use the PC screen as a representation of the complex plane.
With this definition, we can describe how the Mandelbrot Set is generated using by a computer.
By iterating over Z = Z2 + C we can find those values of C that do not cause Z to be greater than 2.
When we encounter any value of C that meets this requirement (Z never greater than 2), we know that this value is indeed part of the Mandelbrot Set. In the Java implementation below, we act on this by coloring the pixel black.
If we encounter any other value which not meet this requirement (in other words, Z got larger than 2), we know that the value is not a part of the set. In our example, we color the belonging pixel with random colors. To generate a more visually compelling picture, you should decide which colors to use based on how many iterations you’ve done. (I know, this is confusing. Just read the comments in the code, and you’ll get a better grasp of it).
But how do we map the coordinates in the complex plane to pixel-coordinates on a computer screen?
The mapping to pixel-coordinates is possible because the definition of a complex number is C = a + ib, where a can represent the horizontal axis, and ib can represent the vertical axis.
Implementation in Java
We now know that it is indeed possible to transform coordinates in the complex plane to actual pixels on a screen. Some programming languages have primitive support for complex numbers. However, Java do not, so this is not an option.
To solve this problem, we need a way to translate a complex number Z = a + ib to an actual pixel-coordinate (a,b). The following three formulas resolve this issue.
- pixel a = a² – b² + Re C, where Re C is the real part of C.
- pixel b = 2ab + Im C, where Im C is the imaginary part of C.
- fn(Z) = (a² + b²)^1/2
Please note that when generating the Mandelbrot Set, it is important to take into account the basis for the Mandelbrot Set; We want to find those C that cause Z to not converge against infinity. In order to find the final answer to this, we’ll have to iterate an infinite number of times! This, of course, does not work in practice, and we must set a maximum limit for the number of iterations.
/** * Drawing the mandelbrot set. * Definition: * If C is within a circle-radius of 2, then C is in the set. * Example: * Z500 = a + ib * Circle: * a² + b² = r² * Within 2 * a² + b² <= 2² * */ @Override public void draw() { // Min and max values for the real and imaginary part. double reMin = -2.00; // Re double reMax = 2.00; // Re double imMin = -2.00; // Im double imMax = 2.00; // Im int maxIterations = 512; // Removing selection rectangle if on screen if (group.getChildren().contains(rectangle)) group.getChildren().remove(rectangle); // Random colors. Color[] colors = new Color[maxIterations]; for (int i = 0; i < colors.length; i++) { colors[i] = Color.color(Math.random(), Math.random(), Math.random()); } // Distance between pixels double deltaA = (reMax - reMin) / canvasWidth; double deltaB = (imMin - imMax) / canvasHeight; // Looping through all pixels on canvas. for (int y = 0; y < canvasHeight - 1; y++) { // y mapped to the imaginary part of C double cIm = imMax + (y * deltaB); for (int x = 0; x < canvasWidth - 1; x++) { // x mapped to the real part of C double cRe = reMin + (x * deltaA); // Z starts at 0 double zRe = 0; double zIm = 0; // Counter int count = 0; while (zRe * zRe + zIm * zIm <= 4 && count <= maxIterations) { // Values for next iteration. // nextZRe = zRe² - zIm² + cRe, double nextZRe = zRe * zRe - zIm * zIm + cRe; // nextZIm = 2 * zRe * zIm + cIm double nextZIm = 2 * zRe * zIm + cIm; // Updating zRe = nextZRe; zIm = nextZIm; count++; } // If count<=maxIterations, we can assume the value is not in the set. if (count <= maxIterations) { // Not in Mandelbrot set, drawing with random colors. gc.setFill(colors[count - 1]); } else { gc.setFill(Color.BLACK); } // Drawing gc.fillRect(x, y, 1, 1); } } }