import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
# Konfiguriere matplotlib um schönere Plots zu erhalten
plt.rcParams.update({
"interactive": True,
"figure.dpi": 200,
"font.family": "serif",
"mathtext.fontset": "cm",
"axes.grid": True,
"axes.labelsize": 14,
"grid.linewidth": .5,
"grid.alpha": .5,
"lines.linewidth": 1,
"lines.dashed_pattern": (6, 4),
"axes.prop_cycle": mpl.cycler(color=['k', 'r', 'b', 'g', 'y'])
})
def random_walk(N: int):
p = np.array([
0, 0.5, 1, 0.5j, 1 + 0.5j, 1j, 0.5 + 1j, 1 + 1j
])
# Erstelle Array aus N zufällig gewählten Punkten aus p
# Dies ist performanter, als in jeder Iteration einen zufälligen Punkt zu wählen.
random_p = np.random.choice(p, size=N)
q = np.empty(N, dtype=np.cdouble) # Erstelle leeren Array der Länge N
q[0] = p[0]
for n in range(N - 1):
q[n + 1] = (q[n] + random_p[n]) / 3
return q
q = random_walk(N=int(1e6))
def plot_points(points: np.ndarray):
plt.figure(figsize=(10, 10))
plt.grid(False)
plt.scatter(points.real, points.imag, c='black', s=.005)
plt.xlabel("$x$")
plt.ylabel("$y$")
plot_points(q)
def count_boxes_naive(points: np.ndarray, epsilon: float):
N = 0
# Bestimme x- und y-Koordinaten für alle Gitterzellen.
x_values = np.arange(min(points.real), max(points.real), epsilon)
y_values = np.arange(min(points.imag), max(points.imag), epsilon)
# Iteriere über alle Gitterzellen.
for x in x_values:
for y in y_values:
# Iteriere über alle Fraktalpunkte.
for point in points:
# Prüfe, ob aktueller Punkt innerhalb der Gitterzelle liegt.
if (
point.real > x and point.real < x + epsilon and
point.imag > y and point.imag < y + epsilon
):
N += 1
# Wenn ein Punkt gefunden wurde, breche Schleife über Fraktalpunkte ab.
# (uns interessiert nur, ob *mindestens* ein Punkt im Kästchen liegt)
break
return N
# Teste naiven Algorithmus
%time count_boxes_naive(q, epsilon=1/32)
def count_boxes_faster(points: np.ndarray, epsilon: float):
N = 0
# Verteile x- und y-Werte für alle Gitterpunkte
x_values = np.arange(min(points.real), max(points.real), epsilon)
y_values = np.arange(min(points.imag), max(points.imag), epsilon)
for x in x_values:
# Erstelle Array mit den Punkten, die sich im Intervall [x, x + epsilon] befinden.
interval_points = points[(points.real > x) & (points.real < x + epsilon)]
for y in y_values:
# Prüfe, ob sich irgendwelche Punkte aus dem x-Intervall auch im Intervall [y, y + epsilon] befinden.
if np.any((interval_points.imag > y) & (interval_points.imag < y + epsilon)):
N += 1
return N
# Teste schnelleren Algorithmus
%time count_boxes_faster(q, epsilon=1/32)
# Teste schnelleren Algorithmus für kleines ε
%time count_boxes_faster(q, epsilon=1/256)
def count_boxes_hist(points: np.ndarray, epsilon: float):
# Das Fraktal hat eine Ausdehnung von 0.5.
# Somit ist die Anzahl der Kästchen pro Seitenlänge gegeben durch:
number_of_boxes = int(.5 / epsilon)
# Erstelle Histogramm der oben berechneten Größe
hist = np.zeros((number_of_boxes, number_of_boxes), dtype=int)
for point in points:
# Ermittle Position des Punktes im Histogramm
i = int(point.real / .5 * number_of_boxes)
j = int(point.imag / .5 * number_of_boxes)
# Erhöhe den entsprechenden Histogramm-Wert
hist[i, j] += 1
# Zähle, wie viele Histogramm-Werte größer als 0 sind,
# also mindestens einen Punkt enthalten
return np.sum(hist > 0)
# Teste Algorithmus
print("Test ε = 1/32:")
%time count_boxes_hist(q, 1/32)
print()
print("Test ε = 1/256:")
%time count_boxes_hist(q, 1/256)
print("Voriger Algorithmus:")
%time count_boxes_faster(q, 1/512)
print()
print("Histogramm-Algorithmus:")
%time count_boxes_hist(q, 1/512)
def calculate_fractal_dimensions(points: np.ndarray, epsilons: list):
# Zähle die Anzahl der ausgefüllten Gitterzellen für alle ε-Werte
N = [count_boxes_faster(points, epsilon) for epsilon in epsilons]
# Berechne für jedes ε-Paar die zugehörige Dimension
return [
np.log(N[i] / N[i + 1]) / np.log(epsilons[i + 1] / epsilons[i])
for i in range(len(epsilons) - 1)
]
# Berechne die Dimension für einige ε-Werte
epsilons = [1/4, 1/8, 1/16, 1/32, 1/64, 1/128, 1/256, 1/512]
dimensions = calculate_fractal_dimensions(q, epsilons)
# Zeige berechnete Dimensionen in Tabelle an
pd.DataFrame({'ε': epsilons[:-1], 'D': dimensions})
# Trage berechnete Dimensionen gegen ε-Werte auf
plt.figure(figsize=(10, 5))
plt.scatter(epsilons[1:], dimensions, c="k")
plt.axhline(np.log(8) / np.log(3), c='r', ls='dashed', label="$D_\\mathrm{analytisch} = 1{,}89278...$")
plt.xscale('log')
plt.xlabel('$\\varepsilon$')
plt.xticks(epsilons, [f"1/{int(1/epsilon)}" for epsilon in epsilons]);
plt.ylabel('$D$')
plt.ylim((1.6, 2.2))
plt.legend()