It works!!!!!!

This commit is contained in:
2026-01-30 17:59:16 +01:00
parent d262924e20
commit 6fbb53ccd1
7 changed files with 830 additions and 0 deletions

684
md/erklärung.md Normal file
View File

@@ -0,0 +1,684 @@
# Die Mandelbrot-Menge: Von Null auf Visualisierung
## 1. Was sind komplexe Zahlen?
### 1.1 Die imaginäre Einheit
Normale Zahlen (reelle Zahlen) kennen wir alle: 1, 2, 3.5, -7, π usw. Diese liegen alle auf einem eindimensionalen Zahlenstrahl.
Komplexe Zahlen erweitern dieses Konzept in die zweite Dimension. Der Trick: Wir erfinden eine neue Zahl namens **i** (die imaginäre Einheit) mit der Eigenschaft:
```
i² = -1
```
Das ist zunächst verwirrend, weil keine normale Zahl diese Eigenschaft hat (sowohl 2² = 4 als auch (-2)² = 4). Aber mathematisch können wir mit **i** genauso rechnen wie mit normalen Zahlen.
### 1.2 Aufbau komplexer Zahlen
Eine komplexe Zahl besteht aus zwei Teilen:
```
z = a + bi
```
- **a** = Realteil (eine normale Zahl)
- **b** = Imaginärteil (auch eine normale Zahl)
- **i** = die imaginäre Einheit
**Beispiele:**
- `3 + 4i` → Realteil: 3, Imaginärteil: 4
- `2 - 5i` → Realteil: 2, Imaginärteil: -5
- `7 + 0i = 7` → eine rein reelle Zahl
- `0 + 3i = 3i` → eine rein imaginäre Zahl
### 1.3 Visualisierung: Die komplexe Ebene
Statt einem Zahlenstrahl verwenden wir eine Ebene:
- **x-Achse (horizontal)**: Realteil
- **y-Achse (vertikal)**: Imaginärteil
Die Zahl `3 + 4i` liegt also am Punkt (3, 4) in dieser Ebene.
```
Imaginärteil (y)
5 |
4 | • (3+4i)
3 |
2 |
1 |
0 |-------|-------|→ Realteil (x)
-1 | 2 4
-2 |
```
### 1.4 Rechnen mit komplexen Zahlen
#### Addition
Einfach: Addiere Real- und Imaginärteile getrennt.
```
(a + bi) + (c + di) = (a + c) + (b + d)i
```
**Beispiel:**
```
(3 + 4i) + (1 + 2i) = (3+1) + (4+2)i = 4 + 6i
```
#### Multiplikation
Hier wird's interessanter. Wir multiplizieren wie bei Klammern, aber beachten dass **i² = -1**:
```
(a + bi) × (c + di) = ac + adi + bci + bdi²
= ac + adi + bci + bd(-1)
= (ac - bd) + (ad + bc)i
```
**Beispiel:**
```
(3 + 4i) × (1 + 2i) = 3×1 + 3×2i + 4i×1 + 4i×2i
= 3 + 6i + 4i + 8i²
= 3 + 10i + 8×(-1)
= 3 + 10i - 8
= -5 + 10i
```
#### Spezialfall: Quadrieren
Besonders wichtig für die Mandelbrot-Menge ist das Quadrieren:
```
(a + bi)² = (a + bi) × (a + bi)
= a² + abi + abi + (bi)²
= a² + 2abi + b²i²
= a² + 2abi - b²
= (a² - b²) + 2abi
```
**Merke dir diese Formel:**
```
(a + bi)² = (a² - b²) + (2ab)i
```
**Beispiel:**
```
(3 + 4i)² = (3² - 4²) + (2×3×4)i
= (9 - 16) + 24i
= -7 + 24i
```
### 1.5 Betrag (Abstand vom Ursprung)
Der Betrag einer komplexen Zahl ist ihr Abstand vom Ursprung (0, 0). Mit dem Satz des Pythagoras:
```
|a + bi| = √(a² + b²)
```
**Beispiel:**
```
|3 + 4i| = √(3² + 4²) = √(9 + 16) = √25 = 5
```
In der Ebene visualisiert ist das die Länge des Pfeils vom Ursprung zum Punkt (3, 4).
---
## 2. Die Mandelbrot-Iteration
### 2.1 Die Grundidee
Die Mandelbrot-Menge ist eine Menge von komplexen Zahlen **c**, die eine bestimmte Eigenschaft haben.
Für jede komplexe Zahl **c** führen wir folgende Iteration durch:
```
z₀ = 0
z₁ = z₀² + c = 0² + c = c
z₂ = z₁² + c
z₃ = z₂² + c
z₄ = z₃² + c
...
```
**Die Frage:** Bleibt die Folge z₀, z₁, z₂, z₃, ... beschränkt (in der Nähe des Ursprungs), oder explodiert sie gegen unendlich?
**Definition:** Die Zahl **c** gehört zur Mandelbrot-Menge, wenn die Folge beschränkt bleibt (nicht gegen unendlich geht).
### 2.2 Beispiel 1: c = 0
```
z₀ = 0
z₁ = 0² + 0 = 0
z₂ = 0² + 0 = 0
z₃ = 0² + 0 = 0
...
```
Die Folge bleibt bei 0. **c = 0 gehört zur Mandelbrot-Menge.**
### 2.3 Beispiel 2: c = 1
```
z₀ = 0
z₁ = 0² + 1 = 1
z₂ = 1² + 1 = 2
z₃ = 2² + 1 = 5
z₄ = 5² + 1 = 26
z₅ = 26² + 1 = 677
...
```
Die Folge explodiert! **c = 1 gehört NICHT zur Mandelbrot-Menge.**
### 2.4 Beispiel 3: c = -1
```
z₀ = 0
z₁ = 0² + (-1) = -1
z₂ = (-1)² + (-1) = 1 - 1 = 0
z₃ = 0² + (-1) = -1
z₄ = (-1)² + (-1) = 0
...
```
Die Folge springt zwischen -1 und 0 hin und her. Sie bleibt beschränkt. **c = -1 gehört zur Mandelbrot-Menge.**
### 2.5 Beispiel 4: c = i (eine komplexe Zahl!)
```
z₀ = 0
z₁ = 0² + i = i
z₂ = i² + i = -1 + i
z₃ = (-1 + i)² + i
```
Berechnen wir z₃ mit unserer Quadrierformel (a=-1, b=1):
```
(-1 + i)² = ((-1)² - 1²) + (2×(-1)×1)i
= (1 - 1) + (-2)i
= -2i
z₃ = -2i + i = -i
```
Weiter:
```
z₄ = (-i)² + i = (0 + (-1)i)² + i
= (0² - (-1)²) + (2×0×(-1))i + i
= -1 + 0i + i
= -1 + i
```
Moment! z₄ = z₂. Die Folge wiederholt sich jetzt:
```
z₂ = -1 + i
z₃ = -i
z₄ = -1 + i
z₅ = -i
...
```
Die Folge bleibt beschränkt (pendelt zwischen zwei Werten). **c = i gehört zur Mandelbrot-Menge.**
---
## 3. Der Algorithmus zur Visualisierung
### 3.1 Warum nicht unendlich lange testen?
Wir können nicht unendlich viele Iterationen durchführen. Daher verwenden wir zwei Abbruchkriterien:
**Kriterium 1: Divergenz erkannt**
Mathematisch bewiesen: Wenn der Betrag von z jemals größer als 2 wird (|z| > 2), dann wird die Folge definitiv gegen unendlich gehen.
Da |z| = √(real² + imag²) ist, können wir stattdessen prüfen:
```
real² + imag² > 4
```
(Das spart die Wurzelberechnung, weil 2² = 4)
**Kriterium 2: Maximale Iterationen**
Wir setzen eine Obergrenze, z.B. 100 oder 1000 Iterationen. Wenn wir diese erreichen ohne Divergenz, nehmen wir an, dass c zur Menge gehört.
### 3.2 Der Pseudocode
Für jede komplexe Zahl c = (c_real, c_imag):
```
z_real = 0
z_imag = 0
iteration = 0
max_iterations = 1000
solange (iteration < max_iterations):
// Berechne z² + c
z_real_neu = z_real² - z_imag² + c_real
z_imag_neu = 2 × z_real × z_imag + c_imag
z_real = z_real_neu
z_imag = z_imag_neu
// Divergenz-Check
wenn (z_real² + z_imag² > 4):
break // Divergenz erkannt!
iteration = iteration + 1
// Ergebnis: iteration
// - Wenn iteration = max_iterations: c ist in der Menge (schwarz färben)
// - Sonst: c ist außerhalb (Farbe basierend auf iteration)
```
### 3.3 Wichtige Details zur Implementierung
**1. Reihenfolge der Berechnung**
```cpp
// FALSCH:
z_real = z_real * z_real - z_imag * z_imag + c_real;
z_imag = 2 * z_real * z_imag + c_imag; // Nutzt bereits das neue z_real!
```
```cpp
// RICHTIG:
double z_real_neu = z_real * z_real - z_imag * z_imag + c_real;
double z_imag_neu = 2 * z_real * z_imag + c_imag;
z_real = z_real_neu;
z_imag = z_imag_neu;
```
**2. Optimierung der Divergenz-Prüfung**
```cpp
// Berechne die Quadrate nur einmal:
double z_real_squared = z_real * z_real;
double z_imag_squared = z_imag * z_imag;
double z_real_neu = z_real_squared - z_imag_squared + c_real;
double z_imag_neu = 2 * z_real * z_imag + c_imag;
// Divergenz-Check
if (z_real_squared + z_imag_squared > 4.0) {
break;
}
```
---
## 4. Von Pixeln zur komplexen Ebene
### 4.1 Das Problem
Dein Bildschirm hat Pixel mit Koordinaten:
- x: 0 bis fenster_breite (z.B. 0 bis 800)
- y: 0 bis fenster_hoehe (z.B. 0 bis 600)
Die interessante Region der Mandelbrot-Menge liegt bei:
- Real-Achse: ungefähr -2.5 bis +1.0
- Imaginär-Achse: ungefähr -1.0 bis +1.0
Wir müssen jeden Pixel auf einen Punkt in der komplexen Ebene abbilden.
### 4.2 Die Mapping-Formel
#### Schritt 1: Normalisiere Pixelkoordinaten auf [0, 1]
```
x_normiert = x / fenster_breite // Wert zwischen 0 und 1
y_normiert = y / fenster_hoehe // Wert zwischen 0 und 1
```
#### Schritt 2: Skaliere auf den gewünschten Bereich
```
c_real = min_real + x_normiert × (max_real - min_real)
c_imag = min_imag + y_normiert × (max_imag - min_imag)
```
#### Beispiel mit konkreten Zahlen
Angenommen:
- Fenster: 800×600 Pixel
- Bereich: real [-2.5, 1.0], imaginär [-1.0, 1.0]
Für Pixel (400, 300) (die Fenstermitte):
```
x_normiert = 400 / 800 = 0.5
y_normiert = 300 / 600 = 0.5
c_real = -2.5 + 0.5 × (1.0 - (-2.5))
= -2.5 + 0.5 × 3.5
= -2.5 + 1.75
= -0.75
c_imag = -1.0 + 0.5 × (1.0 - (-1.0))
= -1.0 + 0.5 × 2.0
= -1.0 + 1.0
= 0.0
```
Die Fenstermitte entspricht also der komplexen Zahl c = -0.75 + 0i.
### 4.3 Koordinatensysteme beachten
**Problem:** In den meisten Grafiksystemen ist y=0 oben und y wächst nach unten. In der Mathematik wächst die imaginäre Achse nach oben.
**Lösung 1:** Y-Koordinate umdrehen
```
y_normiert = 1.0 - (y / fenster_hoehe)
```
**Lösung 2:** min_imag und max_imag vertauschen
```
c_imag = max_imag - y_normiert × (max_imag - min_imag)
```
Beide führen zum gleichen Ergebnis: Die Mandelbrot-Menge wird richtig herum angezeigt.
### 4.4 Seitenverhältnis (Aspect Ratio)
Wenn dein Fenster nicht quadratisch ist, musst du aufpassen, dass Kreise nicht zu Ellipsen werden.
**Option 1:** Passe den Bereich an das Fensterverhältnis an
```
aspect_ratio = fenster_breite / fenster_hoehe
real_span = max_real - min_real
imag_span = max_imag - min_imag
// Zentriere und passe an
center_real = (min_real + max_real) / 2
center_imag = (min_imag + max_imag) / 2
if (aspect_ratio > 1.0) {
// Fenster ist breiter als hoch → erweitere Real-Achse
half_span = imag_span * aspect_ratio / 2
min_real = center_real - half_span
max_real = center_real + half_span
} else {
// Fenster ist höher als breit → erweitere Imaginär-Achse
half_span = real_span / aspect_ratio / 2
min_imag = center_imag - half_span
max_imag = center_imag + half_span
}
```
**Option 2:** Rendere in ein quadratisches Bild und zeige es zentriert im Fenster
---
## 5. Färbung und Visualisierung
### 5.1 Basis-Färbung
Das Einfachste:
- **In der Menge** (max_iterations erreicht): Schwarz
- **Außerhalb**: Färbung basierend auf der Iterationszahl
```
if (iteration == max_iterations) {
farbe = SCHWARZ
} else {
// iteration geht von 0 bis max_iterations-1
helligkeitswert = iteration / max_iterations
farbe = helligkeitswert // z.B. 0 = schwarz, 1 = weiß
}
```
### 5.2 Farb-Paletten
Statt Graustufen kannst du Farbverläufe verwenden:
**Linear mapping:**
```
farbindex = (iteration × 256) / max_iterations
farbe = farbpalette[farbindex]
```
**Beliebte Paletten:**
- Regenbogen (HSV mit variierendem Hue)
- Blau-Violett-Verlauf
- Feuer (Schwarz → Rot → Orange → Gelb → Weiß)
### 5.3 Smooth Coloring (kontinuierliche Färbung)
Problem: Mit der einfachen Methode sieht man harte "Bänder" zwischen den Iterationsstufen.
Lösung: Nutze den finalen Betragswert für eine feinere Färbung:
```
wenn divergiert:
// Wie stark über der Grenze sind wir?
betrag_quadrat = z_real² + z_imag²
// Smooth iteration count
smooth_iteration = iteration + 1 - log(log(betrag_quadrat) / log(2)) / log(2)
// Nutze smooth_iteration für Färbung
```
Das ergibt sanfte Farbverläufe ohne Bänder. (Dies ist mathematisch etwas fortgeschritten, aber das Ergebnis sieht viel besser aus.)
---
## 6. Zoom und Navigation
### 6.1 Zoom implementieren
Beim Zoomen verkleinerst du den sichtbaren Bereich der komplexen Ebene.
**Zoom um einen Faktor:**
```
zoom_factor = 2.0 // 2x näher heran
center_real = (min_real + max_real) / 2
center_imag = (min_imag + max_imag) / 2
span_real = (max_real - min_real) / zoom_factor
span_imag = (max_imag - min_imag) / zoom_factor
min_real = center_real - span_real / 2
max_real = center_real + span_real / 2
min_imag = center_imag - span_imag / 2
max_imag = center_imag + span_imag / 2
```
### 6.2 Zoom auf Mausposition
Wenn der User auf Pixel (mouse_x, mouse_y) klickt:
```
// Berechne die komplexe Zahl an der Mausposition
mouse_real = min_real + (mouse_x / fenster_breite) × (max_real - min_real)
mouse_imag = min_imag + (mouse_y / fenster_hoehe) × (max_imag - min_imag)
// Zentriere auf diese Position und zoome
span_real = (max_real - min_real) / zoom_factor
span_imag = (max_imag - min_imag) / zoom_factor
min_real = mouse_real - span_real / 2
max_real = mouse_real + span_real / 2
min_imag = mouse_imag - span_imag / 2
max_imag = mouse_imag + span_imag / 2
```
### 6.3 Interaktive Navigation
**Pan (Verschieben):**
```
verschiebung_real = delta_x / fenster_breite × (max_real - min_real)
verschiebung_imag = delta_y / fenster_hoehe × (max_imag - min_imag)
min_real -= verschiebung_real
max_real -= verschiebung_real
min_imag -= verschiebung_imag
max_imag -= verschiebung_imag
```
---
## 7. Performance-Optimierungen
### 7.1 Frühe Abbrüche
**Periodizitätsprüfung:** Speichere z in regelmäßigen Abständen. Wenn z sich wiederholt, bist du in einem Zyklus → in der Menge.
**Hauptkardioiden-Test:** Der große "Körper" der Mandelbrot-Menge (eine Kardioidform) kann analytisch getestet werden:
```
// Teste ob c in der Hauptkardioide liegt
q = (c_real - 0.25)² + c_imag²
if (q × (q + (c_real - 0.25)) < 0.25 × c_imag²) {
// In der Hauptkardioide → direkt als "in der Menge" werten
return max_iterations
}
// Teste ob c im Periode-2-Bulb liegt
if ((c_real + 1)² + c_imag² < 0.0625) {
// Im Bulb → in der Menge
return max_iterations
}
```
### 7.2 Symmetrie nutzen
Die Mandelbrot-Menge ist symmetrisch zur reellen Achse:
- Wenn (a + bi) in der Menge ist, dann auch (a - bi)
Du kannst nur die obere Hälfte berechnen und spiegeln.
### 7.3 Multi-Threading
Jeder Pixel kann unabhängig berechnet werden → perfekt für Parallelisierung!
Teile das Bild in Streifen oder Kacheln auf und berechne diese in verschiedenen Threads.
---
## 8. Interessante Koordinaten zum Erkunden
### Klassische Ansicht (Gesamtüberblick)
```
Real: [-2.5, 1.0]
Imag: [-1.25, 1.25]
```
### Seepferdchen-Tal
```
Real: [-0.75, -0.735]
Imag: [0.1, 0.115]
max_iterations: 500+
```
### Spiralen
```
Real: [-0.7, -0.65]
Imag: [0.4, 0.45]
max_iterations: 1000+
```
### Mini-Mandelbrot
```
Real: [-0.16, -0.14]
Imag: [1.025, 1.045]
max_iterations: 2000+
```
**Tipp:** Je tiefer du zoomst, desto höher sollte max_iterations sein, um Details zu sehen!
---
## 9. Häufige Fehler und deren Lösung
### 9.1 "Ich sehe nur Schwarz oder nur Weiß"
**Ursachen:**
- max_iterations zu niedrig oder zu hoch
- Koordinatenbereich außerhalb der interessanten Region
- Mapping-Formel falsch
**Lösung:** Beginne mit der klassischen Ansicht und max_iterations = 100.
### 9.2 "Das Bild ist verzerrt"
**Ursache:** Seitenverhältnis nicht korrekt berücksichtigt.
**Lösung:** Siehe Abschnitt 4.4 zur Aspect Ratio.
### 9.3 "Die Iteration zählt falsch"
**Ursache:** Du verwendest das neue z_real bei der Berechnung von z_imag_neu.
**Lösung:** Speichere beide neue Werte in temporären Variablen.
### 9.4 "Beim Zoomen wird das Bild unscharf"
**Ursache:** Bei tiefen Zoom-Stufen reicht die Präzision von `double` nicht mehr aus.
**Lösung:**
- Nutze höhere Präzision (z.B. `long double`)
- Für extreme Zooms: arbitrary-precision Bibliotheken (GMP, MPFR)
### 9.5 "Die Berechnung ist sehr langsam"
**Lösungen:**
- Reduziere max_iterations für Vorschau-Rendering
- Implementiere Multi-Threading
- Nutze Periodizitätsprüfung und analytische Tests
- Rendere in niedrigerer Auflösung und skaliere hoch
---
## 10. Zusammenfassung: Die komplette Visualisierungspipeline
```
FÜR JEDEN PIXEL (x, y) IM FENSTER:
1. MAPPING: Konvertiere Pixel zu komplexer Zahl
c_real = min_real + (x / breite) × (max_real - min_real)
c_imag = min_imag + (y / höhe) × (max_imag - min_imag)
2. ITERATION: Teste ob c in der Menge liegt
z_real = 0
z_imag = 0
iteration = 0
SOLANGE iteration < max_iterations:
// Berechne z² + c
temp_real = z_real² - z_imag² + c_real
temp_imag = 2 × z_real × z_imag + c_imag
z_real = temp_real
z_imag = temp_imag
// Divergenz?
WENN z_real² + z_imag² > 4:
BREAK
iteration++
3. FÄRBUNG: Basierend auf Iterationszahl
WENN iteration == max_iterations:
farbe = SCHWARZ (in der Menge)
SONST:
farbe = berechne_farbe(iteration)
4. ZEICHNE: Setze Pixel (x, y) auf farbe
```
---
## Viel Erfolg!
Die Mandelbrot-Menge ist ein faszinierendes mathematisches Objekt. Beim Hineinzoomen entdeckst du immer neue Strukturen Spiralen, Seepferdchen-Täler, Mini-Kopien der gesamten Menge.
Ein paar abschließende Tipps:
- Beginne mit niedrigen max_iterations (50-100) für schnelle Tests
- Implementiere Zoom und Pan für interaktive Exploration
- Experimentiere mit verschiedenen Farbpaletten
- Speichere interessante Koordinaten, die du findest
Die Selbstähnlichkeit der Mandelbrot-Menge ist unendlich du kannst beliebig tief zoomen und findest immer neue Details (bis zur Grenze der Gleitkomma-Präzision).