The Mandelbrot set

Multivariable Calculus, Carleton College Math 211, Fall 2014, Prof. Joshua R. Davis

The idea

For any complex number c, we can define a complex function f(z) = z^2 + c. If you’re not familiar with complex numbers, just think of them as 2D vectors. That is, the complex number z = x + i y corresponds to the 2D point (x, y). Unlike 2D vectors, complex numbers have a multiplication operation, such that z^2 corresponds to (x^2 - y^2, 2 x y). Thus z^2 + c corresponds to (x^2 - y^2 + c1, 2 x y + c2).

c = {0.3, 0};
Clear[f];
f[x_, y_] := {x^2 - y^2 + c[[1]], 2 x y + c[[2]]}
{x, y} = {0, 0};

Now we start with (x, y) = (0, 0). We apply f to get a new (x, y) pair. Then we apply f to that to produce another (x, y), and so on. To see what I mean, evaluate this next cell repeatedly.

{x, y} = f[x, y]

We keep applying f, until we get an (x, y) of length more than 2. How many applications of f does it take? The answer depends on c in a surprisingly intricate way. To glimpse what I mean, repeat the example above with c = (0.2, 0.0) instead of c = (0.3, 0.0).

Infrastructure

For any complex number c, we can iterate the complex function f(z) = z^2 + c to produce new z-values from old ones. Starting from z = 0, we count how many iterations are needed to produce z with |z|^2 > 4. There are two cases:

A. For some c, |z|^2 never exceeds 4. Those c form what is called the Mandelbrot set. They are colored black in the images below. (The depth parameter sets how many iterations we try, before giving up and deciding that c is of this type.)

B. Every other c is assigned a positive integer k = k(c), based on how many iterations are required to achieve |z|^2 > 4. They are rainbow-colored in the images below, based on k.

Clear[color, escape, fractal];
color = Compile[{{k, _Integer}, {depth, _Integer}},
	Module[{table, n},
		table = {{1.0, 0.0, 0.0}, {1.0, 0.5, 0.0}, {1.0, 1.0, 0.0}, 
			{0.5, 1.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 0.5}, {0.0, 1.0, 1.0}, 
			{0.0, 0.5, 1.0}, {0.0, 0.0, 1.0}, {0.5, 0.0, 1.0}, {1.0, 0.0, 1.0}, {1.0, 0.0, 0.5}};
		n = Length[table];
		If[k == depth,
			{0.0, 0.0, 0.0},
			table[[Mod[k, n] + 1]]]]];
escape = Compile[{c1, c2, {depth, _Integer}},
	Module[{k, x, y},
		k = 0;
		{x, y} = {0.0, 0.0};
		While[k < depth && x^2 + y^2 ≤ 4,
			{x, y} = {x^2 - y^2 + c1, 2 x y + c2};
			k += 1];
		k]];
fractal = Compile[{x0, x1, y0, y1, {xn, _Integer}, {yn, _Integer}, {depth, _Integer}},
	Table[
		color[escape[c1, c2, depth], depth],
		{c2, y1 - (y1 - y0) / (yn * 2), y0, -(y1 - y0) / yn},
		{c1, x0 + (x1 - x0) / (xn * 2), x1, (x1 - x0) / xn}]];

Static view

This code shows the Mandelbrot set over the rectangle [x0, x1] x [y0, y1], as a static image of width x height pixels. The depth parameter is a cap on how many iterations are tried at each point c. Those c where the cap is reached are colored black. If this code is running too slowly, then decrease width, height, or depth.

x0 = -2.0;
x1 = 2.0;
y0 = -2.0;
y1 = 2.0;
width = 600;
height = 600;
depth = 100;
Image[fractal[x0, x1, y0, y1, width, height, depth]]

Dynamic view

This code produces a dynamic view of the Mandelbrot set. Click anywhere on the fractal to zoom in by a factor of 10. As above, decrease width, height, or depth to improve speed. If you zoom in repeatedly, the edges of the Mandelbrot set will eventually appear smooth. This is an artifact of the imprecise computer simulation. On a purely mathematical level, the edges exhibit complicated, rough behavior at every level of zoom.

DynamicModule[{width, height, depth, zoom, x0, x1, y0, y1, pt, x, y},
	width = 600;
	height = 600;
	depth = 100;
	zoom = 10.0;
	x0 = -2.0;
	x1 = 2.0;
	y0 = -2.0;
	y1 = 2.0;
	EventHandler[
		Style[Dynamic[Image[fractal[x0, x1, y0, y1, width, height, depth]]], DynamicEvaluationTimeout->60],
		"MouseClicked" :> (
			pt=MousePosition["Graphics"];
			x = (pt[[1]] / width) * (x1 - x0) + x0;
			{x0, x1} = {x - (x1 - x0) / (2 zoom), x + (x1 - x0) / (2 zoom)};
			y = (pt[[2]] / height) * (y1 - y0) + y0;
			{y0, y1} = {y - (y1 - y0) / (2 zoom), y + (y1 - y0) / (2 zoom)})]]