Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
73 lines
2.8 KiB
Python
73 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Konvexitaet_Demo.py
|
|
"""
|
|
Kapitel Fundament: Konvexität praktisch erfahrbar machen.
|
|
Teil 1: Die Sehnen-Bedingung numerisch nachprüfen.
|
|
Teil 2: Zeigen, warum lokale Suche bei nicht-konvexen Funktionen scheitert.
|
|
"""
|
|
|
|
import numpy as np
|
|
from scipy.optimize import minimize
|
|
|
|
# --- Teil 1: Sehnen-Test ---------------------------------------------------
|
|
def ist_konvex_numerisch(f, unten, oben, proben=2000, seed=0):
|
|
"""
|
|
Prüft die Konvexitätsdefinition an zufälligen Punktepaaren:
|
|
f(theta*x + (1-theta)*y) <= theta*f(x) + (1-theta)*f(y) ?
|
|
Findet Gegenbeispiele - beweist aber KEINE Konvexität.
|
|
"""
|
|
rng = np.random.default_rng(seed)
|
|
schlimmste_verletzung = 0.0
|
|
for _ in range(proben):
|
|
x, y = rng.uniform(unten, oben, size=2)
|
|
theta = rng.uniform(0.0, 1.0)
|
|
links = f(theta * x + (1 - theta) * y)
|
|
rechts = theta * f(x) + (1 - theta) * f(y)
|
|
schlimmste_verletzung = max(schlimmste_verletzung, links - rechts)
|
|
return schlimmste_verletzung
|
|
|
|
|
|
funktionen = {
|
|
"f(x) = x^2 (konvex)": lambda x: x ** 2,
|
|
"f(x) = |x| (konvex)": lambda x: abs(x),
|
|
"f(x) = e^x (konvex)": lambda x: np.exp(x),
|
|
"f(x) = x^3 (NICHT konvex)": lambda x: x ** 3,
|
|
"f(x) = x^2+3sin(3x) (NICHT konvex)": lambda x: x ** 2 + 3 * np.sin(3 * x),
|
|
}
|
|
|
|
print("=" * 68)
|
|
print(" TEIL 1: SEHNEN-TEST (positive Zahl = Konvexität verletzt)")
|
|
print("=" * 68)
|
|
for name, f in funktionen.items():
|
|
verletzung = ist_konvex_numerisch(f, -3.0, 3.0)
|
|
urteil = "konvex (keine Verletzung gefunden)" if verletzung < 1e-9 \
|
|
else f"NICHT konvex (Verletzung bis {verletzung:.3f})"
|
|
print(f"{name:<34} -> {urteil}")
|
|
|
|
# --- Teil 2: Lokale Suche von verschiedenen Startpunkten -------------------
|
|
def wellige_funktion(x):
|
|
"""Nicht-konvex: eine Parabel mit aufmodulierter Welle -> viele lokale Minima."""
|
|
return x[0] ** 2 + 3.0 * np.sin(3.0 * x[0])
|
|
|
|
print("\n" + "=" * 68)
|
|
print(" TEIL 2: LOKALE SUCHE BEI NICHT-KONVEXER FUNKTION")
|
|
print("=" * 68)
|
|
print(f"{'Startpunkt':>12} | {'gefundenes Minimum':>20} | {'Funktionswert':>14}")
|
|
print("-" * 68)
|
|
|
|
ergebnisse = []
|
|
for start in [-3.0, -1.5, 0.0, 1.5, 3.0]:
|
|
res = minimize(wellige_funktion, x0=[start], method="BFGS")
|
|
ergebnisse.append((start, res.x[0], res.fun))
|
|
print(f"{start:>12.1f} | {res.x[0]:>20.4f} | {res.fun:>14.4f}")
|
|
|
|
bester = min(ergebnisse, key=lambda t: t[2])
|
|
print("-" * 68)
|
|
print(f"Je nach Startpunkt landet derselbe Algorithmus in "
|
|
f"{len({round(e[1], 3) for e in ergebnisse})} verschiedenen Minima.")
|
|
print(f"Das beste gefundene: x = {bester[1]:.4f} mit f = {bester[2]:.4f} "
|
|
f"(Start bei {bester[0]:.1f})")
|
|
print("Bei einer KONVEXEN Funktion waeren alle Zeilen identisch --")
|
|
print("der Startpunkt waere voellig gleichgueltig.")
|
|
print("=" * 68)
|