Complex Numbers and the Mandelbrot Set as They Relate to Development

Complex Numbers

What You Should Expect to Learn


Why do complex numbers matter?
What are complex numbers?
How do complex numbers function?
How do you do basic mathematical functions with complex numbers?
How do you apply these mathematical functions to Roblox and beyond?
What is a sequence and how do you figure one out?
How do you apply complex numbers to mathematical formulas?
How do you make a Mandelbrot Set in roblox?
Where do I go from there?


Table of Contents


Introduction to Complex Numbers
Getting into the nitty gritty
The complex number i
Applying this back to the complex plane
Where it starts getting confusing interesting
Addition and Subtraction
Multiplication and Division
The Mandelbrot Set
Sequences
Mandelbrot Set (Part 2)
Calculating the Mandelbrot Set
Concluding thoughts and messages




Introduction to Complex Numbers
Complex numbers are unimaginably useful for anything as high scale as engine development, to something as comparatively low scale as Roblox scripting. Complex numbers are used frequently for more math-heavy games, and certain developers of enormous games like Phantom Forces use complex numbers (in the form of quaternions) all the time. Complex (or imaginary) numbers are often behind a veil of obscurity due to their strange and hard to think about nature, like all too many parts of math, however, they are easy to think about for anyone if you think about it correctly.


Getting into the nitty gritty
From a young age, we have all known the number line


Normally, we only think of numbers as left to right, left subtracting and right adding
This would be considered 1D, or 1 dimensional because there is only one dimension, left and right

The reality, however, is unsurprisingly more complex
There are numbers in a second dimension on a number line, up and down, and these numbers are called “imaginary” or “complex” numbers


This may seem similar to what you use in math class, the XY cartesian plane, however, it is not
Although the cartesian plane is oftentimes used to represent the complex plane, it is NOT the same
This 2D plane of “real” and “imaginary” numbers is called the Complex Plane

So, let me backtrack and explain a few things about what in the world I just posted


The complex number i
The answer to the question of: “what is the value of the square root of -1?” has been thought upon for a long time, and there really isn’t a “real” answer to this question. This, however, does not prevent us from utilizing complex numbers. Rather than just giving up, we assign the value of the square root of -1 to a variable of sorts, i, and act like it exists
In doing this, we are able to apply i in order to solve problems we couldn’t with only the number line


Applying this back to the complex plane
So, giving i the value of the square root of -1, we can draw a 2 dimensional grid of numbers using this, where up and down is the “imaginary” axis, and left and right is the “real” axis (the number line)

So, the first question you might ask about this weird plane, is how do you represent points on it
If this were a cartesian, or XY plane, you would use something called a coordinate pair (x,y) to represent the points, for example:


HOWEVER, the complex plane is different from a cartesian plane, so a different system will be used
In the spirit of the cartesian plane, I will make an analogy between the two to bridge the gap from elementary school level math to high school level math

The “real” axis is like the x axis
The “imaginary” axis is like the y axis

Rather than doing:
(x, y)
We will do
x+yi

For example, if we translate that same list of points from the above example ((1,4) (1,2) (2,3) (3,1)) into the complex plane, it would look something like this:
image
(1,4) goes to 1+4i
(2,3) goes to 2+3i
Etc…


Where it starts getting confusing interesting

Recall that i has a value of the square root of -1, and imagine the properties this might give a number on the complex plane

Lets think about the primary operations of math, primarily: Addition, Subtraction, Multiplication, and Division

Addition and Subtraction
image
Or, in terms of i
i + i = 2i
image
Or
2i + 3i = 5i

So, for addition (and subsequently subtraction), i does exactly what you would expect it to do
ai + bi = (a+b)i
The same that
a + b = (a+b)

(And obviously as shown before, you cannot add a real and complex number, so you just leave it as is: eg. 1+2i is just 1+2i, nothing special)

Now, moving on to multiplication and division

