Path: blob/main/latex-templates/templates/mathematics/fractals.tex
51 views
unlisted
\documentclass[a4paper, 11pt]{report}1\usepackage[utf8]{inputenc}2\usepackage[T1]{fontenc}3\usepackage{amsmath, amssymb}4\usepackage{graphicx}5\usepackage{siunitx}6\usepackage{booktabs}7\usepackage{xcolor}8\usepackage[makestderr]{pythontex}910\definecolor{mandel}{RGB}{0, 0, 139}11\definecolor{julia}{RGB}{139, 0, 139}12\definecolor{sierp}{RGB}{34, 139, 34}13\definecolor{koch}{RGB}{255, 140, 0}1415\title{Fractal Geometry and Self-Similarity:\\16Computational Analysis of Fractal Structures}17\author{Department of Applied Mathematics\\Technical Report AM-2024-003}18\date{\today}1920\begin{document}21\maketitle2223\begin{abstract}24This report presents a computational exploration of fractal geometry, examining the Mandelbrot set, Julia sets, Sierpinski triangle, and Koch snowflake. We compute fractal dimensions using box-counting methods, analyze escape-time algorithms, and investigate the self-similar structures that characterize these mathematical objects. All visualizations are generated using PythonTeX for complete reproducibility.25\end{abstract}2627\tableofcontents2829\chapter{Introduction}3031Fractals are geometric objects that exhibit self-similarity at all scales. Unlike classical Euclidean geometry, fractals often have non-integer (fractal) dimensions, reflecting their intricate structure.3233\section{Defining Fractals}34A set $F$ is a fractal if:35\begin{itemize}36\item It has a fine structure at arbitrarily small scales37\item It is too irregular to be described by traditional geometry38\item It exhibits self-similarity (exact or statistical)39\item Its fractal dimension exceeds its topological dimension40\end{itemize}4142\section{Fractal Dimension}43The box-counting dimension is defined as:44\begin{equation}45D = \lim_{\epsilon \to 0} \frac{\log N(\epsilon)}{\log(1/\epsilon)}46\end{equation}47where $N(\epsilon)$ is the number of boxes of size $\epsilon$ needed to cover the set.4849\chapter{The Mandelbrot Set}5051\section{Definition}52The Mandelbrot set $M$ is defined as the set of complex numbers $c$ for which the iteration:53\begin{equation}54z_{n+1} = z_n^2 + c, \quad z_0 = 055\end{equation}56remains bounded as $n \to \infty$.5758\begin{pycode}59import numpy as np60import matplotlib.pyplot as plt61from matplotlib.colors import LinearSegmentedColormap62plt.rc('text', usetex=True)63plt.rc('font', family='serif')6465np.random.seed(42)6667def mandelbrot(c, max_iter):68z = 069for n in range(max_iter):70if abs(z) > 2:71return n72z = z*z + c73return max_iter7475def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, max_iter):76x = np.linspace(xmin, xmax, width)77y = np.linspace(ymin, ymax, height)78mset = np.zeros((height, width))7980for i in range(height):81for j in range(width):82c = complex(x[j], y[i])83mset[i, j] = mandelbrot(c, max_iter)8485return mset8687# Generate Mandelbrot set88width, height = 800, 60089max_iter = 1009091# Full view92mset_full = mandelbrot_set(-2.5, 1.0, -1.25, 1.25, width, height, max_iter)9394# Zoomed view (Seahorse valley)95mset_zoom = mandelbrot_set(-0.75, -0.73, 0.1, 0.12, width, height, 200)9697fig, axes = plt.subplots(1, 2, figsize=(14, 5))9899ax = axes[0]100im = ax.imshow(mset_full, extent=[-2.5, 1.0, -1.25, 1.25], cmap='hot', aspect='equal')101ax.set_xlabel('Re$(c)$')102ax.set_ylabel('Im$(c)$')103ax.set_title('Mandelbrot Set')104plt.colorbar(im, ax=ax, label='Iterations')105106ax = axes[1]107im = ax.imshow(mset_zoom, extent=[-0.75, -0.73, 0.1, 0.12], cmap='twilight_shifted', aspect='equal')108ax.set_xlabel('Re$(c)$')109ax.set_ylabel('Im$(c)$')110ax.set_title('Seahorse Valley (Zoom)')111plt.colorbar(im, ax=ax, label='Iterations')112113plt.tight_layout()114plt.savefig('mandelbrot_set.pdf', dpi=150, bbox_inches='tight')115plt.close()116\end{pycode}117118\begin{figure}[htbp]119\centering120\includegraphics[width=0.95\textwidth]{mandelbrot_set.pdf}121\caption{The Mandelbrot set: full view (left) and zoomed into the Seahorse Valley (right), demonstrating self-similarity.}122\end{figure}123124\section{Escape Time Algorithm}125Points outside $M$ are colored by escape time---the number of iterations before $|z_n| > 2$:126\begin{equation}127\text{escape time}(c) = \min\{n : |z_n| > 2\}128\end{equation}129130\chapter{Julia Sets}131132\section{Definition}133For a fixed $c$, the filled Julia set $K_c$ consists of initial points $z_0$ for which $z_{n+1} = z_n^2 + c$ remains bounded.134135\begin{pycode}136def julia_set(c, xmin, xmax, ymin, ymax, width, height, max_iter):137x = np.linspace(xmin, xmax, width)138y = np.linspace(ymin, ymax, height)139jset = np.zeros((height, width))140141for i in range(height):142for j in range(width):143z = complex(x[j], y[i])144for n in range(max_iter):145if abs(z) > 2:146jset[i, j] = n147break148z = z*z + c149else:150jset[i, j] = max_iter151152return jset153154# Different Julia sets155c_values = [156complex(-0.7, 0.27015),157complex(0.285, 0.01),158complex(-0.8, 0.156),159complex(-0.4, 0.6),160]161162fig, axes = plt.subplots(2, 2, figsize=(12, 12))163164for ax, c in zip(axes.flatten(), c_values):165jset = julia_set(c, -1.5, 1.5, -1.5, 1.5, 600, 600, 100)166ax.imshow(jset, extent=[-1.5, 1.5, -1.5, 1.5], cmap='magma', aspect='equal')167ax.set_xlabel('Re$(z)$')168ax.set_ylabel('Im$(z)$')169ax.set_title(f'Julia Set: $c = {c.real:.3f} + {c.imag:.3f}i$')170171plt.tight_layout()172plt.savefig('julia_sets.pdf', dpi=150, bbox_inches='tight')173plt.close()174\end{pycode}175176\begin{figure}[htbp]177\centering178\includegraphics[width=0.95\textwidth]{julia_sets.pdf}179\caption{Julia sets for different values of $c$, showing connected ($c \in M$) and disconnected ($c \notin M$) structures.}180\end{figure}181182\section{Connection to Mandelbrot Set}183The Julia set $K_c$ is connected if and only if $c \in M$. Otherwise, $K_c$ is a Cantor set (totally disconnected).184185\chapter{Sierpinski Triangle}186187\section{Iterated Function System}188The Sierpinski triangle can be generated using three affine transformations:189\begin{align}190T_1(\mathbf{x}) &= \frac{1}{2}\mathbf{x} \\191T_2(\mathbf{x}) &= \frac{1}{2}\mathbf{x} + \begin{pmatrix} 1/2 \\ 0 \end{pmatrix} \\192T_3(\mathbf{x}) &= \frac{1}{2}\mathbf{x} + \begin{pmatrix} 1/4 \\ \sqrt{3}/4 \end{pmatrix}193\end{align}194195\begin{pycode}196def sierpinski_chaos_game(n_points):197# Vertices of equilateral triangle198vertices = np.array([[0, 0], [1, 0], [0.5, np.sqrt(3)/2]])199200# Start from random point201point = np.random.rand(2)202points = [point.copy()]203204for _ in range(n_points):205vertex = vertices[np.random.randint(3)]206point = (point + vertex) / 2207points.append(point.copy())208209return np.array(points)210211def sierpinski_recursive(vertices, depth):212if depth == 0:213return [vertices]214215v0, v1, v2 = vertices216m01 = (v0 + v1) / 2217m12 = (v1 + v2) / 2218m02 = (v0 + v2) / 2219220triangles = []221triangles.extend(sierpinski_recursive([v0, m01, m02], depth - 1))222triangles.extend(sierpinski_recursive([m01, v1, m12], depth - 1))223triangles.extend(sierpinski_recursive([m02, m12, v2], depth - 1))224225return triangles226227fig, axes = plt.subplots(1, 3, figsize=(14, 4))228229# Chaos game230ax = axes[0]231points = sierpinski_chaos_game(50000)232ax.scatter(points[:, 0], points[:, 1], s=0.1, c='darkgreen', alpha=0.5)233ax.set_xlabel('$x$')234ax.set_ylabel('$y$')235ax.set_title('Chaos Game (50,000 points)')236ax.set_aspect('equal')237238# Recursive construction239ax = axes[1]240vertices = np.array([[0, 0], [1, 0], [0.5, np.sqrt(3)/2]])241triangles = sierpinski_recursive(vertices, 6)242for tri in triangles:243triangle = plt.Polygon(tri, fill=True, facecolor='darkgreen', edgecolor='none')244ax.add_patch(triangle)245ax.set_xlim(-0.1, 1.1)246ax.set_ylim(-0.1, 1.0)247ax.set_xlabel('$x$')248ax.set_ylabel('$y$')249ax.set_title('Recursive Construction (Depth 6)')250ax.set_aspect('equal')251252# Different depths253ax = axes[2]254colors = plt.cm.Greens(np.linspace(0.3, 0.9, 5))255for i, depth in enumerate([1, 2, 3, 4, 5]):256n_triangles = 3**depth257ax.scatter(depth, n_triangles, c=[colors[i]], s=100)258ax.set_xlabel('Recursion Depth')259ax.set_ylabel('Number of Triangles')260ax.set_title('Scaling: $N = 3^d$')261ax.set_yscale('log')262ax.grid(True, alpha=0.3)263264plt.tight_layout()265plt.savefig('sierpinski.pdf', dpi=150, bbox_inches='tight')266plt.close()267268# Fractal dimension269sierpinski_dim = np.log(3) / np.log(2)270\end{pycode}271272\begin{figure}[htbp]273\centering274\includegraphics[width=0.95\textwidth]{sierpinski.pdf}275\caption{Sierpinski triangle: chaos game method (left), recursive IFS (center), scaling behavior (right).}276\end{figure}277278The fractal dimension is $D = \frac{\log 3}{\log 2} \approx \py{f'{sierpinski_dim:.4f}'}$.279280\chapter{Koch Snowflake}281282\section{Construction}283Each line segment is replaced by four segments of length $1/3$:284285\begin{pycode}286def koch_curve(p1, p2, depth):287if depth == 0:288return [p1, p2]289290# Divide segment into thirds291d = (p2 - p1) / 3292a = p1293b = p1 + d294c = p1 + 2*d295e = p2296297# Compute apex of equilateral triangle298angle = np.pi / 3299rotation = np.array([[np.cos(angle), -np.sin(angle)],300[np.sin(angle), np.cos(angle)]])301apex = b + rotation @ d302303# Recurse304points = []305for seg in [(a, b), (b, apex), (apex, c), (c, e)]:306pts = koch_curve(seg[0], seg[1], depth - 1)307points.extend(pts[:-1])308points.append(e)309310return points311312def koch_snowflake(depth):313# Start with equilateral triangle314p1 = np.array([0, 0])315p2 = np.array([1, 0])316p3 = np.array([0.5, np.sqrt(3)/2])317318# Generate each side319side1 = koch_curve(p1, p2, depth)320side2 = koch_curve(p2, p3, depth)321side3 = koch_curve(p3, p1, depth)322323return side1[:-1] + side2[:-1] + side3324325fig, axes = plt.subplots(2, 3, figsize=(14, 8))326327# Different iteration depths328for i, depth in enumerate([0, 1, 2, 3, 4, 5]):329ax = axes[i // 3, i % 3]330points = koch_snowflake(depth)331points = np.array(points)332ax.plot(points[:, 0], points[:, 1], 'b-', linewidth=0.5)333ax.fill(points[:, 0], points[:, 1], alpha=0.3, color='blue')334ax.set_xlabel('$x$')335ax.set_ylabel('$y$')336ax.set_title(f'Iteration {depth}')337ax.set_aspect('equal')338ax.set_xlim(-0.1, 1.1)339ax.set_ylim(-0.2, 1.0)340341plt.tight_layout()342plt.savefig('koch_snowflake.pdf', dpi=150, bbox_inches='tight')343plt.close()344345# Compute perimeter and area346koch_dim = np.log(4) / np.log(3)347\end{pycode}348349\begin{figure}[htbp]350\centering351\includegraphics[width=0.95\textwidth]{koch_snowflake.pdf}352\caption{Koch snowflake construction: iterations 0-5. The perimeter grows infinitely while the area converges.}353\end{figure}354355\section{Properties}356\begin{itemize}357\item Perimeter: $L_n = L_0 \cdot (4/3)^n \to \infty$358\item Area: $A_n \to \frac{2\sqrt{3}}{5} s^2$ (finite)359\item Fractal dimension: $D = \frac{\log 4}{\log 3} \approx \py{f'{koch_dim:.4f}'}$360\end{itemize}361362\chapter{Box-Counting Dimension}363364\begin{pycode}365def box_counting(points, box_sizes):366counts = []367for size in box_sizes:368# Count occupied boxes369boxes = set()370for p in points:371box = (int(p[0] / size), int(p[1] / size))372boxes.add(box)373counts.append(len(boxes))374return np.array(counts)375376# Generate Sierpinski points377sierp_points = sierpinski_chaos_game(100000)378379# Box sizes380box_sizes = np.logspace(-3, 0, 20)381counts = box_counting(sierp_points, box_sizes)382383# Fit for dimension384log_sizes = np.log(1 / box_sizes)385log_counts = np.log(counts)386coeffs = np.polyfit(log_sizes, log_counts, 1)387computed_dim = coeffs[0]388389fig, axes = plt.subplots(1, 2, figsize=(12, 4))390391ax = axes[0]392ax.loglog(1/box_sizes, counts, 'bo-')393ax.set_xlabel('$1/\\epsilon$')394ax.set_ylabel('$N(\\epsilon)$')395ax.set_title('Box-Counting for Sierpinski Triangle')396ax.grid(True, alpha=0.3)397398ax = axes[1]399ax.plot(log_sizes, log_counts, 'bo', label='Data')400ax.plot(log_sizes, np.polyval(coeffs, log_sizes), 'r-', label=f'Fit: $D = {computed_dim:.3f}$')401ax.set_xlabel('$\\log(1/\\epsilon)$')402ax.set_ylabel('$\\log N(\\epsilon)$')403ax.set_title('Linear Fit for Fractal Dimension')404ax.legend()405ax.grid(True, alpha=0.3)406407plt.tight_layout()408plt.savefig('box_counting.pdf', dpi=150, bbox_inches='tight')409plt.close()410\end{pycode}411412\begin{figure}[htbp]413\centering414\includegraphics[width=0.95\textwidth]{box_counting.pdf}415\caption{Box-counting dimension estimation: log-log plot (left) and linear fit (right).}416\end{figure}417418Computed dimension: $D = \py{f'{computed_dim:.4f}'}$ (theoretical: $\py{f'{sierpinski_dim:.4f}'}$)419420\chapter{Summary of Results}421422\begin{pycode}423fractal_data = [424('Mandelbrot boundary', 2.0, 'Coastline'),425('Julia set ($c \\in M$)', '$\\approx 2$', 'Connected'),426('Sierpinski triangle', f'{sierpinski_dim:.4f}', 'Self-similar'),427('Koch snowflake', f'{koch_dim:.4f}', 'Infinite perimeter'),428('Cantor set', f'{np.log(2)/np.log(3):.4f}', 'Disconnected'),429]430\end{pycode}431432\begin{table}[htbp]433\centering434\caption{Fractal dimensions of common fractals}435\begin{tabular}{@{}lcc@{}}436\toprule437Fractal & Dimension & Property \\438\midrule439\py{fractal_data[0][0]} & \py{fractal_data[0][1]} & \py{fractal_data[0][2]} \\440\py{fractal_data[1][0]} & \py{fractal_data[1][1]} & \py{fractal_data[1][2]} \\441\py{fractal_data[2][0]} & \py{fractal_data[2][1]} & \py{fractal_data[2][2]} \\442\py{fractal_data[3][0]} & \py{fractal_data[3][1]} & \py{fractal_data[3][2]} \\443\py{fractal_data[4][0]} & \py{fractal_data[4][1]} & \py{fractal_data[4][2]} \\444\bottomrule445\end{tabular}446\end{table}447448\chapter{Conclusions}449450\begin{enumerate}451\item The Mandelbrot set contains all $c$ for which Julia sets are connected452\item Julia sets exhibit diverse topologies depending on parameter $c$453\item IFS methods generate exact self-similar fractals454\item Box-counting provides numerical estimates of fractal dimension455\item Fractals demonstrate that infinite complexity can arise from simple rules456\end{enumerate}457458\end{document}459460461