📜 ⬆️ ⬇️

Constructing the Julia Set

Hey. Passions are raging, the end of the year, sessions, deadlines, the new year, as well as censorship penetrates into all layers of the Internet, which can not but grieve. Habr is no longer a cake . I just wanted to write that I do not agree with this approach, but then I would just be banned. So you have to write interesting content. Although if you are banned because of the preface to the post about Julia's set, well, then the remains of the cake have fallen out and there is no chance.

So back to the topic of the post. I have long wanted to learn a little more about complex numbers, and not just that the root of minus one is i. Particularly interesting were figures with a fractal structure, I wanted to understand what it means and how to make such visualization. Somewhere on the shelf was a book on the complex function of the complex , as well as a course on complex analysis on the cursor , and a little free time from work appeared. Let's get started

Function iteration


Before you write an algorithm, you will have to learn a few basic concepts. We begin with the concept of iterating the function f . We introduce the following notation for the nth iteration of the function f :



The zero mapping is the identity mapping :

')
There are many theorems about fixed points of certain types of mappings; let us recall what a fixed point of some mapping g is , this is a solution to the following equation:


Similarly, for iterating a function, a attracting point is introduced — such a point from the domain of the function f , that the sequence value of the iterations of the function f converges to some fixed point of the map f :





The set of all y satisfying the previous condition is called the limit set of the point x under the action of iteration of the function f .

