I see some things that may help you improve your code.
Move loop invariants outside the loop
To maximize performance, moving loop invariants outside the loop often helps. This is an optimization that many compilers can make on their own, but it helps to write it explicitly. In particular, the nested loop within main where the image is recalculated. ...
I'm going to reuse some parts of the answer I recently posted here on Code Review.
Losing your Loops
(Most) loops are damn slow in Python. Especially multiple nested loops.
NumPy can help to vectorize your code, i.e. in this case that more
of the looping is done in the C backend instead of in the Python
interpreter. I would highly recommend to ...
The namespace identifier is missing from a lot of names - e.g. std::sqrt, std::log, std::abs, std::stoi, std::stod. It's not portable to rely on the unqualified names being defined.
A lot of this is unnecessarily verbose. There's no need to write this->tokens all the time when tokens is perfectly clear. For example, I'...
Unlike in Python, all-caps names are usually left to macros (which you should avoid, by the way) in C++; for example: M_PI, CHAR_BIT and so on.
Moden Language Features
There are several places where you can take advantage of modern C++ features:
constexpr: pure constants and magic numbers should be declared as constexpr.
static constexpr int ...
Trust the math. In elif clause of
if rand <= 0.01: point = f1(point)
elif rand > 0.01 and rand <= 0.86: point = f2(point)
you already know that rand > 0.01 (otherwise the if clause would've been executed), so
elif rand <= 0.86: point = f2(point)
does just as good. The entire cascade should be written as
if rand <= 0.01: point = ...
This will cover performance, as well as Python style.
Save constants in one place
You currently have the magic numbers 2000 and 3000, the resolution of your image. Save these to variables perhaps named X, Y or W, H.
Mention your requirements
You don't just rely on Python 3 and Jupyter - you rely on numpy and pillow. These should go in a requirements.txt ...
I really don't get why people are so afraid to create extra classes. 200+ lines in one Program.cs file isn't something I consider good style. Sure, this is just a project for fun, but even then...
When I write a small program for myself, often they are console applications. And the first thing I do is create a class that will contain all of the logic etc. ...
There's already a good answer, so I'm just going to raise a couple of small points.
I had the same thought as Toby Speight about saving the square root of abs(z), but I would apply it also in the logarithm and avoid re-evaluating a known value. This may be taking micro-optimisation too far, but you can judge for yourself whether you think the hit to ...
I'm going to comment mostly on performance aspects of your code here. Stylistic parts of your code should be improved upon by someone more conversant in C++ than I am.
You have factored out some constants, but when you calculate zooming, 0.9 and 40 are "magic numbers", numbers with no explanation as to what they do, and those whose usage ...
Say what you mean and mean what you say
You named your class "Window" when it should really be something more like "Viewport". There are no "Windows" (in the GUI sense) in your code. If you decide to incorporate this into an application that does contain Windows in the GUI sense, this will be hopelessly confusing.
Don't omit essential documentation from ...
Don't use vectorized numpy, use numba jit instead
Using numpy to calculate the Mandelbrot set is not really a good fit because the same data will be stored and loaded from and to memory repeatedly, thrashing the cache. A better option would be to use a jit compiler to accelerate the critical code path, for example numba jit.
In this case, 4 characters can ...
Just noticed this
private Section GetHoriztonalSections()
which is IMO correct, but this
private short GenerateSection(Section section)
should be named somehow different, because it isn't generating a Section.
public void SaveImage(string filename, ImageFormat imageFormat)
this should be improved by not using SetPixel() because it is ...
I think that your limit checking is a little obscure.
Why are the constants defined in the constructor? They're related to your class, not to your constructor. If I were to be able to change the Width or Height someday, I'd need these constants to validate that my new values are still "legit".
You might want to look at the Contract class in the .Net ...
These can be combined with the Python-specific optimisations from the other answers.
Avoid the redundant square root
if (z.real**2+z.imag**2)**0.5 > 2:
is equivalent to
if z.real ** 2 + z.imag ** 2 > 4:
(simply square both sides of the original comparison to get the optimised comparison)
Avoid squaring unless ...
Use library functionality
Mandelbrot images are calculated with complex numbers. C++ offers the <complex> header which contains a template for generating complex numbers out of floats or other numeric types.
Initialize at definition
Many compilers can give you warnings about code paths with uninitialized variables but in general it might be good to ...
I think you should refactor this loop:
for (int i = 0; i < numberOfChunks; i++)
chunks.Add(new Chunk(new Point(0, imageSize.Height / numberOfChunks * i), new Point(imageSize.Width, imageSize.Height / numberOfChunks * (i + 1))));
Add braces and a couple of local variables to store the new Chunk and new Point.
The constant twoSquared is still as "...
In calcator, I would use a ternary expression: return 1. if n is None else n/100.
calcator can be made even more efficient by having iterate_mandelbrot return 100. if the loop finished. Then you just divide the result of that by 100. This will result in 1. if the loop exits, avoiding the if test entirely.
I would only do run self....
The reason your current code will eventually slow down is because you are drawing every previously calculated pixel, every millisecond. So towards the end of your drawing, you're trying to plot 500,000 pixels in every paintComponent invocation.
To fix this, create yourself a transparent buffered image which you will paint your pixels onto. Then, in your ...
You can change your Point definition to some type that implements Bifunctor. Earlier bifunctor was part of bifunctors package. Bifunctor is functor of two arguments. Here you can find more info. In case you wouldn't like to change definition of Point you can define bimap-like function for your type.
Your formatTree function inefficient, since you are ...
I see a number of things that may help you improve your code.
Fix the bugs
There are a couple of bugs in this program that should be fixed. First, the include guard in Window.h is missing an #endif, but that's an easy fix. Second, there is an object ownership problem that's a little more subtle. Within main, the window object is created as a regular ...
You can simplify the code and make it run faster if you construct the next-level carpet by continuing to work on the previous image (punching more holes in it), rather than starting with a blank slate every time. The code can look prettier and more Pythonic too, since the technique lets you get rid of the recursion.
It's a good ...
I'm not a python expert. I am pretty good with Mandlebrot generation (I've spent a lot of time on my custom Julia Set generator.)
So I'll say this: optimize the heck out of stuff that will be running many iterations. Forget about clean-code or nice OOP principles. For lots-of-iterations stuff like this, you want as nitty gritty as possible.
So let's ...
Joe Wallis gave a much better algorithm, so I'll just give more general notes.
Instead of defining alphabet manually, you could import from the string module.
from string import ascii_lowercase as alphabet
You have a confusing pattern with tmp_size. The fact that you have to define while tmp_size+1 is a red flag. It would be better to write while tmp_size ...
I propose a different method!
If you generate the center, and the size of the shape you can do the entire thing easily with str.format.
The biggest problem is getting the diamond shape.
But this can be done with ease.
For the amount of different letters you want per line it goes:
1, 2, 3, 4, ...
Heslacher's answer had a closing remark about not using SetPixel in my SaveImage method. I got a basic implementation working and performance went from 5 seconds down to 0.7. Great suggestion.
I then tried to add the new code to Heslacher's answer but an unnamed editor (okay, I'll name the editor: Heslacher) said I should post it as my own answer.
Your code reads well and is easily understandable. On top of also finding that you might have too much constant (more on that in a little bit), I just have a few nitpicks:
whitespace in expressions is weird sometimes;
a docstring would help understanding the recursion involved in snowflake_edge;
you don't need the newline continuation (\) inside parenthesis,...
I can see two reasons why your program might be slow.
The first one is that for each row you allocate memory and then free it. If you move the allocation outside of the while loop and reuse the memory then this will get rid of a whole bunch of unnecessary memory allocation operations.
The second one is probably more important: MPI stands for Message ...
I have to start with saying that this is a really cool and well made project! If you would like to improve the performance of your code, I recommend offloading work to GPU shaders. mandelbrot easily fits into the description of a fragment shader. You can use OpenGL through SFML to achieve this, see this and this.
This is a neat implementation of Lindenmayer systems. I have some suggestions for simplifying and organizing the code.
The docstring for a method or a function comes after the def line (not before, as in the code here). So you need something like:
def evaluate(self, depth):
"""Evaluate system by recursively applying the rules on the axiom."""
This is a lot of code to cover, so I'll touch on the highlights. Overall the code is pretty straightforward and easy to read. I like that you used std::complex instead of writing your own complex class or passing around 2 floats or something like that. Here are a few things I think could be improved:
Break Things Into Functions
For the most part your ...