Multiplication and Division
This is where it starts getting harder for most people
Multiplying a real number by a complex number or vice versa is pretty easy
1 * i = i
2 * i = 2i
-1 * i = -i
-3 * i = -3i
Etc
This is perfectly normal.

Where it starts getting interesting is multiplying i by itself

i * i = ?
Well lets go back to what i is, the square root of -1
image
This behavior might seem a bit weird to some people, however when you think about it in a certain way it makes more sense
Anything times itself twice is the equivalent of squaring it
For example, 5 * 5 is the same as 52, or 25
This applies to i too
i * i = i2
image
This should make much more sense now

So, to recap:
1 * i = i
i * i = -1
-1 * i = -i
And now, -i * i
This is the same as just i * i but negative, so -(-1) is just 1
-i * i = 1

If you’ll notice, multiplying a number by i will bring it in and out of the imaginary axis
1 * i takes a real number (1) and “rotates” it onto the imaginary axis
i * i takes an imaginary number (i) and “rotates” it onto the real axis
-1 * i takes a real number (-1) and “rotates” it onto the imaginary axis
On and on
This rotation is not random, it rotates it 90 degrees counter clockwise every time you multiply by i
The same applies to division, it rotates the number 90 degrees clockwise every time you divide by i, so opposite

In case you’re still confused, I will supply a simple graphic


The same with division, but opposite

This is not only quite cool, but is also insanely useful for game development
Let me elaborate:
This behavior of i rotating the number on the imaginary plane allows you to add and subtract angles to find somethings position relative to something elses position (along with many other cool, more pure math things which I will touch on at the end of this post)

You can also multiply combinations of real and complex numbers in the form of x+yi
(x+yi) * (x+yi)

Warning: Algebra class flashback trigger warning

You can use FOIL to multiply the complex pairs

If you forget the rules of FOIL, its First Outer Inner Last, and if you dont know what FOIL is, good, (I think its just another dumb thing they teach to make math more confusing)

(a+bi) * (c+di)
Literally all you do is distribute a → c and d
Then you distribute b → c and d
image

So for example:

(1+2i) * (3-i)
Lets begin with a graph
image
So now lets calculate what point would be the multiplication of those two points then plot it

(1+2i) (3-i)
1*3+1*-i + 2i*3+2i*-i
3 + -i + 6i + 2
5+5i
(1+2i) (3-i) = 5+5i


Now, lets take a closer look at this behavior
image
In drawing a line from 0+0i to each point and getting their angle to the positive real axis, we will begin to see some interesting behavior
Lets bring the result of their multiplication into the equation…

The angles of the two points from the positive real axis ADD, and the lengths of the two points from 0,0 MULTIPLY

Anyone with any experience with more advanced scripting knows how unimaginably useful this behavior is

Division is MUCH more complicated, so I’ll cover that later on when we get to the “formula” section
(But just in case you were wondering, yes, division subtracts the angle, and divides the distance, opposite of multiplication)


The Mandelbrot Set
Lets bring this tutorial on the basics of complex numbers to a close, and directly tie it to the world of Roblox with a fun activity related to complex numbers, involving another illusive math subject: fractals

Before we do this, however, we must learn a bit about sequences


Sequences
Sequences are a set of points, usually defined by a single equation and starting position
Sequences are a lot like tables, in that they each have “indexes” (indices for those who require grammar)

Consider the following equation:

an+1 = an+1 → Where a0 = 1
an basically means “the nth term in a sequence named a”, where “n” is the number, or index representing each value in the sequence

What the sequence equation above is basically saying is:
The next term in sequence a is the previous term + 1, where the first term’s value is equal to 1

So lets start from a0 = 1
From a0 = 1, we can determine that a1 = a0 + 1, so we know that a1 is 2
Then from that we know a2 is 3
Etc

Lets try a slightly more complex example:

an+1 = an * 2 → Where a0 = 4