As a rule, it is of interest to study the sequence of functions formed by iterations (a similar idea can be seen in Newton's method , the desired solution turns out to be the attracting fixed point of the constructed map, and therefore can be found as the limit of the sequence of iterations.)



as well as a sequence of values ​​calculated at some starting point



For a complex world, visualizing the iterative convergence of the point y to x under the action of the iteration of the function f is a bit problematic, all the same 4d, but for the real world the picture is only 2d (the red path diverges while the blue converges to a fixed point).



So, the first pebble necessary for understanding the Julia set is laid, we proceed to the next.

Quadratic complex polynomial



In general, the Julia set is constructed for a function of the form





We will simplify the problem, and we will build the Julia set only for a quadratic polynomial. A quadratic complex polynomial in general form is as follows:



There is also the concept of a unitary ( a = 1 ) centered ( b = 0 ) form of a quadratic polynomial.



Introduce the following mapping φ , and find its inverse mapping:





Consider the following expression



It is easy to see that the last expression resembles a quadratic polynomial in the general form of p (z) , let's find a c such that the resulting expression is reduced to a quadratic polynomial of general form



Thus, we have shown that with the above replacement c , a generalized polynomial can be written as



Behavior of a quadratic polynomial under the action of iterations


We turn to the third pebble base for understanding the Julia set. Consider the second iteration of the complex quadratic polynomial



Not hard to show that



And just here we combine all three points, let's formulate a conclusion: in order to investigate the behavior of a quadratic polynomial under the action of iterations, it is enough to examine it in a unitary centered form, and not in general form . And this is really cool, because in general, we have as many as three coefficients, and in the unitary centered form only one.

Chaotic and normal behavior



Soon it will be possible to give a definition of Julia's set. We will consider more intuitive definitions than strictly mathematical ones. But first, consider the concept of sustainability. The solution of a certain differential equation is called stable if the behavior of the solution with conditions close to the initial conditions is not much different from the behavior of the original solution. It is not difficult to guess that all the salt in the phrase is not much different . In general, various types of stability are distinguished, for example, Lyapunov stability is involved in the definition of the Julia set (this clearer definition follows from the Montel theorem on a compact family of functions ), but we will not even go into the essence of the differences in stability, here the main thing is to understand the idea. Let's try to illustrate it. To begin with, let's take a look at the intuitive perception of stability, here the blue and yellow trajectories of points with a small change behave almost the same, while for the red trajectory, a small change leads in a completely different direction.



Let's consider the following polynomial f (z) = z 2 - 1 + 0.213i , and construct a trajectory for 100 iterations with z 0 = 0.3 + 0.2 i and z 0 = 0.31 + 0.2 i



In this example, you see that a small change in the initial condition resulted in a divergence of the trajectories, we call this behavior chaotic in a certain neighborhood of the initial point, i.e. behavior strongly depends on small changes in the initial conditions. While the behavior under which stability is maintained, i.e. small changes in the initial conditions do not strongly affect the behavior in general, we will call normal, as in the following example. Consider the following polynomial f (z) = z 2 - 1 + 0.2i (just slightly changed the constant c ), and construct a trajectory for 100 iterations at z 0 = 0.3 + 0.2i and z 0 = 0.31 + 0.2i



Fatu, Julia and Mandelbrot sets





Attractor Pool



So we learned the meaning of the Julia set, we can proceed to the algorithm for constructing such a set. But for this, it is necessary first to introduce some other definitions.



Consider A (∞) in the context of our reasoning for some polynomial f (z) = z 2 + c



It turns out that the following theorem exists. The set A (∞) (in the context described above) is open , connected and unbounded . It is fully contained in the set of veils . Julia set coincides with the boundary A (∞) , which is a closed and bounded subset of all complex numbers.

Constructing the Julia Set



I remind you that we will write an algorithm for constructing the Julia set of quadratic dynamics , and not any function. So, we learned that the Julia set coincides with the boundary A (∞) , which is a closed and bounded subset of all complex numbers, i.e. Julia's set is also closed and bounded. Let us denote as follows the subset of complex numbers that is generated by iterations of the function f and remains bounded. We call such a set a filled Julia set :



And the last theorem that we need sounds like this:


In addition to the algorithm itself, it can be concluded from this theorem that the entire Julia set is inside a ball of radius R with center at the origin.

PS: approximately here lovers of design and golden sections, mystics and occult, will notice that with f (z) = z 2 + 1 , the threshold R is equal to the golden section o_0

Algorithm


  1. Choose c to set the polynomial f (z) = z 2 + s
  2. Calculate R for a given polynomial f (z) = z 2 + s
  3. We select the maxIter parameter to designate the maximum iteration, obviously the higher it is, the better the accuracy, the slower the algorithm
  4. We generate an array of colors, the total maxIter pieces, say from less bright to brighter, we denote by color, how far away is the point from the Julia set
  5. For each point, we calculate whether it is part of the filled Julia set or not, as well as the iteration number at which the threshold was exceeded
  6. if | z | > R then use the first color, then use the light on which iteration number the threshold was exceeded


Julia set generation procedure
/// <summary> /// Generate bmp with Julia set /// </summary> /// <param name="c">parameter of square dynamics</param> /// <param name="w">width of bmp</param> /// <param name="h">height of bmp</param> /// <param name="maxIter">max iterations of function</param> /// <param name="xMin">window in complex plane</param> /// <param name="yMin">window in complex plane</param> /// <param name="xMax">window in complex plane</param> /// <param name="yMax">window in complex plane</param> /// <returns></returns> static XBitmap PlotJuliaSet(ComplexNumber c, int w, int h, int maxIter, double xMin = Double.NaN, double yMin = Double.NaN, double xMax = Double.NaN, double yMax = Double.NaN) { double r = CalculateR(c); if (Double.IsNaN(xMin) || Double.IsNaN(xMax) || Double.IsNaN(yMin) || Double.IsNaN(yMax)) { xMin = -r; yMin = -r; xMax = r; yMax = r; } Logger.Instance.Log("R = " + r); double xStep = Math.Abs(xMax - xMin) / w; double yStep = Math.Abs(yMax - yMin) / h; XBitmap bmp = new XBitmap(w, h); IDictionary<int, IDictionary<int, int>> xyIdx = new Dictionary<int, IDictionary<int, int>>(); int maxIdx = 0; for (int i = 0; i < w; i++) { xyIdx.Add(i, new Dictionary<int, int>()); for (int j = 0; j < h; j++) { double x = xMin + i * xStep; double y = yMin + j * yStep; ComplexNumber z = new ComplexNumber(x, y); IList<ComplexNumber> zIter = SqPolyIteration(z, c, maxIter, r); int idx = zIter.Count - 1; if (maxIdx < idx) { maxIdx = idx; } xyIdx[i].Add(j, idx); } } for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int idx = xyIdx[i][j]; double x = xMin + i * xStep; double y = yMin + j * yStep; ComplexNumber z = new ComplexNumber(x, y); bmp.SetPixel(w - i - 1, j, ComplexHeatMap(idx, 0, maxIdx, z, r)); } } return bmp; } private static IList<ComplexNumber> SqPolyIteration(ComplexNumber z0, ComplexNumber c, int n, double r = 0) { IList<ComplexNumber> res = new List<ComplexNumber>(); res.Add(z0); for (int i = 0; i < n; i++) { if (r > 0) { if (res.Last().Mod > r) { break; } } res.Add(res.Last() * res.Last() + c); } return res; } private static double CalculateR(ComplexNumber c) { return (1 + Math.Sqrt(1 + 4*c.Mod))/2; } public static Color ComplexHeatMap(decimal value, decimal min, decimal max, ComplexNumber z, double r) { decimal val = (value - min) / (max - min); return Color.FromArgb( 255, Convert.ToByte(255 * val), Convert.ToByte(255 * (1 - val)), Convert.ToByte(255 * (z.Mod / r > 1 ? 1 : z.Mod / r)) ); } 



results



Here are a few of the 5k to 5k pictures made using this feature.


And now let's look at the Julia set for the polynomial f (z) = z 2 - 0.74543 + 0.11301i , the whole set is contained in the ball of radius 1.50197192317588 .













Approximately on this picture it seems that the limit of accuracy has been reached, and all the red elements belong to the set of Julia. But it was not there, we increase the maxIter parameter, and we get an even more accurate approximation. In general, you can continue indefinitely. No kidding -)

Source: https://habr.com/ru/post/206516/


All Articles