# ASCII Mandelbrot

First of all, let me answer the question that a lot of you may have:

The Mandelbrot set is the set of values of c in the complex plane for which the orbit of 0 under iteration of the quadratic map when f(x) = x2 + c.

The fun thing about the Mandelbrot set is that it's a fractal and you can indefinitely zoom into it. Here's a demonstration.

This code does not zoom into the set, it only shows the first iteration of the fractal. This ASCII image is taken directly from the result of my program:

                                     *
*******
************
***************
*****************
********************
*********************
* *       **********************  *
*****     *********************** *
********   *****************************
********* *************************
**********************************
***********************************
*************************************
************************************
***********************************
***********************************
***********************************
***********************************
************************************
*************************************
***********************************
**********************************
********* *************************
********   *****************************
*****     *********************** *
* *       **********************  *
*********************
********************
****************
***************
************
*******
*


The code is simple, it calculates if a character between height and width is placed on the set, if it is, it inserts an asterisk. If it's not, it inserts a space. It then adds a newline char. Here's the code:

#include <iostream>
#include <vector>
#include <string>
#include <cmath>

const int height = 40, width = 80;
typedef std::vector<double> tuple;

tuple coord_to_complex(double i, double j)
{
double real = (4*i) / (width - 1) - 2.0;
double imaginary = (2.2 * j) / (height - 1) - 1.1;

tuple ret = {real, imaginary};

return ret;

}

bool is_in_mandelbrot(tuple re_im)
{
auto cr = re_im[0], ci = re_im[1];
auto zr = cr, zi = ci;

for (int t = 0; t <= 1000; ++t)
{
zr = (std::pow(zr, 2) - std::pow(zi, 2)) + cr;
zi = (2*zi*zr) + ci;

if (std::sqrt(std::pow(zr, 2) + std::pow(zi, 2)) > 2)
return false;
}

return true;

}

std::string print_set()
{
std::string ret = "";

for (int i = 0; i <= height; ++i)
{
for (int j = 0; j <= width; ++j)
{
if (is_in_mandelbrot(coord_to_complex(j, i)))
{
ret += '*';
}
else
{
ret += ' ';
}

}

ret += '\n';
}

return ret;
}

int main() {
std::cout << print_set() << std::endl;
return 0;
}


There's nothing else to say but thanks. So thank you for reading my code.

Credits: Most of the code is based on a codegolfing practice in Python that I found on Github so thanks to the Python programmer who wrote solved that coding challenge.

Good work! I would only like to give you one piece of advice. I see this line in you code:

typedef std::vector<double> tuple;


I don't know if you are aware of this or not, but there is actually a data structure in the C++ standard library called std::tuple. Click on the text for more information about it. I see that you did not include the std::tuple, which is good. You also fully qualified all standard C++ library names with std:: instead of using namespace std, which is also very good.

Just it is generally frowned upon in the C++ programming community to name your own variables and/or data types with the same name as names defined in the C++ standard library. You could have some name collisions and hidden bugs! If this is just your own project, of course you can do whatever you want. But keep in mind that if you are working on a project with multiple programmers at a large company, someone else might use your tuple intending to use std::tuple or vice versa. In large source files, such small one line declarations can easily be missed if you are scrolling through the code editor very fast. Someone can unintentionally make the mistake. Or someone who does not like you might put using std::tuple; somewhere intentionally to make your code fail. So it is best to avoid name collisions. There is a kind of philosophy called defensive programming. People tend to do things by their habits. If they are used to one kind of programming practice in their personal projects, they will do the same things at their job. Strive to work how you would work at your job. Think about what other people will think when they have to manage your code. Good luck!

• I understand. I didn't know there's such a thing as std::tuple. Otherwise I would have used it. Nov 14, 2018 at 0:28

It's strange that you're using vectors instead of std::complex to represent your complex numbers. I think you'll find that using the standard complex-number class will simplify your code.

It's much more efficient and accurate to square a number by simply multiplying rather than going via the much more general std::pow(). Of course, with std::complex, you don't need to do this squaring yourself, as the std::abs() function is provided to do that for you.

It's also more efficient to compare the squared magnitude against the square of the bound, rather than to compute the square root of the magnitude (so use std::norm() rather than std::abs()).

These changes make for a much more natural version of the code (it's easier to see how it corresponds to the Mandelbrot algorithm):

#include <complex>

constexpr int height = 40, width = 80;

std::complex<double> coord_to_complex(double i, double j)
{
double real = (4*i) / (width - 1) - 2.0;
double imaginary = (2.2 * j) / (height - 1) - 1.1;

return {real, imaginary};
}

bool is_in_mandelbrot(const std::complex<double> c)
{
auto z = c;

for (int t = 0;  t <= 1000;  ++t)
{
z = z * z + c;

if (std::norm(z) > 4)
return false;
}

return true;
}


You can save some memory allocations by reserving space for the output:

std::string ret = "";
ret.reserve((width+1) * height);