The sequence would go:
a0 = 4
a1 = 8
a2 = 16
a3 = 32
Etc…

Why dont we, just for fun, try the fibonacci sequence (Ill start this one at 1 rather than 0 for simplicity sake)
an = an-1 + an-2 → Where a1 = 1 and a2 = 1

a1 = 1 (given)
a2 = 1 (given)
a3 = a1+a2 = 1+1 = 2
a4 = a2+a3 = 1+2 = 3
a5 = a3+a4 = 2+3 = 5
a6 = a4+a5 = 3+5 = 8

And if we look up the Fibonacci sequence, it is indeed 1,1,2,3,5,8,…

Now that you’ve practiced with sequences, its time to move onto a more complex sequence

Mandelbrot Set (Part 2)

The Mandelbrot set is defined using the function:
zn+1 = zn2 + C → Where z0 = 0+0i, and C is a constant representing a position on the complex plane

(zn and C are both complex number pairs)

If a sequence, when given position C, extends to infinity as n increases, it is NOT part of the Mandelbrot Set
If a sequence, when given position C, infinitely loops around and doesn’t extend to infinity, it is part of the Mandelbrot Set

When looking at most images of the Mandelbrot Set, anything that’s black is part of the set, anything else is not

Calculating the Mandelbrot Set

Please consider these three points and how they relate to trying to calculate the Mandelbrot Set in Roblox

Consideration 1:
While I would not love to sit here for hours and calculate 50 values of the Mandelbrot Set, at this point we will begin the creation of a Lua program which calculates it for us
Consideration 2:
Infinity is a tiny bit too high for Roblox (or any other computer for that matter), no offense. While we could calculate the set to z99999999999999999999999999999999999999999999999999999999 and see if its not infinity it would be better to choose a reasonable number for approximation. For a rough outline of the Mandelbrot Set, you really only need to calculate up to z10 or so in the sequence, and see if its within let’s say 2 units from the origin
Consideration 3:
Roblox doesn’t support complex numbers by default, so we’ll be writing a couple of formulas representing what complex calculations would look like

Considering these three things, lets begin writing our Lua program

First we must write some functions so Roblox can deal with our representation of complex numbers
We will be using the Vector2 class to represent a complex number, where X represents the real component, and Y represents the imaginary component

For example, the equivalent of 5-2i would be Vector2.new(5,-2)

The Vector2 class already supports addition in the same way it works with complex numbers, so we have that covered already
Multiplication is a different story however
You can simplify the FOIL equation a bit, the process of which is shown below

Remember,

  1. X multiplied by i is rotated into the Y axis
  2. Y multiplied by i is rotated into the X axis
  3. Only when a Y value is multiplied by i will it be inverted (multiplied by -1) (Multiplying by i twice only is inverted once because once multiplied once its in the X axis and doesn’t invert)

If any of these rules dont make sense feel free to scroll back to the section earlier about how i rotates numbers 90 degrees

(ax+ayi) + (bx+byi) 
ax*bx + ax*byi + ayi*bx + ayi*byi --multiply out using foil
(ax*bx + -ay*by) + (ax*by + ay*bx)  --separate out into real and imaginary 
                                     --using rules listed above
Real component = (ax*bx - ay*by)
Imaginary component = (ax*by + ay*bx) 
Dividing complex numbers formula (unrelated but interesting)

image
image
The way to derive this is slightly more complicated, but that’s not what this tutorial is about, so I will direct the curious, as per usual, to Khan Academy
He does not explicitly say how to derive these formulas, but I believe you could derive them through the same means we derived the multiplication formula (I’ve never done it but I can’t imagine why it would be different, if anyone wants to try it out that’d be cool)


function multiply_complex(a,b)
    local real = a.X*b.X - a.Y*b.Y
    local imaginary = a.X*b.Y + a.Y*b.X
    return Vector2.new(real,imaginary)
end

Let us now program in the Mandelbrot formula itself, the formula
I will be using recursion which can look a bit weird, but I will put the iterative solution below for those who are more comfortable with it
zn+1=zn2 + C → Where z0 = 0+0i
(This formula can logically be rewritten as:
zn=zn-12 + C → Where z0 = 0+0i
which is what I’ll be using below for simplicity sake)

function mandelbrot(n,c)
    if n == 0 then
        return Vector2.new(0,0) --if n is the starting value that we know, then give it to who asked for it
    end
    local previous = mandelbrot(n-1,c) --Keep asking for the previous value of the set until we reach the known value z_0 = 0+0i, from which point we can calculate the rest
    return multiply_complex(previous,previous) + c --previous*previous = previous^2
end
Alternative approach using a loop
function mandelbrot_iterative(n,c)
    local value = Vector2.new(0,0) --starting value at n_0
    for i = 1,n do
        value = multiply_complex(value,value) + c
    end
    return value
end

At this point, all we have to do is test many different c values, and calculate them up to z10 and see if they’re less than 2 studs from the origin (we’ll be using the built in .Magnitude property of Vector2, where magnitude is the distance from 0,0 of the Vector2 value)
This can be accomplished with a simple for loop

We also need a function for creating a part (because we’ll be representing the mandelbrot set with parts)
This function is listed below alongside the for loop

function create_part(pos)
    local part = Instance.new("Part")
    part.Size = Vector3.new(0.1,0.1,0.1)
    part.Anchored = true
    part.Color = Color3.new(0,0,0)
    part.Position = Vector3.new(pos.X,0,pos.Y)
    part.Parent = workspace
end

for x = -4,4,0.1 do
    for y = -4,4,0.1 do
        local z_10 = mandelbrot(10,Vector2.new(x,y))
        if z_10.Magnitude < 2 then
            create_part(Vector3.new(x,0,y))
        end
    end
end

Compiling this code into one location would look something like:

function multiply_complex(a,b)
    local real = a.X*b.X - a.Y*b.Y
    local imaginary = a.X*b.Y + a.Y*b.X
    return Vector2.new(real,imaginary)
end

function create_part(pos)
    local part = Instance.new("Part")
    part.Size = Vector3.new(0.1,0.1,0.1)
    part.Anchored = true
    part.Color = Color3.new(0,0,0)
    part.Position = Vector3.new(pos.X,0,pos.Y)
    part.Parent = workspace
end

function mandelbrot(n,c)
    if n == 0 then
        return Vector2.new(0,0) --if n is the starting value that we know, then give it to who asked for it
    end
    local previous = mandelbrot(n-1,c) --Keep asking for the previous value of the set until we reach the known value z_0 = 0+0i, from which point we can calculate the rest
    return multiply_complex(previous,previous) + c --previous*previous = previous^2
end

for x = -4,4,0.1 do
    for y = -4,4,0.1 do
        local z_10 = mandelbrot(10,Vector2.new(x,y)) --calculate up to z_10 in the set
        if z_10.Magnitude < 4 then --if distance from 0,0 is less than 2 its part of the set
            create_part(Vector2.new(x,y)) --make a part there
        end
    end
end

Quickly plugging this code into studio yields these results:
image

Now this is cool and all but it isn’t quite what it could be…
Lets dial up the bass a bit
Ill add some variables in to make it easier to play around with

local fineness = 0.05 --how finely it moves C, will add more density (much more lag)
local scale = 5 --how big it is
local origin = Vector3.new(0,0,0) --where its located

local n_depth = 10 --how far in we calculate 
local condition = 4 --if its above this it wont be in the set (treated as infinity)

local waitTime = nil --use for super laggy ones, will wait in between y sweeps so you dont crash roblox

function multiply_complex(a,b)
    local real = a.X*b.X - a.Y*b.Y
    local imaginary = a.X*b.Y + a.Y*b.X
    return Vector2.new(real,imaginary)
end

function create_part(pos)
    local part = Instance.new("Part")
    part.Size = Vector3.new(fineness*scale,fineness*scale,fineness*scale)
    part.Anchored = true
    part.Color = Color3.new(0,0,0)
    part.Position = origin + Vector3.new(pos.X,0,pos.Y)
    part.Parent = workspace
end

function mandelbrot(n,c)
    if n == 0 then
        return Vector2.new(0,0) --if n is the starting value that we know, then give it to who asked for it
    end
    local previous = mandelbrot(n-1,c) --Keep asking for the previous value of the set until we reach the known value z_0 = 0+0i, from which point we can calculate the rest
    return multiply_complex(previous,previous) + c --previous*previous = previous^2
end

for x = -4,4,fineness do
    if waitTime then
        wait(waitTime)
    end
    for y = -4,4,fineness do
        local z_n = mandelbrot(n_depth,Vector2.new(x,y)) --calculate up to z_10 in the set
        if z_n.Magnitude < condition then --if distance from 0,0 is less than 2 its part of the set
            create_part(Vector2.new(x*scale,y*scale)) --make a part there
        end
    end
end

Concluding thoughts and messages

Have fun with this script and the new things you have learned about
I would make a tutorial about quaternions and apply this more directly to Roblox, but people smarter than me have already made these tutorials →
Rotations With Quaternions - suremark,
How To Think About Quaternions - EgoMoose,
A couple of Advanced CFrame Tricks - EgoMoose

Thanks for taking the time to read this tutorial about the complex numbers mandelbrot set

And no I don’t apologize for the puns


TL;DR: Number go spin

69 Likes

sigh dont you just love looking at mandelbrot set

2 Likes

I just got home from school and I see this :joy: But seriously, nice tutorial, helpful!

2 Likes

Hey, I’m glad you made your post a tutorial, very useful! I’m trying to wrap my head around this.

2 Likes

I guess this is why they say computer science is heavily dependent on math topics for complex stuff. But anyways it was a good tutorial but I didn’t understand most stuff

2 Likes

Nice Tutorial! While there are still some concepts that I have to wrap my mind around, I think Im a bit more comfortable with what the i is in formulas XD Hopefully I will be learning what quaternions are too soon because i need to use them in some project im working on :sunglasses: (Hint: Astrobee, a drone in ISS)

Present for you :bread: :smiley:

1 Like

Well, the formula relies on the following identity(you will see why):
a^2 - b^2 = (a+b)(a-b)
Suppose we have two complex numbers that we want to divide and that their quotient is the following:
(a + bi) / (c + di) We multiply both of them with (c-di) in order to achieve the above identity and so we have : (a+bi)(c-di) / (c+di)(c-di) = (ac - adi + bic - bd(i^2))/((c^2)-(di)^2) =
(ac - adi + bic + bd) / (c^2 + d^2) = (ac + bd) / (c^2 + d^2) + i(bc-ad) / (c^2 - d^2)
Thus, the real part of our new complex number is :
(ac + bd) / (c^2 - d^2) and the imaginary :
(bc - ad) / (c^2 - d^2)
That’s how basically the formulas are derived. Oh, and by the way I really liked your tutorial. Sorry, for the bump.

More math…!
I love and yet hate this at the same time. Good job! :clap:

this type of stuff is why i’m scared of the next years of “high-school” bro…

only reason i still try to like math is because it is absolutely necessary in game development, and, currently, it’s pretty cool, i’m just kinda scared of what is to come in the next few years since i’m just starting “high-school”

btw, i put “high-school” under quotes because it doesn’t exist in my country, but if it did, i would be in the first year

1 Like

It’s a bit of a bump, but I thought I’d add a contribution I made a few days ago here. I’m open sourcing a Mandelbrot set renderer! It lets you render high quality videos of zooming into it!

MandelbrotSetViewer.rbxm (66.7 KB)

Math is cool! Why didn’t we learn about this fractal in school?